Better course recovering handling and logging
This commit is contained in:
		
							parent
							
								
									9674f08b8b
								
							
						
					
					
						commit
						21200bd9a2
					
				
					 4 changed files with 62 additions and 10 deletions
				
			
		| 
						 | 
					@ -261,6 +261,10 @@ public final class CircuitBreaker<T> {
 | 
				
			||||||
            if (log.isWarnEnabled()) {
 | 
					            if (log.isWarnEnabled()) {
 | 
				
			||||||
                log.warn("Attempt error: {}, {}", e.getMessage(), this.state);
 | 
					                log.warn("Attempt error: {}, {}", e.getMessage(), this.state);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            final Throwable cause = e.getCause();
 | 
				
			||||||
 | 
					            if (cause != null && cause instanceof Exception) {
 | 
				
			||||||
 | 
					                return Result.ofError((Exception) cause);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            return Result.ofError(e);
 | 
					            return Result.ofError(e);
 | 
				
			||||||
        } catch (final TimeoutException e) {
 | 
					        } catch (final TimeoutException e) {
 | 
				
			||||||
            future.cancel(false);
 | 
					            future.cancel(false);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -48,7 +48,10 @@ public final class LmsSetup implements GrantEntity, Activatable {
 | 
				
			||||||
         * The SEB restriciton is usually in the form of certain hash keys and addition
 | 
					         * The SEB restriciton is usually in the form of certain hash keys and addition
 | 
				
			||||||
         * restriction settings that prompt the LMS to check access on course/quiz connection and
 | 
					         * restriction settings that prompt the LMS to check access on course/quiz connection and
 | 
				
			||||||
         * allow only access for a dedicated SEB client with the right configuration in place. */
 | 
					         * allow only access for a dedicated SEB client with the right configuration in place. */
 | 
				
			||||||
        SEB_RESTRICTION
 | 
					        SEB_RESTRICTION,
 | 
				
			||||||
 | 
					        /** Indicates if the LMS integration has some process for course recovery
 | 
				
			||||||
 | 
					         * after backup-restore process for example. */
 | 
				
			||||||
 | 
					        COURSE_RECOVERY
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /** Defines the supported types if LMS bindings.
 | 
					    /** Defines the supported types if LMS bindings.
 | 
				
			||||||
| 
						 | 
					@ -59,9 +62,9 @@ public final class LmsSetup implements GrantEntity, Activatable {
 | 
				
			||||||
        /** The Open edX LMS binding features both APIs, course access as well as SEB restriction */
 | 
					        /** The Open edX LMS binding features both APIs, course access as well as SEB restriction */
 | 
				
			||||||
        OPEN_EDX(Features.COURSE_API, Features.SEB_RESTRICTION),
 | 
					        OPEN_EDX(Features.COURSE_API, Features.SEB_RESTRICTION),
 | 
				
			||||||
        /** The Moodle binding features only the course access API so far */
 | 
					        /** The Moodle binding features only the course access API so far */
 | 
				
			||||||
        MOODLE(Features.COURSE_API /* , Features.SEB_RESTRICTION */),
 | 
					        MOODLE(Features.COURSE_API, Features.COURSE_RECOVERY /* , Features.SEB_RESTRICTION */),
 | 
				
			||||||
        /** The Moodle binding features with SEB Server integration plugin for fully featured */
 | 
					        /** The Moodle binding features with SEB Server integration plugin for fully featured */
 | 
				
			||||||
        MOODLE_PLUGIN(Features.COURSE_API, Features.SEB_RESTRICTION),
 | 
					        MOODLE_PLUGIN(Features.COURSE_API, Features.COURSE_RECOVERY, Features.SEB_RESTRICTION),
 | 
				
			||||||
        /** The Ans Delft binding is on the way */
 | 
					        /** The Ans Delft binding is on the way */
 | 
				
			||||||
        ANS_DELFT(Features.COURSE_API, Features.SEB_RESTRICTION),
 | 
					        ANS_DELFT(Features.COURSE_API, Features.SEB_RESTRICTION),
 | 
				
			||||||
        /** The OpenOLAT binding is on the way */
 | 
					        /** The OpenOLAT binding is on the way */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -48,6 +48,8 @@ public class LmsAPITemplateAdapter implements LmsAPITemplate {
 | 
				
			||||||
    private final CircuitBreaker<Collection<QuizData>> quizzesRequest;
 | 
					    private final CircuitBreaker<Collection<QuizData>> quizzesRequest;
 | 
				
			||||||
    /** CircuitBreaker for protected quiz and course data requests */
 | 
					    /** CircuitBreaker for protected quiz and course data requests */
 | 
				
			||||||
    private final CircuitBreaker<QuizData> quizRequest;
 | 
					    private final CircuitBreaker<QuizData> quizRequest;
 | 
				
			||||||
 | 
					    /** CircuitBreaker for protected quiz and course data requests */
 | 
				
			||||||
 | 
					    private final CircuitBreaker<QuizData> quizRecoverRequest;
 | 
				
			||||||
    /** CircuitBreaker for protected chapter data requests */
 | 
					    /** CircuitBreaker for protected chapter data requests */
 | 
				
			||||||
    private final CircuitBreaker<Chapters> chaptersRequest;
 | 
					    private final CircuitBreaker<Chapters> chaptersRequest;
 | 
				
			||||||
    /** CircuitBreaker for protected examinee account details requests */
 | 
					    /** CircuitBreaker for protected examinee account details requests */
 | 
				
			||||||
| 
						 | 
					@ -109,6 +111,20 @@ public class LmsAPITemplateAdapter implements LmsAPITemplate {
 | 
				
			||||||
                        Long.class,
 | 
					                        Long.class,
 | 
				
			||||||
                        0L));
 | 
					                        0L));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.quizRecoverRequest = asyncService.createCircuitBreaker(
 | 
				
			||||||
 | 
					                environment.getProperty(
 | 
				
			||||||
 | 
					                        "sebserver.webservice.circuitbreaker.quizzesRequest.attempts",
 | 
				
			||||||
 | 
					                        Integer.class,
 | 
				
			||||||
 | 
					                        1),
 | 
				
			||||||
 | 
					                environment.getProperty(
 | 
				
			||||||
 | 
					                        "sebserver.webservice.circuitbreaker.quizzesRequest.blockingTime",
 | 
				
			||||||
 | 
					                        Long.class,
 | 
				
			||||||
 | 
					                        Constants.SECOND_IN_MILLIS * 10),
 | 
				
			||||||
 | 
					                environment.getProperty(
 | 
				
			||||||
 | 
					                        "sebserver.webservice.circuitbreaker.quizzesRequest.timeToRecover",
 | 
				
			||||||
 | 
					                        Long.class,
 | 
				
			||||||
 | 
					                        0L));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.chaptersRequest = asyncService.createCircuitBreaker(
 | 
					        this.chaptersRequest = asyncService.createCircuitBreaker(
 | 
				
			||||||
                environment.getProperty(
 | 
					                environment.getProperty(
 | 
				
			||||||
                        "sebserver.webservice.circuitbreaker.chaptersRequest.attempts",
 | 
					                        "sebserver.webservice.circuitbreaker.chaptersRequest.attempts",
 | 
				
			||||||
| 
						 | 
					@ -293,11 +309,8 @@ public class LmsAPITemplateAdapter implements LmsAPITemplate {
 | 
				
			||||||
            log.debug("Try to recover quiz for exam {} for LMSSetup: {}", exam, lmsSetup());
 | 
					            log.debug("Try to recover quiz for exam {} for LMSSetup: {}", exam, lmsSetup());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return this.quizRequest.protectedRun(() -> this.courseAccessAPI
 | 
					        return this.quizRecoverRequest.protectedRun(() -> this.courseAccessAPI
 | 
				
			||||||
                .tryRecoverQuizForExam(exam)
 | 
					                .tryRecoverQuizForExam(exam)
 | 
				
			||||||
                .onError(error -> log.error(
 | 
					 | 
				
			||||||
                        "Failed to run protectedQuizRecoverRequest: {}",
 | 
					 | 
				
			||||||
                        error.getMessage()))
 | 
					 | 
				
			||||||
                .getOrThrow());
 | 
					                .getOrThrow());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -27,6 +27,7 @@ import ch.ethz.seb.sebserver.gbl.api.EntityType;
 | 
				
			||||||
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
 | 
					import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
 | 
				
			||||||
import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamStatus;
 | 
					import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamStatus;
 | 
				
			||||||
import ch.ethz.seb.sebserver.gbl.model.exam.QuizData;
 | 
					import ch.ethz.seb.sebserver.gbl.model.exam.QuizData;
 | 
				
			||||||
 | 
					import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup.Features;
 | 
				
			||||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
 | 
					import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
 | 
				
			||||||
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;
 | 
				
			||||||
| 
						 | 
					@ -35,6 +36,7 @@ import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.AdditionalAttribut
 | 
				
			||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.AdditionalAttributesDAO;
 | 
					import ch.ethz.seb.sebserver.webservice.servicelayer.dao.AdditionalAttributesDAO;
 | 
				
			||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO;
 | 
					import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO;
 | 
				
			||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPIService;
 | 
					import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPIService;
 | 
				
			||||||
 | 
					import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplate;
 | 
				
			||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.SEBRestrictionService;
 | 
					import ch.ethz.seb.sebserver.webservice.servicelayer.lms.SEBRestrictionService;
 | 
				
			||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamFinishedEvent;
 | 
					import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamFinishedEvent;
 | 
				
			||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamResetEvent;
 | 
					import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamResetEvent;
 | 
				
			||||||
| 
						 | 
					@ -149,6 +151,11 @@ class ExamUpdateHandler {
 | 
				
			||||||
                                } else {
 | 
					                                } else {
 | 
				
			||||||
                                    if (!exam.isLmsAvailable()) {
 | 
					                                    if (!exam.isLmsAvailable()) {
 | 
				
			||||||
                                        this.examDAO.markLMSAvailability(quiz.id, true, updateId);
 | 
					                                        this.examDAO.markLMSAvailability(quiz.id, true, updateId);
 | 
				
			||||||
 | 
					                                        // delete attempts attribute
 | 
				
			||||||
 | 
					                                        this.additionalAttributesDAO.delete(
 | 
				
			||||||
 | 
					                                                EntityType.EXAM,
 | 
				
			||||||
 | 
					                                                exam.id,
 | 
				
			||||||
 | 
					                                                Exam.ADDITIONAL_ATTR_QUIZ_RECOVER_ATTEMPTS);
 | 
				
			||||||
                                    }
 | 
					                                    }
 | 
				
			||||||
                                    failedOrMissing.remove(quiz.id);
 | 
					                                    failedOrMissing.remove(quiz.id);
 | 
				
			||||||
                                    log.info("Updated quiz data for exam: {}", updateQuizData.get());
 | 
					                                    log.info("Updated quiz data for exam: {}", updateQuizData.get());
 | 
				
			||||||
| 
						 | 
					@ -351,8 +358,24 @@ class ExamUpdateHandler {
 | 
				
			||||||
                !Utils.isEqualsWithEmptyCheck(exam.getDescription(), quizData.description) ||
 | 
					                !Utils.isEqualsWithEmptyCheck(exam.getDescription(), quizData.description) ||
 | 
				
			||||||
                !Utils.isEqualsWithEmptyCheck(exam.getStartURL(), quizData.startURL)) {
 | 
					                !Utils.isEqualsWithEmptyCheck(exam.getStartURL(), quizData.startURL)) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (log.isDebugEnabled()) {
 | 
					            if (!Utils.isEqualsWithEmptyCheck(exam.name, quizData.name)) {
 | 
				
			||||||
                log.debug("Update difference from LMS. Exam:{}, QuizData: {}", exam, quizData);
 | 
					                log.info("Update name difference from LMS. Exam:{}, QuizData: {}", exam.name, quizData.name);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (!Objects.equals(exam.startTime, quizData.startTime)) {
 | 
				
			||||||
 | 
					                log.info("Update startTime difference from LMS. Exam:{}, QuizData: {}", exam.startTime,
 | 
				
			||||||
 | 
					                        quizData.startTime);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (!Objects.equals(exam.endTime, quizData.endTime)) {
 | 
				
			||||||
 | 
					                log.info("Update endTime difference from LMS. Exam:{}, QuizData: {}", exam.endTime, quizData.endTime);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (!Utils.isEqualsWithEmptyCheck(exam.getDescription(), quizData.description)) {
 | 
				
			||||||
 | 
					                log.info("Update description difference from LMS. Exam:{}, QuizData: {}", exam.getDescription(),
 | 
				
			||||||
 | 
					                        quizData.description);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (!Utils.isEqualsWithEmptyCheck(exam.getStartURL(), quizData.startURL)) {
 | 
				
			||||||
 | 
					                log.info("Update startURL difference from LMS. Exam:{}, QuizData: {}",
 | 
				
			||||||
 | 
					                        exam.getStartURL(),
 | 
				
			||||||
 | 
					                        quizData.startURL);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return true;
 | 
					            return true;
 | 
				
			||||||
| 
						 | 
					@ -384,6 +407,14 @@ class ExamUpdateHandler {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return Result.tryCatch(() -> {
 | 
					        return Result.tryCatch(() -> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            final LmsAPITemplate lmsTemplate = this.lmsAPIService
 | 
				
			||||||
 | 
					                    .getLmsAPITemplate(lmsSetupId)
 | 
				
			||||||
 | 
					                    .getOrThrow();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (!lmsTemplate.getType().features.contains(Features.COURSE_RECOVERY)) {
 | 
				
			||||||
 | 
					                throw new UnsupportedOperationException("No Course Recovery");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            final Exam exam = exams.get(quizId);
 | 
					            final Exam exam = exams.get(quizId);
 | 
				
			||||||
            final int attempts = Integer.parseInt(this.additionalAttributesDAO.getAdditionalAttribute(
 | 
					            final int attempts = Integer.parseInt(this.additionalAttributesDAO.getAdditionalAttribute(
 | 
				
			||||||
                    EntityType.EXAM,
 | 
					                    EntityType.EXAM,
 | 
				
			||||||
| 
						 | 
					@ -400,7 +431,8 @@ class ExamUpdateHandler {
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            log.info(
 | 
					            log.info(
 | 
				
			||||||
                    "Try to recover quiz data for Moodle quiz with internal identifier: {}",
 | 
					                    "Try to recover quiz data from LMS: {} quiz with internal identifier: {}",
 | 
				
			||||||
 | 
					                    lmsSetupId,
 | 
				
			||||||
                    quizId);
 | 
					                    quizId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return this.lmsAPIService
 | 
					            return this.lmsAPIService
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue