From bda8f19eab8ed88185355b79fca6e429bb915d95 Mon Sep 17 00:00:00 2001 From: Konstantin <47260935+KonstiAnon@users.noreply.github.com> Date: Tue, 26 Nov 2024 00:07:14 +0100 Subject: [PATCH] Quiz exercises: Fix an issue with the evaluation when practice mode submissions are available (#9821) --- .../quiz/service/QuizResultService.java | 63 +++++++++++-------- 1 file changed, 36 insertions(+), 27 deletions(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/quiz/service/QuizResultService.java b/src/main/java/de/tum/cit/aet/artemis/quiz/service/QuizResultService.java index b540fdc5f9bd..4d461ceb1a12 100644 --- a/src/main/java/de/tum/cit/aet/artemis/quiz/service/QuizResultService.java +++ b/src/main/java/de/tum/cit/aet/artemis/quiz/service/QuizResultService.java @@ -6,6 +6,7 @@ import java.util.Comparator; import java.util.HashSet; import java.util.List; +import java.util.Optional; import java.util.Set; import jakarta.validation.constraints.NotNull; @@ -88,9 +89,12 @@ public void evaluateQuizAndUpdateStatistics(@NotNull Long quizExerciseId) { * Evaluate the given quiz exercise by performing the following actions for each participation: * 1. Get the submission for each participation (there should be only one as in exam mode, the submission gets created upfront and will be updated) * - If no submission is found, print a warning and continue as we cannot evaluate that submission - * - If more than one submission is found, select one of them + * - Filter out submissions that are not submitted before the quiz deadline (practice mode) + * - If more than one submission is found, select one with the highest ID * 2. mark submission and participation as evaluated * 3. Create a new result for the selected submission and calculate scores + * - If a rated result already exists, skip the evaluation + * - If no rated result exists, create a new one and evaluate the submission * 4. Save the updated submission & participation and the newly created result *

* After processing all participations, the created results will be returned for further processing @@ -104,6 +108,7 @@ private Set evaluateSubmissions(@NotNull QuizExercise quizExercise) { Set createdResults = new HashSet<>(); List studentParticipations = studentParticipationRepository.findAllWithEagerLegalSubmissionsAndEagerResultsByExerciseId(quizExercise.getId()); submittedAnswerRepository.loadQuizSubmissionsSubmittedAnswers(studentParticipations); + ZonedDateTime quizDeadline = quizExercise.getDueDate(); for (var participation : studentParticipations) { if (participation.isTestRun()) { @@ -122,8 +127,19 @@ private Set evaluateSubmissions(@NotNull QuizExercise quizExercise) { else if (submissions.size() > 1) { log.warn("Found multiple ({}) submissions for participation {} (Participant {}) in quiz {}, taking the one with highest id", submissions.size(), participation.getId(), participation.getParticipant().getName(), quizExercise.getId()); - // Load submission with highest id - quizSubmission = (QuizSubmission) submissions.stream().max(Comparator.comparing(Submission::getId)).get(); + // Filter submissions to only include those submitted before the quiz deadline if the due date is not null, otherwise select the one with the highest ID + Optional validSubmission = submissions.stream() + .filter(submission -> quizExercise.getDueDate() == null + || (submission.getSubmissionDate() != null && !submission.getSubmissionDate().isAfter(quizExercise.getDueDate()))) + .max(Comparator.comparing(Submission::getId)); + if (validSubmission.isPresent()) { + quizSubmission = (QuizSubmission) validSubmission.get(); + } + else { + log.warn("No valid submissions found for participation {} (Participant {}) in quiz {}", participation.getId(), participation.getParticipant().getName(), + quizExercise.getId()); + continue; + } } else { quizSubmission = (QuizSubmission) submissions.iterator().next(); @@ -131,48 +147,41 @@ else if (submissions.size() > 1) { participation.setInitializationState(InitializationState.FINISHED); - boolean resultExisting = false; - // create new result if none is existing - Result result; - if (participation.getResults().isEmpty()) { - result = new Result().participation(participation); + Optional existingRatedResult = participation.getResults().stream().filter(result -> Boolean.TRUE.equals(result.isRated())).findFirst(); + + if (existingRatedResult.isPresent()) { + // A rated result already exists; no need to create a new one + log.debug("A rated result already exists for participation {} (Participant {}), skipping evaluation.", participation.getId(), + participation.getParticipant().getName()); } else { - resultExisting = true; - result = participation.getResults().iterator().next(); - } - // Only create Results once after the first evaluation - if (!resultExisting) { - // delete result from quizSubmission, to be able to set a new one - if (quizSubmission.getLatestResult() != null) { - resultService.deleteResult(quizSubmission.getLatestResult(), true); - } - result.setRated(true); - result.setAssessmentType(AssessmentType.AUTOMATIC); - result.setCompletionDate(ZonedDateTime.now()); + // No rated result exists; create a new one + Result result = new Result().participation(participation).rated(true).assessmentType(AssessmentType.AUTOMATIC).completionDate(ZonedDateTime.now()); - // set submission to calculate scores + // Associate submission with result result.setSubmission(quizSubmission); - // calculate scores and update result and submission accordingly + + // Calculate and update scores quizSubmission.calculateAndUpdateScores(quizExercise.getQuizQuestions()); result.evaluateQuizSubmission(quizExercise); - // remove submission to follow save order for ordered collections + + // Detach submission to maintain proper save order result.setSubmission(null); - // NOTE: we save participation, submission and result here individually so that one exception (e.g. duplicated key) cannot destroy multiple student answers + // Save entities individually submissionRepository.save(quizSubmission); result = resultRepository.save(result); - // add result to participation + // Update participation with new result participation.addResult(result); studentParticipationRepository.save(participation); - // add result to submission + // Re-associate result with submission and save result.setSubmission(quizSubmission); quizSubmission.addResult(result); submissionRepository.save(quizSubmission); - // Add result so that it can be returned (and processed later) + // Add result to the set of created results createdResults.add(result); } }