Filter fromDate in Moodle lookup

This commit is contained in:
anhefti 2020-12-17 18:03:56 +01:00
parent 3cefcbe3f3
commit 7fcfcf1445
4 changed files with 44 additions and 10 deletions

View file

@ -43,7 +43,7 @@ public abstract class CourseAccess {
protected CourseAccess(final AsyncService asyncService) { protected CourseAccess(final AsyncService asyncService) {
this.allQuizzesRequest = asyncService.createMemoizingCircuitBreaker( this.allQuizzesRequest = asyncService.createMemoizingCircuitBreaker(
allQuizzesSupplier(), allQuizzesSupplier(null),
3, 3,
Constants.MINUTE_IN_MILLIS, Constants.MINUTE_IN_MILLIS,
Constants.MINUTE_IN_MILLIS, Constants.MINUTE_IN_MILLIS,
@ -101,6 +101,9 @@ public abstract class CourseAccess {
} }
public Result<List<QuizData>> getQuizzes(final FilterMap filterMap) { public Result<List<QuizData>> getQuizzes(final FilterMap filterMap) {
if (filterMap != null) {
this.allQuizzesRequest.setSupplier(allQuizzesSupplier(filterMap));
}
return this.allQuizzesRequest.get() return this.allQuizzesRequest.get()
.map(LmsAPIService.quizzesFilterFunction(filterMap)); .map(LmsAPIService.quizzesFilterFunction(filterMap));
} }
@ -136,7 +139,7 @@ public abstract class CourseAccess {
protected abstract Supplier<List<QuizData>> quizzesSupplier(final Set<String> ids); protected abstract Supplier<List<QuizData>> quizzesSupplier(final Set<String> ids);
protected abstract Supplier<List<QuizData>> allQuizzesSupplier(); protected abstract Supplier<List<QuizData>> allQuizzesSupplier(final FilterMap filterMap);
protected abstract Supplier<Chapters> getCourseChaptersSupplier(final String courseId); protected abstract Supplier<Chapters> getCourseChaptersSupplier(final String courseId);

View file

@ -49,6 +49,7 @@ 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.gbl.util.Utils; import ch.ethz.seb.sebserver.gbl.util.Utils;
import ch.ethz.seb.sebserver.webservice.WebserviceInfo; import ch.ethz.seb.sebserver.webservice.WebserviceInfo;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.CourseAccess; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.CourseAccess;
/** Implements the LmsAPITemplate for Open edX LMS Course API access. /** Implements the LmsAPITemplate for Open edX LMS Course API access.
@ -173,7 +174,7 @@ final class OpenEdxCourseAccess extends CourseAccess {
} }
@Override @Override
protected Supplier<List<QuizData>> allQuizzesSupplier() { protected Supplier<List<QuizData>> allQuizzesSupplier(final FilterMap filterMap) {
return () -> getRestTemplate() return () -> getRestTemplate()
.map(this::collectAllQuizzes) .map(this::collectAllQuizzes)
.getOrThrow(); .getOrThrow();

View file

@ -20,6 +20,7 @@ import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.LinkedMultiValueMap;
@ -40,6 +41,7 @@ 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.gbl.util.Utils; import ch.ethz.seb.sebserver.gbl.util.Utils;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.CourseAccess; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.CourseAccess;
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.MoodleRestTemplateFactory.MoodleAPIRestTemplate; import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.MoodleRestTemplateFactory.MoodleAPIRestTemplate;
@ -48,6 +50,8 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.MoodleRestT
* See also: https://docs.moodle.org/dev/Web_service_API_functions */ * See also: https://docs.moodle.org/dev/Web_service_API_functions */
public class MoodleCourseAccess extends CourseAccess { public class MoodleCourseAccess extends CourseAccess {
private static final long INITIAL_WAIT_TIME = 3 * Constants.SECOND_IN_MILLIS;
private static final Logger log = LoggerFactory.getLogger(MoodleCourseAccess.class); private static final Logger log = LoggerFactory.getLogger(MoodleCourseAccess.class);
private static final String MOODLE_QUIZ_START_URL_PATH = "mod/quiz/view.php?id="; private static final String MOODLE_QUIZ_START_URL_PATH = "mod/quiz/view.php?id=";
@ -172,9 +176,9 @@ public class MoodleCourseAccess extends CourseAccess {
} }
@Override @Override
protected Supplier<List<QuizData>> allQuizzesSupplier() { protected Supplier<List<QuizData>> allQuizzesSupplier(final FilterMap filterMap) {
return () -> getRestTemplate() return () -> getRestTemplate()
.map(template -> collectAllQuizzes(template)) .map(template -> collectAllQuizzes(template, filterMap))
.getOrThrow(); .getOrThrow();
} }
@ -183,34 +187,51 @@ public class MoodleCourseAccess extends CourseAccess {
throw new UnsupportedOperationException("not available yet"); throw new UnsupportedOperationException("not available yet");
} }
private List<QuizData> collectAllQuizzes(final MoodleAPIRestTemplate restTemplate) { private List<QuizData> collectAllQuizzes(
final MoodleAPIRestTemplate restTemplate,
final FilterMap filterMap) {
final String urlPrefix = (this.lmsSetup.lmsApiUrl.endsWith(Constants.URL_PATH_SEPARATOR)) final String urlPrefix = (this.lmsSetup.lmsApiUrl.endsWith(Constants.URL_PATH_SEPARATOR))
? this.lmsSetup.lmsApiUrl + MOODLE_QUIZ_START_URL_PATH ? this.lmsSetup.lmsApiUrl + MOODLE_QUIZ_START_URL_PATH
: this.lmsSetup.lmsApiUrl + Constants.URL_PATH_SEPARATOR + MOODLE_QUIZ_START_URL_PATH; : this.lmsSetup.lmsApiUrl + Constants.URL_PATH_SEPARATOR + MOODLE_QUIZ_START_URL_PATH;
final DateTime quizFromTime = (filterMap != null) ? filterMap.getQuizFromTime() : null;
final long fromCutTime = (quizFromTime != null) ? Utils.toUnixTimeInSeconds(quizFromTime) : -1;
Collection<CourseData> courseQuizData = Collections.emptyList(); Collection<CourseData> courseQuizData = Collections.emptyList();
if (this.moodleCourseDataLazyLoader.isRunning()) { if (this.moodleCourseDataLazyLoader.isRunning()) {
courseQuizData = this.moodleCourseDataLazyLoader.getPreFilteredCourseIds(); courseQuizData = this.moodleCourseDataLazyLoader.getPreFilteredCourseIds();
} else if (this.moodleCourseDataLazyLoader.getLastRunTime() <= 0) { } else if (this.moodleCourseDataLazyLoader.getLastRunTime() <= 0) {
// set cut time if available
if (fromCutTime >= 0) {
this.moodleCourseDataLazyLoader.setFromCutTime(fromCutTime);
}
// first run async and wait some time, get what is there // first run async and wait some time, get what is there
this.moodleCourseDataLazyLoader.loadAsync(restTemplate); this.moodleCourseDataLazyLoader.loadAsync(restTemplate);
try { try {
Thread.sleep(5 * Constants.SECOND_IN_MILLIS); Thread.sleep(INITIAL_WAIT_TIME);
courseQuizData = this.moodleCourseDataLazyLoader.getPreFilteredCourseIds(); courseQuizData = this.moodleCourseDataLazyLoader.getPreFilteredCourseIds();
} catch (final Exception e) { } catch (final Exception e) {
log.error("Failed to wait for first load run: ", e); log.error("Failed to wait for first load run: ", e);
return Collections.emptyList(); return Collections.emptyList();
} }
} else if (this.moodleCourseDataLazyLoader.isLongRunningTask()) { } else if (this.moodleCourseDataLazyLoader.isLongRunningTask()) {
// kick off the task again when old asynchronously and take back what is there instantly // on long running tasks if we have a different fromCutTime as before
if (Utils.getMillisecondsNow() - this.moodleCourseDataLazyLoader.getLastRunTime() > 10 // kick off the lazy loadung task imeditially with the new time filter
if (fromCutTime > 0 && fromCutTime != this.moodleCourseDataLazyLoader.getFromCutTime()) {
this.moodleCourseDataLazyLoader.setFromCutTime(fromCutTime);
this.moodleCourseDataLazyLoader.loadAsync(restTemplate);
// otherwise kick off only if the last fetch task was then minutes ago
} else if (Utils.getMillisecondsNow() - this.moodleCourseDataLazyLoader.getLastRunTime() > 10
* Constants.MINUTE_IN_MILLIS) { * Constants.MINUTE_IN_MILLIS) {
this.moodleCourseDataLazyLoader.loadAsync(restTemplate); this.moodleCourseDataLazyLoader.loadAsync(restTemplate);
} }
courseQuizData = this.moodleCourseDataLazyLoader.getPreFilteredCourseIds(); courseQuizData = this.moodleCourseDataLazyLoader.getPreFilteredCourseIds();
} else { } else {
// just run the task in sync // just run the task in sync
if (fromCutTime >= 0) {
this.moodleCourseDataLazyLoader.setFromCutTime(fromCutTime);
}
this.moodleCourseDataLazyLoader.loadSync(restTemplate); this.moodleCourseDataLazyLoader.loadSync(restTemplate);
courseQuizData = this.moodleCourseDataLazyLoader.getPreFilteredCourseIds(); courseQuizData = this.moodleCourseDataLazyLoader.getPreFilteredCourseIds();
} }

View file

@ -65,7 +65,7 @@ public class MoodleCourseDataLazyLoader {
private long lastLoadTime = 0; private long lastLoadTime = 0;
private boolean running = false; private boolean running = false;
private final long fromCutTime = DateTime.now(DateTimeZone.UTC).minusYears(3).getMillis() / 1000; private long fromCutTime;
public MoodleCourseDataLazyLoader( public MoodleCourseDataLazyLoader(
final JSONMapper jsonMapper, final JSONMapper jsonMapper,
@ -73,6 +73,15 @@ public class MoodleCourseDataLazyLoader {
this.jsonMapper = jsonMapper; this.jsonMapper = jsonMapper;
this.asyncRunner = asyncRunner; this.asyncRunner = asyncRunner;
this.fromCutTime = Utils.toUnixTimeInSeconds(DateTime.now(DateTimeZone.UTC).minusYears(3));
}
public long getFromCutTime() {
return this.fromCutTime;
}
public void setFromCutTime(final long fromCutTime) {
this.fromCutTime = fromCutTime;
} }
public Set<CourseData> getPreFilteredCourseIds() { public Set<CourseData> getPreFilteredCourseIds() {