Search in sources :

Example 56 with StudentExam

use of de.tum.in.www1.artemis.domain.exam.StudentExam in project ArTEMiS by ls1intum.

the class ExamIntegrationTest method testGetStatsForExamAssessmentDashboard.

public void testGetStatsForExamAssessmentDashboard(int numberOfCorrectionRounds) throws Exception {
    User examTutor1 = userRepo.findOneByLogin("tutor1").get();
    User examTutor2 = userRepo.findOneByLogin("tutor2").get();
    var examVisibleDate = ZonedDateTime.now().minusMinutes(5);
    var examStartDate = ZonedDateTime.now().plusMinutes(5);
    var examEndDate = ZonedDateTime.now().plusMinutes(20);
    Course course = database.addEmptyCourse();
    Exam exam = database.addExam(course, examVisibleDate, examStartDate, examEndDate);
    exam.setNumberOfCorrectionRoundsInExam(numberOfCorrectionRounds);
    exam = examRepository.save(exam);
    exam = database.addExerciseGroupsAndExercisesToExam(exam, false);
    var stats = request.get("/api/courses/" + course.getId() + "/exams/" + exam.getId() + "/stats-for-exam-assessment-dashboard", HttpStatus.OK, StatsForDashboardDTO.class);
    assertThat(stats.getNumberOfSubmissions()).isInstanceOf(DueDateStat.class);
    assertThat(stats.getTutorLeaderboardEntries()).isInstanceOf(List.class);
    assertThat(stats.getNumberOfAssessmentsOfCorrectionRounds()).isInstanceOf(DueDateStat[].class);
    assertThat(stats.getNumberOfAssessmentLocks()).isZero();
    assertThat(stats.getNumberOfSubmissions().inTime()).isZero();
    assertThat(stats.getNumberOfAssessmentsOfCorrectionRounds()[0].inTime()).isZero();
    assertThat(stats.getTotalNumberOfAssessmentLocks()).isZero();
    var lockedSubmissions = request.get("/api/courses/" + course.getId() + "/exams/" + exam.getId() + "/lockedSubmissions", HttpStatus.OK, List.class);
    assertThat(lockedSubmissions).isEmpty();
    // register users. Instructors are ignored from scores as they are exclusive for test run exercises
    Set<User> registeredStudents = users.stream().filter(user -> !user.getLogin().contains("instructor") && !user.getLogin().contains("admin")).collect(Collectors.toSet());
    exam.setRegisteredUsers(registeredStudents);
    exam.setNumberOfExercisesInExam(exam.getExerciseGroups().size());
    exam.setRandomizeExerciseOrder(false);
    exam = examRepository.save(exam);
    exam = examRepository.findWithRegisteredUsersAndExerciseGroupsAndExercisesById(exam.getId()).get();
    // generate individual student exams
    List<StudentExam> studentExams = request.postListWithResponseBody("/api/courses/" + course.getId() + "/exams/" + exam.getId() + "/generate-student-exams", Optional.empty(), StudentExam.class, HttpStatus.OK);
    Integer noGeneratedParticipations = request.postWithResponseBody("/api/courses/" + course.getId() + "/exams/" + exam.getId() + "/student-exams/start-exercises", Optional.empty(), Integer.class, HttpStatus.OK);
    // set start and submitted date as results are created below
    studentExams.forEach(studentExam -> {
        studentExam.setStarted(true);
        studentExam.setStartedDate(ZonedDateTime.now().minusMinutes(2));
        studentExam.setSubmitted(true);
        studentExam.setSubmissionDate(ZonedDateTime.now().minusMinutes(1));
    });
    studentExamRepository.saveAll(studentExams);
    // Fetch the created participations and assign them to the exercises
    int participationCounter = 0;
    List<Exercise> exercisesInExam = exam.getExerciseGroups().stream().map(ExerciseGroup::getExercises).flatMap(Collection::stream).collect(Collectors.toList());
    for (var exercise : exercisesInExam) {
        List<StudentParticipation> participations = studentParticipationRepository.findByExerciseIdWithEagerLegalSubmissionsResult(exercise.getId());
        exercise.setStudentParticipations(new HashSet<>(participations));
        participationCounter += exercise.getStudentParticipations().size();
    }
    assertEquals(participationCounter, noGeneratedParticipations);
    // Assign submissions to the participations
    for (var exercise : exercisesInExam) {
        for (var participation : exercise.getStudentParticipations()) {
            Submission submission;
            assertThat(participation.getSubmissions()).hasSize(1);
            submission = participation.getSubmissions().iterator().next();
            submission.submitted(true);
            submission.setSubmissionDate(ZonedDateTime.now().minusMinutes(6));
            submissionRepository.save(submission);
        }
    }
    // check the stats again - check the count of submitted submissions
    stats = request.get("/api/courses/" + course.getId() + "/exams/" + exam.getId() + "/stats-for-exam-assessment-dashboard", HttpStatus.OK, StatsForDashboardDTO.class);
    assertThat(stats.getNumberOfAssessmentLocks()).isZero();
    // 75 = (15 users * 5 exercises); quiz submissions are not counted
    assertThat(stats.getNumberOfSubmissions().inTime()).isEqualTo(75L);
    assertThat(stats.getNumberOfAssessmentsOfCorrectionRounds()[0].inTime()).isZero();
    assertThat(stats.getNumberOfComplaints()).isZero();
    assertThat(stats.getTotalNumberOfAssessmentLocks()).isZero();
    // Score used for all exercise results
    Double resultScore = 75.0;
    // Lock all submissions
    for (var exercise : exercisesInExam) {
        for (var participation : exercise.getStudentParticipations()) {
            Submission submission;
            assertThat(participation.getSubmissions()).hasSize(1);
            submission = participation.getSubmissions().iterator().next();
            // Create results
            var result = new Result().score(resultScore).resultString("Good");
            if (exercise instanceof QuizExercise) {
                result.completionDate(ZonedDateTime.now().minusMinutes(4));
                result.setRated(true);
            }
            result.setAssessmentType(AssessmentType.SEMI_AUTOMATIC);
            result.setParticipation(participation);
            result.setAssessor(examTutor1);
            result = resultRepository.save(result);
            result.setSubmission(submission);
            submission.addResult(result);
            submissionRepository.save(submission);
        }
    }
    // check the stats again
    database.changeUser("tutor1");
    stats = request.get("/api/courses/" + course.getId() + "/exams/" + exam.getId() + "/stats-for-exam-assessment-dashboard", HttpStatus.OK, StatsForDashboardDTO.class);
    assertThat(stats.getNumberOfAssessmentLocks()).isEqualTo(75L);
    // 75 = (15 users * 5 exercises); quiz submissions are not counted
    assertThat(stats.getNumberOfSubmissions().inTime()).isEqualTo(75L);
    // the 15 quiz submissions are already assessed
    assertThat(stats.getNumberOfAssessmentsOfCorrectionRounds()[0].inTime()).isEqualTo(15L);
    assertThat(stats.getNumberOfComplaints()).isZero();
    assertThat(stats.getTotalNumberOfAssessmentLocks()).isEqualTo(75L);
    // test the query needed for assessment information
    database.changeUser("tutor2");
    exam.getExerciseGroups().forEach(group -> {
        var locks = group.getExercises().stream().map(exercise -> resultRepository.countNumberOfLockedAssessmentsByOtherTutorsForExamExerciseForCorrectionRounds(exercise, numberOfCorrectionRounds, examTutor2)[0].inTime()).reduce(Long::sum).get();
        if (group.getExercises().stream().anyMatch(exercise -> !(exercise instanceof QuizExercise)))
            assertThat(locks).isEqualTo(15L);
    });
    database.changeUser("instructor1");
    lockedSubmissions = request.get("/api/courses/" + course.getId() + "/exams/" + exam.getId() + "/lockedSubmissions", HttpStatus.OK, List.class);
    assertThat(lockedSubmissions).hasSize(75);
    // Finish assessment of all submissions
    for (var exercise : exercisesInExam) {
        for (var participation : exercise.getStudentParticipations()) {
            Submission submission;
            assertThat(participation.getSubmissions()).hasSize(1);
            submission = participation.getSubmissions().iterator().next();
            var result = submission.getLatestResult().completionDate(ZonedDateTime.now().minusMinutes(5));
            result.setRated(true);
            resultRepository.save(result);
        }
    }
    // check the stats again
    stats = request.get("/api/courses/" + course.getId() + "/exams/" + exam.getId() + "/stats-for-exam-assessment-dashboard", HttpStatus.OK, StatsForDashboardDTO.class);
    assertThat(stats.getNumberOfAssessmentLocks()).isZero();
    // 75 = (15 users * 5 exercises); quiz submissions are not counted
    assertThat(stats.getNumberOfSubmissions().inTime()).isEqualTo(75L);
    // 75 + the 15 quiz submissions
    assertThat(stats.getNumberOfAssessmentsOfCorrectionRounds()[0].inTime()).isEqualTo(90L);
    assertThat(stats.getNumberOfComplaints()).isZero();
    assertThat(stats.getTotalNumberOfAssessmentLocks()).isZero();
    lockedSubmissions = request.get("/api/courses/" + course.getId() + "/exams/" + exam.getId() + "/lockedSubmissions", HttpStatus.OK, List.class);
    assertThat(lockedSubmissions).isEmpty();
    if (numberOfCorrectionRounds == 2) {
        lockAndAssessForSecondCorrection(exam, course, exercisesInExam, numberOfCorrectionRounds);
    }
}
Also used : Assertions.assertThrows(org.junit.jupiter.api.Assertions.assertThrows) BeforeEach(org.junit.jupiter.api.BeforeEach) java.util(java.util) PasswordService(de.tum.in.www1.artemis.service.user.PasswordService) de.tum.in.www1.artemis.repository(de.tum.in.www1.artemis.repository) Assertions.assertThat(org.assertj.core.api.Assertions.assertThat) ZonedDateTime(java.time.ZonedDateTime) Participation(de.tum.in.www1.artemis.domain.participation.Participation) Autowired(org.springframework.beans.factory.annotation.Autowired) TextAssessmentKnowledgeService(de.tum.in.www1.artemis.service.TextAssessmentKnowledgeService) StudentExam(de.tum.in.www1.artemis.domain.exam.StudentExam) ExerciseGroup(de.tum.in.www1.artemis.domain.exam.ExerciseGroup) ExamService(de.tum.in.www1.artemis.service.exam.ExamService) ModelingSubmission(de.tum.in.www1.artemis.domain.modeling.ModelingSubmission) Assertions.assertEquals(org.junit.jupiter.api.Assertions.assertEquals) ModelFactory(de.tum.in.www1.artemis.util.ModelFactory) Path(java.nio.file.Path) IncludedInOverallScore(de.tum.in.www1.artemis.domain.enumeration.IncludedInOverallScore) Exam(de.tum.in.www1.artemis.domain.exam.Exam) DiagramType(de.tum.in.www1.artemis.domain.enumeration.DiagramType) ExamDateService(de.tum.in.www1.artemis.service.exam.ExamDateService) StudentDTO(de.tum.in.www1.artemis.service.dto.StudentDTO) Awaitility.await(org.awaitility.Awaitility.await) QuizExercise(de.tum.in.www1.artemis.domain.quiz.QuizExercise) Files(java.nio.file.Files) ExamRegistrationService(de.tum.in.www1.artemis.service.exam.ExamRegistrationService) AssessmentType(de.tum.in.www1.artemis.domain.enumeration.AssessmentType) Collectors(java.util.stream.Collectors) Test(org.junit.jupiter.api.Test) Mockito(org.mockito.Mockito) HttpStatus(org.springframework.http.HttpStatus) EntityNotFoundException(de.tum.in.www1.artemis.web.rest.errors.EntityNotFoundException) AfterEach(org.junit.jupiter.api.AfterEach) ChronoUnit(java.time.temporal.ChronoUnit) de.tum.in.www1.artemis.domain(de.tum.in.www1.artemis.domain) WithMockUser(org.springframework.security.test.context.support.WithMockUser) de.tum.in.www1.artemis.web.rest.dto(de.tum.in.www1.artemis.web.rest.dto) LdapUserDto(de.tum.in.www1.artemis.service.ldap.LdapUserDto) ZonedDateTime.now(java.time.ZonedDateTime.now) StudentParticipation(de.tum.in.www1.artemis.domain.participation.StudentParticipation) ZipFileTestUtilService(de.tum.in.www1.artemis.util.ZipFileTestUtilService) LinkedMultiValueMap(org.springframework.util.LinkedMultiValueMap) ModelingExercise(de.tum.in.www1.artemis.domain.modeling.ModelingExercise) WithMockUser(org.springframework.security.test.context.support.WithMockUser) ModelingSubmission(de.tum.in.www1.artemis.domain.modeling.ModelingSubmission) StudentExam(de.tum.in.www1.artemis.domain.exam.StudentExam) StudentParticipation(de.tum.in.www1.artemis.domain.participation.StudentParticipation) QuizExercise(de.tum.in.www1.artemis.domain.quiz.QuizExercise) QuizExercise(de.tum.in.www1.artemis.domain.quiz.QuizExercise) ModelingExercise(de.tum.in.www1.artemis.domain.modeling.ModelingExercise) ExerciseGroup(de.tum.in.www1.artemis.domain.exam.ExerciseGroup) StudentExam(de.tum.in.www1.artemis.domain.exam.StudentExam) Exam(de.tum.in.www1.artemis.domain.exam.Exam)

Example 57 with StudentExam

use of de.tum.in.www1.artemis.domain.exam.StudentExam in project Artemis by ls1intum.

the class ExerciseDeletionService method delete.

/**
 * Delete the exercise by id and all its participations.
 *
 * @param exerciseId                   the exercise to be deleted
 * @param deleteStudentReposBuildPlans whether the student repos and build plans should be deleted (can be true for programming exercises and should be false for all other exercise types)
 * @param deleteBaseReposBuildPlans    whether the template and solution repos and build plans should be deleted (can be true for programming exercises and should be false for all other exercise types)
 */
// ok
@Transactional
public void delete(long exerciseId, boolean deleteStudentReposBuildPlans, boolean deleteBaseReposBuildPlans) {
    // Delete has a transactional mechanism. Therefore, all lazy objects that are deleted below, should be fetched when needed.
    final var exercise = exerciseRepository.findByIdElseThrow(exerciseId);
    log.info("Checking if exercise {} is modeling exercise", exercise.getId());
    if (exercise instanceof ModelingExercise) {
        log.info("Deleting clusters, elements and cancel scheduled operations of exercise {}", exercise.getId());
        modelingExerciseService.deleteClustersAndElements((ModelingExercise) exercise);
        modelingExerciseService.cancelScheduledOperations(exerciseId);
    }
    participantScoreRepository.deleteAllByExerciseIdTransactional(exerciseId);
    // delete all exercise units linking to the exercise
    List<ExerciseUnit> exerciseUnits = this.exerciseUnitRepository.findByIdWithLearningGoalsBidirectional(exerciseId);
    for (ExerciseUnit exerciseUnit : exerciseUnits) {
        this.lectureUnitService.removeLectureUnit(exerciseUnit);
    }
    // delete all plagiarism results belonging to this exercise
    plagiarismResultRepository.deletePlagiarismResultsByExerciseId(exerciseId);
    // delete all participations belonging to this exercise, this will also delete submissions, results, feedback, complaints, etc.
    participationService.deleteAllByExerciseId(exercise.getId(), deleteStudentReposBuildPlans, deleteStudentReposBuildPlans);
    // clean up the many-to-many relationship to avoid problems when deleting the entities but not the relationship table
    // to avoid a ConcurrentModificationException, we need to use a copy of the set
    var exampleSubmissions = new HashSet<>(exercise.getExampleSubmissions());
    for (ExampleSubmission exampleSubmission : exampleSubmissions) {
        exampleSubmissionService.deleteById(exampleSubmission.getId());
    }
    // make sure tutor participations are deleted before the exercise is deleted
    tutorParticipationRepository.deleteAllByAssessedExerciseId(exercise.getId());
    if (exercise.isExamExercise()) {
        Exam exam = examRepository.findOneWithEagerExercisesGroupsAndStudentExams(exercise.getExerciseGroup().getExam().getId());
        for (StudentExam studentExam : exam.getStudentExams()) {
            if (studentExam.getExercises().contains(exercise)) {
                // remove exercise reference from student exam
                List<Exercise> exerciseList = studentExam.getExercises();
                exerciseList.remove(exercise);
                studentExam.setExercises(exerciseList);
                studentExamRepository.save(studentExam);
            }
        }
    }
    // Programming exercises have some special stuff that needs to be cleaned up (solution/template participation, build plans, etc.).
    if (exercise instanceof ProgrammingExercise) {
        // TODO: delete all schedules related to this programming exercise
        programmingExerciseService.delete(exercise.getId(), deleteBaseReposBuildPlans);
    } else {
        // delete text assessment knowledge if exercise is of type TextExercise and if no other exercise uses same knowledge
        if (exercise instanceof TextExercise) {
            // explicitly load the text exercise as such so that the knowledge is eagerly loaded as well
            TextExercise textExercise = textExerciseRepository.findByIdElseThrow(exercise.getId());
            if (textExercise.getKnowledge() != null) {
                textAssessmentKnowledgeService.deleteKnowledge(textExercise.getKnowledge().getId(), textExercise.getId());
            }
        } else // delete model assessment knowledge if exercise is of type ModelExercise and if no other exercise uses same knowledge
        if (exercise instanceof ModelingExercise) {
            // explicitly load the modeling exercise as such so that the knowledge is eagerly loaded as well
            ModelingExercise modelingExercise = modelingExerciseRepository.findByIdElseThrow(exercise.getId());
            if (modelingExercise.getKnowledge() != null) {
                modelAssessmentKnowledgeService.deleteKnowledge(modelingExercise.getKnowledge().getId(), modelingExercise.getId());
            }
        }
        exerciseRepository.delete(exercise);
    }
}
Also used : ExerciseUnit(de.tum.in.www1.artemis.domain.lecture.ExerciseUnit) QuizExercise(de.tum.in.www1.artemis.domain.quiz.QuizExercise) ProgrammingExercise(de.tum.in.www1.artemis.domain.ProgrammingExercise) TextExercise(de.tum.in.www1.artemis.domain.TextExercise) Exercise(de.tum.in.www1.artemis.domain.Exercise) ModelingExercise(de.tum.in.www1.artemis.domain.modeling.ModelingExercise) ExampleSubmission(de.tum.in.www1.artemis.domain.ExampleSubmission) ModelingExercise(de.tum.in.www1.artemis.domain.modeling.ModelingExercise) ProgrammingExercise(de.tum.in.www1.artemis.domain.ProgrammingExercise) TextExercise(de.tum.in.www1.artemis.domain.TextExercise) StudentExam(de.tum.in.www1.artemis.domain.exam.StudentExam) StudentExam(de.tum.in.www1.artemis.domain.exam.StudentExam) Exam(de.tum.in.www1.artemis.domain.exam.Exam) HashSet(java.util.HashSet) Transactional(org.springframework.transaction.annotation.Transactional)

Example 58 with StudentExam

use of de.tum.in.www1.artemis.domain.exam.StudentExam in project Artemis by ls1intum.

the class ExamRegistrationService method unregisterAllStudentFromExam.

/**
 * Unregisters all students from the exam
 *
 * @param examId the exam for which a student should be unregistered
 * @param deleteParticipationsAndSubmission whether the participations and submissions of the student should be deleted
 */
public void unregisterAllStudentFromExam(Long examId, boolean deleteParticipationsAndSubmission) {
    var exam = examRepository.findWithRegisteredUsersById(examId).orElseThrow(() -> new EntityNotFoundException("Exam", examId));
    // remove all registered students
    List<Long> userIds = new ArrayList<>();
    exam.getRegisteredUsers().forEach(user -> userIds.add(user.getId()));
    List<User> registeredStudentsList = userRepository.findAllById(userIds);
    registeredStudentsList.forEach(exam::removeRegisteredUser);
    examRepository.save(exam);
    // remove all students exams
    Set<StudentExam> studentExams = studentExamRepository.findAllWithExercisesByExamId(examId);
    studentExams.forEach(studentExam -> removeStudentExam(studentExam, deleteParticipationsAndSubmission));
    User currentUser = userRepository.getUserWithGroupsAndAuthorities();
    AuditEvent auditEvent = new AuditEvent(currentUser.getLogin(), Constants.REMOVE_ALL_USERS_FROM_EXAM, "exam=" + exam.getTitle());
    auditEventRepository.add(auditEvent);
    log.info("User {} has removed all users from the exam {} with id {}. This also deleted potentially existing student exams with all its participations and submissions.", currentUser.getLogin(), exam.getTitle(), exam.getId());
}
Also used : User(de.tum.in.www1.artemis.domain.User) AuditEvent(org.springframework.boot.actuate.audit.AuditEvent) EntityNotFoundException(de.tum.in.www1.artemis.web.rest.errors.EntityNotFoundException) StudentExam(de.tum.in.www1.artemis.domain.exam.StudentExam)

Example 59 with StudentExam

use of de.tum.in.www1.artemis.domain.exam.StudentExam in project Artemis by ls1intum.

the class ExamService method calculateExamScores.

/**
 * Puts students, result and exerciseGroups together for ExamScoresDTO
 *
 * @param examId the id of the exam
 * @return return ExamScoresDTO with students, scores and exerciseGroups for exam
 */
public ExamScoresDTO calculateExamScores(Long examId) {
    Exam exam = examRepository.findWithExerciseGroupsAndExercisesById(examId).orElseThrow(() -> new EntityNotFoundException("Exam", examId));
    // without test run participations
    List<StudentParticipation> studentParticipations = studentParticipationRepository.findByExamIdWithSubmissionRelevantResult(examId);
    // Adding exam information to DTO
    ExamScoresDTO scores = new ExamScoresDTO(exam.getId(), exam.getTitle(), exam.getMaxPoints());
    // setting multiplicity of correction rounds
    scores.hasSecondCorrectionAndStarted = false;
    // Counts how many participants each exercise has
    Map<Long, Long> exerciseIdToNumberParticipations = studentParticipations.stream().collect(Collectors.groupingBy(studentParticipation -> studentParticipation.getExercise().getId(), Collectors.counting()));
    // Adding exercise group information to DTO
    for (ExerciseGroup exerciseGroup : exam.getExerciseGroups()) {
        // Find the maximum points for this exercise group
        OptionalDouble optionalMaxPointsGroup = exerciseGroup.getExercises().stream().mapToDouble(Exercise::getMaxPoints).max();
        Double maxPointsGroup = optionalMaxPointsGroup.orElse(0);
        // Counter for exerciseGroup participations. Is calculated by summing up the number of exercise participations
        long numberOfExerciseGroupParticipants = 0;
        // Add information about exercise groups and exercises
        var exerciseGroupDTO = new ExamScoresDTO.ExerciseGroup(exerciseGroup.getId(), exerciseGroup.getTitle(), maxPointsGroup);
        for (Exercise exercise : exerciseGroup.getExercises()) {
            Long participantsForExercise = exerciseIdToNumberParticipations.get(exercise.getId());
            // If no participation exists for an exercise then no entry exists in the map
            if (participantsForExercise == null) {
                participantsForExercise = 0L;
            }
            numberOfExerciseGroupParticipants += participantsForExercise;
            exerciseGroupDTO.containedExercises.add(new ExamScoresDTO.ExerciseGroup.ExerciseInfo(exercise.getId(), exercise.getTitle(), exercise.getMaxPoints(), participantsForExercise, exercise.getClass().getSimpleName()));
        }
        exerciseGroupDTO.numberOfParticipants = numberOfExerciseGroupParticipants;
        scores.exerciseGroups.add(exerciseGroupDTO);
    }
    // Adding registered student information to DTO
    // fetched without test runs
    Set<StudentExam> studentExams = studentExamRepository.findByExamId(examId);
    ObjectMapper objectMapper = new ObjectMapper();
    for (StudentExam studentExam : studentExams) {
        User user = studentExam.getUser();
        var studentResult = new ExamScoresDTO.StudentResult(user.getId(), user.getName(), user.getEmail(), user.getLogin(), user.getRegistrationNumber(), studentExam.isSubmitted());
        // Adding student results information to DTO
        List<StudentParticipation> participationsOfStudent = studentParticipations.stream().filter(studentParticipation -> studentParticipation.getStudent().get().getId().equals(studentResult.userId)).toList();
        studentResult.overallPointsAchieved = 0.0;
        studentResult.overallPointsAchievedInFirstCorrection = 0.0;
        for (StudentParticipation studentParticipation : participationsOfStudent) {
            Exercise exercise = studentParticipation.getExercise();
            // Relevant Result is already calculated
            if (studentParticipation.getResults() != null && !studentParticipation.getResults().isEmpty()) {
                Result relevantResult = studentParticipation.getResults().iterator().next();
                // Note: It is important that we round on the individual exercise level first and then sum up.
                // This is necessary so that the student arrives at the same overall result when doing his own recalculation.
                // Let's assume that the student achieved 1.05 points in each of 5 exercises.
                // In the client, these are now displayed rounded as 1.1 points.
                // If the student adds up the displayed points, he gets a total of 5.5 points.
                // In order to get the same total result as the student, we have to round before summing.
                double achievedPoints = roundScoreSpecifiedByCourseSettings(relevantResult.getScore() / 100.0 * exercise.getMaxPoints(), exam.getCourse());
                // points earned in NOT_INCLUDED exercises do not count towards the students result in the exam
                if (!exercise.getIncludedInOverallScore().equals(IncludedInOverallScore.NOT_INCLUDED)) {
                    studentResult.overallPointsAchieved += achievedPoints;
                }
                // collect points of first correction, if a second correction exists
                if (exam.getNumberOfCorrectionRoundsInExam() == 2 && !exercise.getIncludedInOverallScore().equals(IncludedInOverallScore.NOT_INCLUDED)) {
                    Optional<Submission> latestSubmission = studentParticipation.findLatestSubmission();
                    if (latestSubmission.isPresent()) {
                        Submission submission = latestSubmission.get();
                        // Check if second correction already started
                        if (submission.getManualResults().size() > 1) {
                            if (!scores.hasSecondCorrectionAndStarted) {
                                scores.hasSecondCorrectionAndStarted = true;
                            }
                            Result firstManualResult = submission.getFirstManualResult();
                            double achievedPointsInFirstCorrection = 0.0;
                            if (firstManualResult != null) {
                                Double resultScore = firstManualResult.getScore();
                                achievedPointsInFirstCorrection = resultScore != null ? roundScoreSpecifiedByCourseSettings(resultScore / 100.0 * exercise.getMaxPoints(), exam.getCourse()) : 0.0;
                            }
                            studentResult.overallPointsAchievedInFirstCorrection += achievedPointsInFirstCorrection;
                        }
                    }
                }
                // Check whether the student attempted to solve the exercise
                boolean hasNonEmptySubmission = hasNonEmptySubmission(studentParticipation.getSubmissions(), exercise, objectMapper);
                studentResult.exerciseGroupIdToExerciseResult.put(exercise.getExerciseGroup().getId(), new ExamScoresDTO.ExerciseResult(exercise.getId(), exercise.getTitle(), exercise.getMaxPoints(), relevantResult.getScore(), achievedPoints, hasNonEmptySubmission));
            }
        }
        if (scores.maxPoints != null) {
            studentResult.overallScoreAchieved = (studentResult.overallPointsAchieved / scores.maxPoints) * 100.0;
            var overallScoreAchievedInFirstCorrection = (studentResult.overallPointsAchievedInFirstCorrection / scores.maxPoints) * 100.0;
            // Sets grading scale related properties for exam scores
            Optional<GradingScale> gradingScale = gradingScaleRepository.findByExamId(examId);
            if (gradingScale.isPresent()) {
                // Calculate current student grade
                GradeStep studentGrade = gradingScaleRepository.matchPercentageToGradeStep(studentResult.overallScoreAchieved, gradingScale.get().getId());
                GradeStep studentGradeInFirstCorrection = gradingScaleRepository.matchPercentageToGradeStep(overallScoreAchievedInFirstCorrection, gradingScale.get().getId());
                studentResult.overallGrade = studentGrade.getGradeName();
                studentResult.overallGradeInFirstCorrection = studentGradeInFirstCorrection.getGradeName();
                studentResult.hasPassed = studentGrade.getIsPassingGrade();
            }
        }
        scores.studentResults.add(studentResult);
    }
    // Updating exam information in DTO
    double sumOverallPoints = scores.studentResults.stream().mapToDouble(studentResult -> studentResult.overallPointsAchieved).sum();
    int numberOfStudentResults = scores.studentResults.size();
    if (numberOfStudentResults != 0) {
        scores.averagePointsAchieved = sumOverallPoints / numberOfStudentResults;
    }
    return scores;
}
Also used : Async(org.springframework.scheduling.annotation.Async) java.util(java.util) SecurityUtils(de.tum.in.www1.artemis.security.SecurityUtils) de.tum.in.www1.artemis.repository(de.tum.in.www1.artemis.repository) TimeLogUtil(de.tum.in.www1.artemis.service.util.TimeLogUtil) BadRequestAlertException(de.tum.in.www1.artemis.web.rest.errors.BadRequestAlertException) ZonedDateTime(java.time.ZonedDateTime) LoggerFactory(org.slf4j.LoggerFactory) GitService(de.tum.in.www1.artemis.service.connectors.GitService) StudentExam(de.tum.in.www1.artemis.domain.exam.StudentExam) InstanceMessageSendService(de.tum.in.www1.artemis.service.messaging.InstanceMessageSendService) RoundingUtil.roundScoreSpecifiedByCourseSettings(de.tum.in.www1.artemis.service.util.RoundingUtil.roundScoreSpecifiedByCourseSettings) Value(org.springframework.beans.factory.annotation.Value) ExerciseGroup(de.tum.in.www1.artemis.domain.exam.ExerciseGroup) AuditEvent(org.springframework.boot.actuate.audit.AuditEvent) Service(org.springframework.stereotype.Service) ModelingSubmission(de.tum.in.www1.artemis.domain.modeling.ModelingSubmission) GroupNotificationService(de.tum.in.www1.artemis.service.notifications.GroupNotificationService) Path(java.nio.file.Path) Exam(de.tum.in.www1.artemis.domain.exam.Exam) de.tum.in.www1.artemis.service(de.tum.in.www1.artemis.service) Logger(org.slf4j.Logger) QuizExercise(de.tum.in.www1.artemis.domain.quiz.QuizExercise) AuditEventRepository(org.springframework.boot.actuate.audit.AuditEventRepository) Files(java.nio.file.Files) GitAPIException(org.eclipse.jgit.api.errors.GitAPIException) QuizSubmission(de.tum.in.www1.artemis.domain.quiz.QuizSubmission) ObjectMapper(com.fasterxml.jackson.databind.ObjectMapper) IOException(java.io.IOException) NotNull(javax.validation.constraints.NotNull) Collectors(java.util.stream.Collectors) Constants(de.tum.in.www1.artemis.config.Constants) EntityNotFoundException(de.tum.in.www1.artemis.web.rest.errors.EntityNotFoundException) de.tum.in.www1.artemis.domain(de.tum.in.www1.artemis.domain) de.tum.in.www1.artemis.web.rest.dto(de.tum.in.www1.artemis.web.rest.dto) StudentParticipation(de.tum.in.www1.artemis.domain.participation.StudentParticipation) ModelingExercise(de.tum.in.www1.artemis.domain.modeling.ModelingExercise) de.tum.in.www1.artemis.domain.enumeration(de.tum.in.www1.artemis.domain.enumeration) StudentParticipation(de.tum.in.www1.artemis.domain.participation.StudentParticipation) QuizExercise(de.tum.in.www1.artemis.domain.quiz.QuizExercise) ModelingExercise(de.tum.in.www1.artemis.domain.modeling.ModelingExercise) ExerciseGroup(de.tum.in.www1.artemis.domain.exam.ExerciseGroup) StudentExam(de.tum.in.www1.artemis.domain.exam.StudentExam) Exam(de.tum.in.www1.artemis.domain.exam.Exam) ObjectMapper(com.fasterxml.jackson.databind.ObjectMapper) ModelingSubmission(de.tum.in.www1.artemis.domain.modeling.ModelingSubmission) QuizSubmission(de.tum.in.www1.artemis.domain.quiz.QuizSubmission) EntityNotFoundException(de.tum.in.www1.artemis.web.rest.errors.EntityNotFoundException) StudentExam(de.tum.in.www1.artemis.domain.exam.StudentExam)

Example 60 with StudentExam

use of de.tum.in.www1.artemis.domain.exam.StudentExam in project Artemis by ls1intum.

the class ExamResource method generateMissingStudentExams.

/**
 * POST /courses/:courseId/exams/:examId/generate-missing-student-exams:
 * Generates exams for students, who don't have an individual exam yet.
 * They are created randomly based on the exam configuration and the exercise groups.
 *
 * @param courseId the id of the course
 * @param examId   the id of the exam
 * @return the list of student exams with their corresponding users
 */
@PostMapping(value = "/courses/{courseId}/exams/{examId}/generate-missing-student-exams")
@PreAuthorize("hasRole('INSTRUCTOR')")
public ResponseEntity<List<StudentExam>> generateMissingStudentExams(@PathVariable Long courseId, @PathVariable Long examId) {
    log.info("REST request to generate missing student exams for exam {}", examId);
    final Exam exam = examRepository.findByIdWithRegisteredUsersExerciseGroupsAndExercisesElseThrow(examId);
    examAccessService.checkCourseAndExamAccessForInstructorElseThrow(courseId, examId);
    // Validate settings of the exam
    examService.validateForStudentExamGeneration(exam);
    List<StudentExam> studentExams = studentExamRepository.generateMissingStudentExams(exam);
    // we need to break a cycle for the serialization
    for (StudentExam studentExam : studentExams) {
        studentExam.getExam().setRegisteredUsers(null);
        studentExam.getExam().setExerciseGroups(null);
        studentExam.getExam().setStudentExams(null);
    }
    // Reschedule after creation (possible longer working time)
    examMonitoringScheduleService.scheduleExamActivitySave(examId);
    log.info("Generated {} missing student exams for exam {}", studentExams.size(), examId);
    return ResponseEntity.ok().body(studentExams);
}
Also used : StudentExam(de.tum.in.www1.artemis.domain.exam.StudentExam) StudentExam(de.tum.in.www1.artemis.domain.exam.StudentExam) Exam(de.tum.in.www1.artemis.domain.exam.Exam) PreAuthorize(org.springframework.security.access.prepost.PreAuthorize)

