Search in sources :

Example 1 with QuizScheduleService

use of de.tum.in.www1.artemis.service.scheduled.quiz.QuizScheduleService in project Artemis by ls1intum.

the class QuizSubmissionWebsocketService method saveSubmission.

// TODO it would be nice to have some kind of startQuiz call that creates the participation with an initialization date. This should happen when the quiz is first shown
// to the user. Then we also could find out how long students work on the quiz on average
/**
 * Saves a quiz submission into the hash maps. Submitted quizzes are marked to be saved into the database in the QuizScheduleService
 *
 * @param exerciseId     the exerciseID to the corresponding QuizExercise
 * @param quizSubmission the submission which should be saved
 * @param principal      refers to the user who initiated the request
 */
@MessageMapping("/topic/quizExercise/{exerciseId}/submission")
public void saveSubmission(@DestinationVariable Long exerciseId, @Payload QuizSubmission quizSubmission, Principal principal) {
    // Without this, custom jpa repository methods don't work in websocket channel.
    SecurityUtils.setAuthorizationObject();
    String username = principal.getName();
    try {
        QuizSubmission updatedQuizSubmission = quizSubmissionService.saveSubmissionForLiveMode(exerciseId, quizSubmission, username, false);
    // send updated submission over websocket (use a thread to prevent that the outbound channel blocks the inbound channel (e.g. due a slow client))
    // to improve the performance, this is currently deactivated: slow clients might lead to bottlenecks so that more important messages can not be distributed any more
    // new Thread(() -> sendSubmissionToUser(username, exerciseId, quizSubmission)).start();
    // log.info("WS.Inbound: Sent quiz submission (async) back to user {} in quiz {} after {} µs ", principal.getName(), exerciseId, (System.nanoTime() - start) / 1000);
    } catch (QuizSubmissionException ex) {
        // send error message over websocket (use a thread to prevent that the outbound channel blocks the inbound channel (e.g. due a slow client))
        new Thread(() -> messagingTemplate.convertAndSendToUser(username, "/topic/quizExercise/" + exerciseId + "/submission", new WebsocketError(ex.getMessage()))).start();
    }
}
Also used : QuizSubmission(de.tum.in.www1.artemis.domain.quiz.QuizSubmission) QuizSubmissionException(de.tum.in.www1.artemis.exception.QuizSubmissionException) MessageMapping(org.springframework.messaging.handler.annotation.MessageMapping)

Example 2 with QuizScheduleService

use of de.tum.in.www1.artemis.service.scheduled.quiz.QuizScheduleService in project ArTEMiS by ls1intum.

the class QuizSubmissionWebsocketService method saveSubmission.

// TODO it would be nice to have some kind of startQuiz call that creates the participation with an initialization date. This should happen when the quiz is first shown
// to the user. Then we also could find out how long students work on the quiz on average
/**
 * Saves a quiz submission into the hash maps. Submitted quizzes are marked to be saved into the database in the QuizScheduleService
 *
 * @param exerciseId     the exerciseID to the corresponding QuizExercise
 * @param quizSubmission the submission which should be saved
 * @param principal      refers to the user who initiated the request
 */
@MessageMapping("/topic/quizExercise/{exerciseId}/submission")
public void saveSubmission(@DestinationVariable Long exerciseId, @Payload QuizSubmission quizSubmission, Principal principal) {
    // Without this, custom jpa repository methods don't work in websocket channel.
    SecurityUtils.setAuthorizationObject();
    String username = principal.getName();
    try {
        QuizSubmission updatedQuizSubmission = quizSubmissionService.saveSubmissionForLiveMode(exerciseId, quizSubmission, username, false);
    // send updated submission over websocket (use a thread to prevent that the outbound channel blocks the inbound channel (e.g. due a slow client))
    // to improve the performance, this is currently deactivated: slow clients might lead to bottlenecks so that more important messages can not be distributed any more
    // new Thread(() -> sendSubmissionToUser(username, exerciseId, quizSubmission)).start();
    // log.info("WS.Inbound: Sent quiz submission (async) back to user {} in quiz {} after {} µs ", principal.getName(), exerciseId, (System.nanoTime() - start) / 1000);
    } catch (QuizSubmissionException ex) {
        // send error message over websocket (use a thread to prevent that the outbound channel blocks the inbound channel (e.g. due a slow client))
        new Thread(() -> messagingTemplate.convertAndSendToUser(username, "/topic/quizExercise/" + exerciseId + "/submission", new WebsocketError(ex.getMessage()))).start();
    }
}
Also used : QuizSubmission(de.tum.in.www1.artemis.domain.quiz.QuizSubmission) QuizSubmissionException(de.tum.in.www1.artemis.exception.QuizSubmissionException) MessageMapping(org.springframework.messaging.handler.annotation.MessageMapping)

Example 3 with QuizScheduleService

use of de.tum.in.www1.artemis.service.scheduled.quiz.QuizScheduleService in project ArTEMiS by ls1intum.

the class QuizSubmissionService method checkSubmissionForLiveModeOrThrow.

