simplified LMS API
This commit is contained in:
parent
0d8fb4b880
commit
0b00995aa7
13 changed files with 117 additions and 230 deletions
|
@ -37,8 +37,6 @@ public interface ExamDAO extends ActivatableEntityDAO<Exam, Exam>, BulkActionSup
|
||||||
* happened */
|
* happened */
|
||||||
Result<GrantEntity> examGrantEntityByClientConnection(Long connectionId);
|
Result<GrantEntity> examGrantEntityByClientConnection(Long connectionId);
|
||||||
|
|
||||||
Result<Exam> getWithQuizDataFromCache(Long id);
|
|
||||||
|
|
||||||
/** Get all active Exams for a given institution.
|
/** Get all active Exams for a given institution.
|
||||||
*
|
*
|
||||||
* @param institutionId the identifier of the institution
|
* @param institutionId the identifier of the institution
|
||||||
|
|
|
@ -392,7 +392,7 @@ public class ExamConfigurationMapDAOImpl implements ExamConfigurationMapDAO {
|
||||||
final String status = config.getStatus();
|
final String status = config.getStatus();
|
||||||
|
|
||||||
final Exam exam = this.examDAO
|
final Exam exam = this.examDAO
|
||||||
.getWithQuizDataFromCache(record.getExamId())
|
.byPK(record.getExamId())
|
||||||
.getOr(null);
|
.getOr(null);
|
||||||
|
|
||||||
return new ExamConfigurationMap(
|
return new ExamConfigurationMap(
|
||||||
|
|
|
@ -119,13 +119,6 @@ public class ExamDAOImpl implements ExamDAO {
|
||||||
.map(record -> toDomainModel(record, null, null).getOrThrow());
|
.map(record -> toDomainModel(record, null, null).getOrThrow());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
@Transactional(readOnly = true)
|
|
||||||
public Result<Exam> getWithQuizDataFromCache(final Long id) {
|
|
||||||
return recordById(id)
|
|
||||||
.flatMap(this::toDomainModelFromCache);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public Result<Collection<Exam>> all(final Long institutionId, final Boolean active) {
|
public Result<Collection<Exam>> all(final Long institutionId, final Boolean active) {
|
||||||
|
@ -169,7 +162,6 @@ public class ExamDAOImpl implements ExamDAO {
|
||||||
|
|
||||||
return Result.tryCatch(() -> {
|
return Result.tryCatch(() -> {
|
||||||
|
|
||||||
final boolean cached = filterMap.getBoolean(Exam.FILTER_CACHED_QUIZZES);
|
|
||||||
final String name = filterMap.getQuizName();
|
final String name = filterMap.getQuizName();
|
||||||
final DateTime from = filterMap.getExamFromTime();
|
final DateTime from = filterMap.getExamFromTime();
|
||||||
final Predicate<Exam> quizDataFilter = exam -> {
|
final Predicate<Exam> quizDataFilter = exam -> {
|
||||||
|
@ -236,7 +228,7 @@ public class ExamDAOImpl implements ExamDAO {
|
||||||
.build()
|
.build()
|
||||||
.execute();
|
.execute();
|
||||||
|
|
||||||
return this.toDomainModel(records, cached)
|
return this.toDomainModel(records)
|
||||||
.getOrThrow()
|
.getOrThrow()
|
||||||
.stream()
|
.stream()
|
||||||
.filter(quizDataFilter.and(predicate))
|
.filter(quizDataFilter.and(predicate))
|
||||||
|
@ -768,17 +760,6 @@ public class ExamDAOImpl implements ExamDAO {
|
||||||
exam.getDescription());
|
exam.getDescription());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Result<Exam> toDomainModelFromCache(final ExamRecord record) {
|
|
||||||
|
|
||||||
return this.lmsAPIService
|
|
||||||
.getLmsAPITemplate(record.getLmsSetupId())
|
|
||||||
.flatMap(template -> this.toDomainModel(
|
|
||||||
record,
|
|
||||||
template.getQuizFromCache(record.getExternalId())
|
|
||||||
.getOrThrow(),
|
|
||||||
null));
|
|
||||||
}
|
|
||||||
|
|
||||||
private Result<Exam> toDomainModel(final ExamRecord record) {
|
private Result<Exam> toDomainModel(final ExamRecord record) {
|
||||||
return toDomainModel(
|
return toDomainModel(
|
||||||
record.getLmsSetupId(),
|
record.getLmsSetupId(),
|
||||||
|
@ -787,12 +768,6 @@ public class ExamDAOImpl implements ExamDAO {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Result<Collection<Exam>> toDomainModel(final Collection<ExamRecord> records) {
|
private Result<Collection<Exam>> toDomainModel(final Collection<ExamRecord> records) {
|
||||||
return toDomainModel(records, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Result<Collection<Exam>> toDomainModel(
|
|
||||||
final Collection<ExamRecord> records,
|
|
||||||
final boolean cached) {
|
|
||||||
|
|
||||||
return Result.tryCatch(() -> {
|
return Result.tryCatch(() -> {
|
||||||
|
|
||||||
|
@ -807,8 +782,7 @@ public class ExamDAOImpl implements ExamDAO {
|
||||||
.stream()
|
.stream()
|
||||||
.flatMap(entry -> toDomainModel(
|
.flatMap(entry -> toDomainModel(
|
||||||
entry.getKey(),
|
entry.getKey(),
|
||||||
entry.getValue(),
|
entry.getValue())
|
||||||
cached)
|
|
||||||
.onError(error -> log.error(
|
.onError(error -> log.error(
|
||||||
"Failed to get quizzes from LMS Setup: {}",
|
"Failed to get quizzes from LMS Setup: {}",
|
||||||
entry.getKey(), error))
|
entry.getKey(), error))
|
||||||
|
@ -822,14 +796,6 @@ public class ExamDAOImpl implements ExamDAO {
|
||||||
final Long lmsSetupId,
|
final Long lmsSetupId,
|
||||||
final Collection<ExamRecord> records) {
|
final Collection<ExamRecord> records) {
|
||||||
|
|
||||||
return toDomainModel(lmsSetupId, records, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Result<Collection<Exam>> toDomainModel(
|
|
||||||
final Long lmsSetupId,
|
|
||||||
final Collection<ExamRecord> records,
|
|
||||||
final boolean cached) {
|
|
||||||
|
|
||||||
return Result.tryCatch(() -> {
|
return Result.tryCatch(() -> {
|
||||||
|
|
||||||
// map records
|
// map records
|
||||||
|
@ -840,7 +806,7 @@ public class ExamDAOImpl implements ExamDAO {
|
||||||
// get and map quizzes
|
// get and map quizzes
|
||||||
final Map<String, QuizData> quizzes = this.lmsAPIService
|
final Map<String, QuizData> quizzes = this.lmsAPIService
|
||||||
.getLmsAPITemplate(lmsSetupId)
|
.getLmsAPITemplate(lmsSetupId)
|
||||||
.map(template -> getQuizzesFromLMS(template, recordMapping.keySet(), cached))
|
.map(template -> getQuizzesFromLMS(template, recordMapping.keySet()))
|
||||||
.onError(error -> log.error("Failed to get quizzes for exams: ", error))
|
.onError(error -> log.error("Failed to get quizzes for exams: ", error))
|
||||||
.getOr(Collections.emptyList())
|
.getOr(Collections.emptyList())
|
||||||
.stream()
|
.stream()
|
||||||
|
@ -894,13 +860,10 @@ public class ExamDAOImpl implements ExamDAO {
|
||||||
|
|
||||||
private Collection<Result<QuizData>> getQuizzesFromLMS(
|
private Collection<Result<QuizData>> getQuizzesFromLMS(
|
||||||
final LmsAPITemplate template,
|
final LmsAPITemplate template,
|
||||||
final Set<String> ids,
|
final Set<String> ids) {
|
||||||
final boolean cached) {
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return (cached)
|
return template.getQuizzes(ids);
|
||||||
? template.getQuizzesFromCache(ids)
|
|
||||||
: template.getQuizzes(ids);
|
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
log.error("Unexpected error while using LmsAPITemplate to get quizzes: ", e);
|
log.error("Unexpected error while using LmsAPITemplate to get quizzes: ", e);
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
|
|
|
@ -8,15 +8,10 @@
|
||||||
|
|
||||||
package ch.ethz.seb.sebserver.webservice.servicelayer.lms;
|
package ch.ethz.seb.sebserver.webservice.servicelayer.lms;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
|
|
||||||
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
|
||||||
import ch.ethz.seb.sebserver.gbl.async.MemoizingCircuitBreaker;
|
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.Chapters;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
|
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
|
||||||
|
@ -27,7 +22,6 @@ import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetupTestResult;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.user.ExamineeAccountDetails;
|
import ch.ethz.seb.sebserver.gbl.model.user.ExamineeAccountDetails;
|
||||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ResourceNotFoundException;
|
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.AbstractCourseAccess;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.AbstractCourseAccess;
|
||||||
|
|
||||||
/** Defines an LMS API access template to build SEB Server LMS integration.
|
/** Defines an LMS API access template to build SEB Server LMS integration.
|
||||||
|
@ -135,41 +129,10 @@ public interface LmsAPITemplate {
|
||||||
|
|
||||||
/** Get the quiz data with specified identifier.
|
/** Get the quiz data with specified identifier.
|
||||||
*
|
*
|
||||||
* Default implementation: Uses {@link #getQuizzes(Set<String> ids) } and returns the first matching or an error.
|
|
||||||
*
|
*
|
||||||
* @param id the quiz data identifier
|
* @param id the quiz data identifier
|
||||||
* @return Result refer to the quiz data or to an error when happened */
|
* @return Result refer to the quiz data or to an error when happened */
|
||||||
default Result<QuizData> getQuiz(final String id) {
|
Result<QuizData> getQuiz(final String id);
|
||||||
if (StringUtils.isBlank(id)) {
|
|
||||||
return Result.ofError(new RuntimeException("missing model id"));
|
|
||||||
}
|
|
||||||
|
|
||||||
return getQuizzes(new HashSet<>(Arrays.asList(id)))
|
|
||||||
.stream()
|
|
||||||
.findFirst()
|
|
||||||
.orElse(Result.ofError(new ResourceNotFoundException(EntityType.EXAM, id)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Get all {@link QuizData } for the set of {@link QuizData }-identifiers (ids) from the LMS defined within the
|
|
||||||
* underling LmsSetup, in a collection of Results.
|
|
||||||
*
|
|
||||||
* If there is caching involved this function shall try to get the data from the cache first.
|
|
||||||
*
|
|
||||||
* NOTE: This function depends on the specific LMS implementation and on whether caching the quiz data
|
|
||||||
* makes sense or not. Following strategy is recommended:
|
|
||||||
* Looks first in the cache if the whole set of {@link QuizData } can be get from the cache.
|
|
||||||
* If all quizzes are cached, returns all from cache.
|
|
||||||
* If one or more quiz is not in the cache, requests all quizzes from the API and refreshes the cache
|
|
||||||
*
|
|
||||||
* @param ids the Set of Quiz identifiers to get the {@link QuizData } for
|
|
||||||
* @return Collection of all {@link QuizData } from the given id set */
|
|
||||||
Collection<Result<QuizData>> getQuizzesFromCache(Set<String> ids);
|
|
||||||
|
|
||||||
/** Get a particular quiz data from cache if available. If not, tries to get it from the LMS.
|
|
||||||
*
|
|
||||||
* @param id the quiz identifier, external identifier of the exam.
|
|
||||||
* @return Result refer to the {@link QuizData } or to an error when happended */
|
|
||||||
Result<QuizData> getQuizFromCache(String id);
|
|
||||||
|
|
||||||
/** Clears the underling caches if there are some for a particular implementation. */
|
/** Clears the underling caches if there are some for a particular implementation. */
|
||||||
void clearCache();
|
void clearCache();
|
||||||
|
|
|
@ -9,8 +9,6 @@
|
||||||
package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl;
|
package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
@ -21,7 +19,6 @@ import org.springframework.core.env.Environment;
|
||||||
import ch.ethz.seb.sebserver.gbl.Constants;
|
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||||
import ch.ethz.seb.sebserver.gbl.async.AsyncService;
|
import ch.ethz.seb.sebserver.gbl.async.AsyncService;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.exam.QuizData;
|
import ch.ethz.seb.sebserver.gbl.model.exam.QuizData;
|
||||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
|
||||||
|
|
||||||
/** This implements an overall short time cache for QuizData objects for all implementing
|
/** This implements an overall short time cache for QuizData objects for all implementing
|
||||||
* instances. It uses EH-Cache with a short time to live about 1 - 2 minutes.
|
* instances. It uses EH-Cache with a short time to live about 1 - 2 minutes.
|
||||||
|
@ -97,11 +94,6 @@ public abstract class AbstractCachedCourseAccess extends AbstractCourseAccess {
|
||||||
this.cache.evict(createCacheKey);
|
this.cache.evict(createCacheKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Result<Collection<Result<QuizData>>> getQuizzesFromCache(final Set<String> ids) {
|
|
||||||
return Result.of(ids.stream().map(this::getQuizFromCache).collect(Collectors.toList()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Get the LMS setup identifier that is wrapped within the implementing template.
|
/** Get the LMS setup identifier that is wrapped within the implementing template.
|
||||||
* This is used to create the cache Key.
|
* This is used to create the cache Key.
|
||||||
*
|
*
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
|
|
||||||
package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl;
|
package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl;
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -135,28 +134,6 @@ public abstract class AbstractCourseAccess {
|
||||||
Collections.emptyMap());
|
Collections.emptyMap());
|
||||||
}
|
}
|
||||||
|
|
||||||
/** This abstraction has no cache implementation and therefore this returns a Result
|
|
||||||
* with an "No cache supported error.
|
|
||||||
* </p>
|
|
||||||
* To implement and use caching, this must be overridden and implemented
|
|
||||||
*
|
|
||||||
* @param id The identifier of the QuizData to get from cache
|
|
||||||
* @return Result with an "No cache supported error */
|
|
||||||
public Result<QuizData> getQuizFromCache(final String id) {
|
|
||||||
return Result.ofRuntimeError("No cache supported");
|
|
||||||
}
|
|
||||||
|
|
||||||
/** This abstraction has no cache implementation and therefore this returns a Result
|
|
||||||
* with an "No cache supported error.
|
|
||||||
* </p>
|
|
||||||
* To implement and use caching, this must be overridden and implemented
|
|
||||||
*
|
|
||||||
* @param ids Collection of quiz data identifier to get from the cache
|
|
||||||
* @return Result with an "No cache supported error */
|
|
||||||
public Result<Collection<Result<QuizData>>> getQuizzesFromCache(final Set<String> ids) {
|
|
||||||
return Result.ofRuntimeError("No cache supported");
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Provides a supplier for the quiz data request to use within the circuit breaker */
|
/** Provides a supplier for the quiz data request to use within the circuit breaker */
|
||||||
protected abstract Supplier<List<QuizData>> quizzesSupplier(final Set<String> ids);
|
protected abstract Supplier<List<QuizData>> quizzesSupplier(final Set<String> ids);
|
||||||
|
|
||||||
|
|
|
@ -12,9 +12,12 @@ import java.net.URL;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
@ -195,29 +198,52 @@ final class OpenEdxCourseAccess extends AbstractCachedCourseAccess {
|
||||||
.getOrThrow();
|
.getOrThrow();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public Collection<Result<QuizData>> getQuizzesFromCache(final Set<String> ids) {
|
||||||
public Result<QuizData> getQuizFromCache(final String id) {
|
final HashSet<String> leftIds = new HashSet<>(ids);
|
||||||
return Result.tryCatch(() -> {
|
final Collection<Result<QuizData>> result = new ArrayList<>();
|
||||||
|
ids.stream()
|
||||||
|
.map(this::getQuizFromCache)
|
||||||
|
.forEach(q -> {
|
||||||
|
if (q != null) {
|
||||||
|
leftIds.remove(q.id);
|
||||||
|
result.add(Result.of(q));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// first try to get it from short time cache
|
if (!leftIds.isEmpty()) {
|
||||||
QuizData quizData = super.getFromCache(id);
|
super.quizzesRequest.protectedRun(this.quizzesSupplier(leftIds))
|
||||||
if (quizData != null) {
|
.onError(error -> log.error("Failed to get quizzes by ids: ", error))
|
||||||
return quizData;
|
.getOrElse(() -> Collections.emptyList())
|
||||||
}
|
.stream()
|
||||||
|
.forEach(q -> {
|
||||||
|
leftIds.remove(q.id);
|
||||||
|
result.add(Result.of(q));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Otherwise get one course from LMS and cache
|
if (!leftIds.isEmpty()) {
|
||||||
final LmsSetup lmsSetup = getApiTemplateDataSupplier().getLmsSetup();
|
leftIds.forEach(q -> result.add(Result.ofError(new NoSuchElementException())));
|
||||||
final String externalStartURI = getExternalLMSServerAddress(lmsSetup);
|
}
|
||||||
quizData = quizDataOf(
|
|
||||||
lmsSetup,
|
|
||||||
this.getOneCourse(id, this.restTemplate, id),
|
|
||||||
externalStartURI);
|
|
||||||
|
|
||||||
if (quizData != null) {
|
return result;
|
||||||
super.putToCache(quizData);
|
}
|
||||||
}
|
|
||||||
return quizData;
|
public QuizData getQuizFromCache(final String id) {
|
||||||
});
|
return super.getFromCache(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public QuizData getQuizFromLMS(final String id) {
|
||||||
|
final LmsSetup lmsSetup = getApiTemplateDataSupplier().getLmsSetup();
|
||||||
|
final String externalStartURI = getExternalLMSServerAddress(lmsSetup);
|
||||||
|
final QuizData quizData = quizDataOf(
|
||||||
|
lmsSetup,
|
||||||
|
this.getOneCourse(id, this.restTemplate, id),
|
||||||
|
externalStartURI);
|
||||||
|
|
||||||
|
if (quizData != null) {
|
||||||
|
super.putToCache(quizData);
|
||||||
|
}
|
||||||
|
return quizData;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -10,9 +10,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.edx;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -82,33 +80,21 @@ final class OpenEdxLmsAPITemplate implements LmsAPITemplate {
|
||||||
.collect(Collectors.toList()));
|
.collect(Collectors.toList()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result<QuizData> getQuiz(final String id) {
|
||||||
|
return Result.tryCatch(() -> {
|
||||||
|
final QuizData quizFromCache = this.openEdxCourseAccess.getQuizFromCache(id);
|
||||||
|
if (quizFromCache != null) {
|
||||||
|
return quizFromCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.openEdxCourseAccess.getQuizFromLMS(id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<Result<QuizData>> getQuizzes(final Set<String> ids) {
|
public Collection<Result<QuizData>> getQuizzes(final Set<String> ids) {
|
||||||
final Map<String, QuizData> mapping = this.openEdxCourseAccess
|
return this.openEdxCourseAccess.getQuizzesFromCache(ids);
|
||||||
.quizzesSupplier(ids)
|
|
||||||
.get()
|
|
||||||
.stream()
|
|
||||||
.collect(Collectors.toMap(qd -> qd.id, Function.identity()));
|
|
||||||
|
|
||||||
return ids.stream()
|
|
||||||
.map(id -> {
|
|
||||||
final QuizData data = mapping.get(id);
|
|
||||||
return (data == null) ? Result.<QuizData> ofRuntimeError("Missing id: " + id) : Result.of(data);
|
|
||||||
})
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Result<QuizData> getQuizFromCache(final String id) {
|
|
||||||
return this.openEdxCourseAccess
|
|
||||||
.getQuizFromCache(id)
|
|
||||||
.orElse(() -> getQuiz(id));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<Result<QuizData>> getQuizzesFromCache(final Set<String> ids) {
|
|
||||||
return this.openEdxCourseAccess.getQuizzesFromCache(ids)
|
|
||||||
.getOrElse(() -> getQuizzes(ids));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -9,9 +9,7 @@
|
||||||
package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.mockup;
|
package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.mockup;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
@ -164,6 +162,15 @@ public class MockupLmsAPITemplate implements LmsAPITemplate {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result<QuizData> getQuiz(final String id) {
|
||||||
|
return Result.of(this.mockups
|
||||||
|
.stream()
|
||||||
|
.filter(q -> id.equals(q.id))
|
||||||
|
.findFirst()
|
||||||
|
.get());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<Result<QuizData>> getQuizzes(final Set<String> ids) {
|
public Collection<Result<QuizData>> getQuizzes(final Set<String> ids) {
|
||||||
if (!authenticate()) {
|
if (!authenticate()) {
|
||||||
|
@ -178,16 +185,6 @@ public class MockupLmsAPITemplate implements LmsAPITemplate {
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<Result<QuizData>> getQuizzesFromCache(final Set<String> ids) {
|
|
||||||
return getQuizzes(ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Result<QuizData> getQuizFromCache(final String id) {
|
|
||||||
return getQuizzes(new HashSet<>(Arrays.asList(id))).iterator().next();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clearCache() {
|
public void clearCache() {
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.joda.time.DateTime;
|
import org.joda.time.DateTime;
|
||||||
|
@ -213,7 +214,6 @@ public class MoodleCourseAccess extends AbstractCourseAccess {
|
||||||
return LmsSetupTestResult.ofOkay(LmsType.MOODLE);
|
return LmsSetupTestResult.ofOkay(LmsType.MOODLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Result<QuizData> getQuizFromCache(final String id) {
|
public Result<QuizData> getQuizFromCache(final String id) {
|
||||||
return Result.tryCatch(() -> {
|
return Result.tryCatch(() -> {
|
||||||
|
|
||||||
|
@ -245,43 +245,48 @@ public class MoodleCourseAccess extends AbstractCourseAccess {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new RuntimeException("No quiz found in cache");
|
// get from LMS
|
||||||
|
final Set<String> ids = Stream.of(id).collect(Collectors.toSet());
|
||||||
|
return super.quizzesRequest
|
||||||
|
.protectedRun(quizzesSupplier(ids))
|
||||||
|
.getOrThrow()
|
||||||
|
.get(0);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public Collection<Result<QuizData>> getQuizzesFromCache(final Set<String> ids) {
|
||||||
public Result<Collection<Result<QuizData>>> getQuizzesFromCache(final Set<String> ids) {
|
final List<QuizData> cached = getCached();
|
||||||
return Result.tryCatch(() -> {
|
final List<QuizData> available = (cached != null)
|
||||||
final List<QuizData> cached = getCached();
|
? cached
|
||||||
final List<QuizData> available = (cached != null)
|
: Collections.emptyList();
|
||||||
? cached
|
|
||||||
: Collections.emptyList();
|
|
||||||
|
|
||||||
final Map<String, QuizData> quizMapping = available
|
final Map<String, QuizData> quizMapping = available
|
||||||
|
.stream()
|
||||||
|
.collect(Collectors.toMap(q -> q.id, Function.identity()));
|
||||||
|
|
||||||
|
if (!quizMapping.keySet().containsAll(ids)) {
|
||||||
|
|
||||||
|
final Map<String, QuizData> collect = super.quizzesRequest
|
||||||
|
.protectedRun(quizzesSupplier(ids))
|
||||||
|
.onError(error -> log.error("Failed to get quizzes by ids: ", error))
|
||||||
|
.getOrElse(() -> Collections.emptyList())
|
||||||
.stream()
|
.stream()
|
||||||
.collect(Collectors.toMap(q -> q.id, Function.identity()));
|
.collect(Collectors.toMap(qd -> qd.id, Function.identity()));
|
||||||
|
if (collect != null) {
|
||||||
if (!quizMapping.keySet().containsAll(ids)) {
|
quizMapping.clear();
|
||||||
|
quizMapping.putAll(collect);
|
||||||
final Map<String, QuizData> collect = quizzesSupplier(ids).get()
|
|
||||||
.stream()
|
|
||||||
.collect(Collectors.toMap(qd -> qd.id, Function.identity()));
|
|
||||||
if (collect != null) {
|
|
||||||
quizMapping.clear();
|
|
||||||
quizMapping.putAll(collect);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return ids
|
return ids
|
||||||
.stream()
|
.stream()
|
||||||
.map(id -> {
|
.map(id -> {
|
||||||
final QuizData q = quizMapping.get(id);
|
final QuizData q = quizMapping.get(id);
|
||||||
return (q == null)
|
return (q == null)
|
||||||
? Result.<QuizData> ofError(new NoSuchElementException("Quiz with id: " + id))
|
? Result.<QuizData> ofError(new NoSuchElementException("Quiz with id: " + id))
|
||||||
: Result.of(q);
|
: Result.of(q);
|
||||||
})
|
})
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -10,9 +10,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -95,25 +93,13 @@ public class MoodleLmsAPITemplate implements LmsAPITemplate {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<Result<QuizData>> getQuizzes(final Set<String> ids) {
|
public Result<QuizData> getQuiz(final String id) {
|
||||||
final Map<String, QuizData> mapping = this.moodleCourseAccess
|
return this.moodleCourseAccess.getQuizFromCache(id);
|
||||||
.quizzesSupplier(ids)
|
|
||||||
.get()
|
|
||||||
.stream()
|
|
||||||
.collect(Collectors.toMap(qd -> qd.id, Function.identity()));
|
|
||||||
|
|
||||||
return ids.stream()
|
|
||||||
.map(id -> {
|
|
||||||
final QuizData data = mapping.get(id);
|
|
||||||
return (data == null) ? Result.<QuizData> ofRuntimeError("Missing id: " + id) : Result.of(data);
|
|
||||||
})
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Result<QuizData> getQuizFromCache(final String id) {
|
public Collection<Result<QuizData>> getQuizzes(final Set<String> ids) {
|
||||||
return this.moodleCourseAccess.getQuizFromCache(id)
|
return this.moodleCourseAccess.getQuizzesFromCache(ids);
|
||||||
.orElse(() -> getQuiz(id));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -121,12 +107,6 @@ public class MoodleLmsAPITemplate implements LmsAPITemplate {
|
||||||
this.moodleCourseAccess.clearCache();
|
this.moodleCourseAccess.clearCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<Result<QuizData>> getQuizzesFromCache(final Set<String> ids) {
|
|
||||||
return this.moodleCourseAccess.getQuizzesFromCache(ids)
|
|
||||||
.getOrElse(() -> getQuizzes(ids));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Result<Chapters> getCourseChapters(final String courseId) {
|
public Result<Chapters> getCourseChapters(final String courseId) {
|
||||||
return Result.tryCatch(() -> this.moodleCourseAccess
|
return Result.tryCatch(() -> this.moodleCourseAccess
|
||||||
|
|
|
@ -124,7 +124,7 @@ public class ExamSessionServiceImpl implements ExamSessionService {
|
||||||
final Collection<APIMessage> result = new ArrayList<>();
|
final Collection<APIMessage> result = new ArrayList<>();
|
||||||
|
|
||||||
final Exam exam = this.examDAO
|
final Exam exam = this.examDAO
|
||||||
.getWithQuizDataFromCache(examId)
|
.byPK(examId)
|
||||||
.getOrThrow();
|
.getOrThrow();
|
||||||
|
|
||||||
// check lms connection
|
// check lms connection
|
||||||
|
|
|
@ -280,7 +280,7 @@ public class ExamAdministrationController extends EntityController<Exam, Exam> {
|
||||||
|
|
||||||
checkReadPrivilege(institutionId);
|
checkReadPrivilege(institutionId);
|
||||||
return this.examDAO
|
return this.examDAO
|
||||||
.getWithQuizDataFromCache(modelId)
|
.byPK(modelId)
|
||||||
.flatMap(this.examAdminService::isRestricted)
|
.flatMap(this.examAdminService::isRestricted)
|
||||||
.getOrThrow();
|
.getOrThrow();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue