simplified LMS API

This commit is contained in:
anhefti 2021-05-17 19:26:26 +02:00
parent 0d8fb4b880
commit 0b00995aa7
13 changed files with 117 additions and 230 deletions

View file

@ -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

View file

@ -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(

View file

@ -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();

View file

@ -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();

View file

@ -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.
* *

View file

@ -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);

View file

@ -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

View file

@ -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

View file

@ -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() {

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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();
} }