/**
 * Check that the user is allowed to currently submit to the specified exercise and throws an exception if not
 */
private void checkSubmissionForLiveModeOrThrow(Long exerciseId, String userLogin, String logText, long start) throws QuizSubmissionException {
    // check if submission is still allowed
    QuizExercise quizExercise = quizScheduleService.getQuizExercise(exerciseId);
    if (quizExercise == null) {
        // Fallback solution
        log.info("Quiz not in QuizScheduleService cache, fetching from DB");
        quizExercise = quizExerciseRepository.findByIdElseThrow(exerciseId);
        quizExercise.setQuizBatches(null);
    }
    log.debug("{}: Received quiz exercise for user {} in quiz {} in {} µs.", logText, userLogin, exerciseId, (System.nanoTime() - start) / 1000);
    if (!quizExercise.isQuizStarted() || quizExercise.isQuizEnded()) {
        throw new QuizSubmissionException("The quiz is not active");
    }
    var cachedSubmission = quizScheduleService.getQuizSubmission(exerciseId, userLogin);
    if (cachedSubmission.isSubmitted()) {
        // the old submission has not yet been processed, so don't allow a new one yet
        throw new QuizSubmissionException("You have already submitted the quiz");
    }
    if (quizExercise.getQuizMode() == QuizMode.SYNCHRONIZED) {
        // the batch exists if the quiz is active, otherwise a new inactive batch is returned
        if (!quizBatchService.getOrCreateSynchronizedQuizBatch(quizExercise).isSubmissionAllowed()) {
            throw new QuizSubmissionException("The quiz is not active");
        }
        // in synchronized mode we cache the participation after we processed the submission, so we can check there if the submission was already processed
        var cachedParticipation = quizScheduleService.getParticipation(exerciseId, userLogin);
        if (cachedParticipation != null && cachedParticipation.getResults().stream().anyMatch(r -> r.getSubmission().isSubmitted())) {
            throw new QuizSubmissionException("You have already submitted the quiz");
        }
    } else {
        // in the other modes the resubmission checks are done at join time and the student-batch association is removed when processing a submission
        var batch = quizBatchService.getQuizBatchForStudentByLogin(quizExercise, userLogin);
        // there is no way of distinguishing these two error cases without an extra db query
        if (batch.isEmpty()) {
            throw new QuizSubmissionException("You did not join or have already submitted the quiz");
        }
        if (!batch.get().isSubmissionAllowed()) {
            throw new QuizSubmissionException("The quiz is not active");
        }
    }
// TODO: additional checks that may be beneficial
// for example it is possible for students that are not members of the course to submit the quiz
// but for performance reasons the checks may have to be done in the quiz submission service where no feedback for the students can be generated
}
Also used : Logger(org.slf4j.Logger) QuizExercise(de.tum.in.www1.artemis.domain.quiz.QuizExercise) QuizSubmission(de.tum.in.www1.artemis.domain.quiz.QuizSubmission) QuizSubmissionException(de.tum.in.www1.artemis.exception.QuizSubmissionException) QuizScheduleService(de.tum.in.www1.artemis.service.scheduled.quiz.QuizScheduleService) ZonedDateTime(java.time.ZonedDateTime) Participation(de.tum.in.www1.artemis.domain.participation.Participation) LoggerFactory(org.slf4j.LoggerFactory) SubmissionType(de.tum.in.www1.artemis.domain.enumeration.SubmissionType) AssessmentType(de.tum.in.www1.artemis.domain.enumeration.AssessmentType) ArrayList(java.util.ArrayList) Result(de.tum.in.www1.artemis.domain.Result) EntityNotFoundException(de.tum.in.www1.artemis.web.rest.errors.EntityNotFoundException) QuizMode(de.tum.in.www1.artemis.domain.enumeration.QuizMode) Service(org.springframework.stereotype.Service) QuizExerciseRepository(de.tum.in.www1.artemis.repository.QuizExerciseRepository) Optional(java.util.Optional) StudentParticipation(de.tum.in.www1.artemis.domain.participation.StudentParticipation) SubmittedAnswer(de.tum.in.www1.artemis.domain.quiz.SubmittedAnswer) QuizSubmissionRepository(de.tum.in.www1.artemis.repository.QuizSubmissionRepository) ResultRepository(de.tum.in.www1.artemis.repository.ResultRepository) QuizSubmissionException(de.tum.in.www1.artemis.exception.QuizSubmissionException) QuizExercise(de.tum.in.www1.artemis.domain.quiz.QuizExercise)

Example 4 with QuizScheduleService

use of de.tum.in.www1.artemis.service.scheduled.quiz.QuizScheduleService in project ArTEMiS by ls1intum.

the class QuizSubmissionService method saveSubmissionForLiveMode.

/**
 * Saves a quiz submission into the hash maps for live quizzes. Submitted quizzes are marked to be saved into the database in the QuizScheduleService
 *
 * @param exerciseId the exerciseID to the corresponding QuizExercise
 * @param quizSubmission the submission which should be saved
 * @param userLogin the login of the user who has initiated the request
 * @param submitted whether the user has pressed the submit button or not
 *
 * @return the updated quiz submission object
 * @throws QuizSubmissionException handles errors, e.g. when the live quiz has already ended, or when the quiz was already submitted before
 */
public QuizSubmission saveSubmissionForLiveMode(Long exerciseId, QuizSubmission quizSubmission, String userLogin, boolean submitted) throws QuizSubmissionException {
    // TODO: what happens if a user executes this call twice in the same moment (using 2 threads)
    String logText = submitted ? "submit quiz in live mode:" : "save quiz in live mode:";
    long start = System.nanoTime();
    checkSubmissionForLiveModeOrThrow(exerciseId, userLogin, logText, start);
    // recreate pointers back to submission in each submitted answer
    for (SubmittedAnswer submittedAnswer : quizSubmission.getSubmittedAnswers()) {
        submittedAnswer.setSubmission(quizSubmission);
    }
    // set submission date
    quizSubmission.setSubmissionDate(ZonedDateTime.now());
    // save submission to HashMap
    quizScheduleService.updateSubmission(exerciseId, userLogin, quizSubmission);
    log.info("{} Saved quiz submission for user {} in quiz {} after {} µs ", logText, userLogin, exerciseId, (System.nanoTime() - start) / 1000);
    return quizSubmission;
}
Also used : SubmittedAnswer(de.tum.in.www1.artemis.domain.quiz.SubmittedAnswer)

Example 5 with QuizScheduleService

use of de.tum.in.www1.artemis.service.scheduled.quiz.QuizScheduleService in project Artemis by ls1intum.

the class QuizSubmissionService method saveSubmissionForLiveMode.

/**
 * Saves a quiz submission into the hash maps for live quizzes. Submitted quizzes are marked to be saved into the database in the QuizScheduleService
 *
 * @param exerciseId the exerciseID to the corresponding QuizExercise
 * @param quizSubmission the submission which should be saved
 * @param userLogin the login of the user who has initiated the request
 * @param submitted whether the user has pressed the submit button or not
 *
 * @return the updated quiz submission object
 * @throws QuizSubmissionException handles errors, e.g. when the live quiz has already ended, or when the quiz was already submitted before
 */
public QuizSubmission saveSubmissionForLiveMode(Long exerciseId, QuizSubmission quizSubmission, String userLogin, boolean submitted) throws QuizSubmissionException {
    // TODO: what happens if a user executes this call twice in the same moment (using 2 threads)
    String logText = submitted ? "submit quiz in live mode:" : "save quiz in live mode:";
    long start = System.nanoTime();
    checkSubmissionForLiveModeOrThrow(exerciseId, userLogin, logText, start);
    // recreate pointers back to submission in each submitted answer
    for (SubmittedAnswer submittedAnswer : quizSubmission.getSubmittedAnswers()) {
        submittedAnswer.setSubmission(quizSubmission);
    }
    // set submission date
    quizSubmission.setSubmissionDate(ZonedDateTime.now());
    // save submission to HashMap
    quizScheduleService.updateSubmission(exerciseId, userLogin, quizSubmission);
    log.info("{} Saved quiz submission for user {} in quiz {} after {} µs ", logText, userLogin, exerciseId, (System.nanoTime() - start) / 1000);
    return quizSubmission;
}
Also used : SubmittedAnswer(de.tum.in.www1.artemis.domain.quiz.SubmittedAnswer)

Aggregations

QuizExercise (de.tum.in.www1.artemis.domain.quiz.QuizExercise)5 QuizSubmission (de.tum.in.www1.artemis.domain.quiz.QuizSubmission)4 SubmittedAnswer (de.tum.in.www1.artemis.domain.quiz.SubmittedAnswer)4 QuizSubmissionException (de.tum.in.www1.artemis.exception.QuizSubmissionException)4 Result (de.tum.in.www1.artemis.domain.Result)2 AssessmentType (de.tum.in.www1.artemis.domain.enumeration.AssessmentType)2 QuizMode (de.tum.in.www1.artemis.domain.enumeration.QuizMode)2 SubmissionType (de.tum.in.www1.artemis.domain.enumeration.SubmissionType)2 Participation (de.tum.in.www1.artemis.domain.participation.Participation)2 StudentParticipation (de.tum.in.www1.artemis.domain.participation.StudentParticipation)2 QuizExerciseRepository (de.tum.in.www1.artemis.repository.QuizExerciseRepository)2 QuizSubmissionRepository (de.tum.in.www1.artemis.repository.QuizSubmissionRepository)2 ResultRepository (de.tum.in.www1.artemis.repository.ResultRepository)2 EntityNotFoundException (de.tum.in.www1.artemis.web.rest.errors.EntityNotFoundException)2 ZonedDateTime (java.time.ZonedDateTime)2 ArrayList (java.util.ArrayList)2 Optional (java.util.Optional)2 Logger (org.slf4j.Logger)2 LoggerFactory (org.slf4j.LoggerFactory)2 MessageMapping (org.springframework.messaging.handler.annotation.MessageMapping)2