Aggregations

StudentExam (de.tum.in.www1.artemis.domain.exam.StudentExam)69 WithMockUser (org.springframework.security.test.context.support.WithMockUser)44 Test (org.junit.jupiter.api.Test)42 Exam (de.tum.in.www1.artemis.domain.exam.Exam)37 StudentParticipation (de.tum.in.www1.artemis.domain.participation.StudentParticipation)22 ModelingExercise (de.tum.in.www1.artemis.domain.modeling.ModelingExercise)20 QuizExercise (de.tum.in.www1.artemis.domain.quiz.QuizExercise)18 EntityNotFoundException (de.tum.in.www1.artemis.web.rest.errors.EntityNotFoundException)18 AbstractSpringIntegrationBambooBitbucketJiraTest (de.tum.in.www1.artemis.AbstractSpringIntegrationBambooBitbucketJiraTest)16 ModelingSubmission (de.tum.in.www1.artemis.domain.modeling.ModelingSubmission)14 ZonedDateTime (java.time.ZonedDateTime)14 ExerciseGroup (de.tum.in.www1.artemis.domain.exam.ExerciseGroup)12 Participation (de.tum.in.www1.artemis.domain.participation.Participation)12 de.tum.in.www1.artemis.repository (de.tum.in.www1.artemis.repository)12 java.util (java.util)12 Collectors (java.util.stream.Collectors)12 LinkedMultiValueMap (org.springframework.util.LinkedMultiValueMap)12 de.tum.in.www1.artemis.domain (de.tum.in.www1.artemis.domain)10 AssessmentType (de.tum.in.www1.artemis.domain.enumeration.AssessmentType)10 de.tum.in.www1.artemis.web.rest.dto (de.tum.in.www1.artemis.web.rest.dto)10