Search in sources :

Example 46 with StudentExam

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

the class ExamSubmissionServiceTest method init.

@BeforeEach
void init() {
    List<User> users = database.addUsers(1, 0, 0, 1);
    user = users.get(0);
    exercise = database.addCourseExamExerciseGroupWithOneTextExercise();
    Course course = exercise.getCourseViaExerciseGroupOrCourseMember();
    exam = examRepository.findByCourseId(course.getId()).get(0);
    studentExam = database.addStudentExam(exam);
    // 2 hours
    studentExam.setWorkingTime(7200);
    studentExam.setUser(user);
    studentExam.addExercise(exercise);
    studentExam = studentExamRepository.save(studentExam);
}
Also used : User(de.tum.in.www1.artemis.domain.User) WithMockUser(org.springframework.security.test.context.support.WithMockUser) Course(de.tum.in.www1.artemis.domain.Course) BeforeEach(org.junit.jupiter.api.BeforeEach)

Example 47 with StudentExam

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

the class ExamQuizService method evaluateQuizParticipationsForTestRun.

/**
 * This method is intended to be called after a user submits a test run. We calculate the achieved score in the quiz exercises immediately and attach a result.
 * Note: We do not insert the result of this test run quiz participation into the quiz statistics.
 * @param testRun The test run containing the users participations in all exam exercises
 */
public void evaluateQuizParticipationsForTestRun(StudentExam testRun) {
    final var participations = testRun.getExercises().stream().flatMap(exercise -> exercise.getStudentParticipations().stream().filter(StudentParticipation::isTestRun).filter(participation -> participation.getExercise() instanceof QuizExercise)).collect(Collectors.toSet());
    for (final var participation : participations) {
        var quizExercise = (QuizExercise) participation.getExercise();
        final var optionalExistingSubmission = participation.findLatestSubmission();
        if (optionalExistingSubmission.isPresent()) {
            QuizSubmission submission = (QuizSubmission) submissionRepository.findWithEagerResultAndFeedbackById(optionalExistingSubmission.get().getId()).orElseThrow(() -> new EntityNotFoundException("Submission with id \"" + optionalExistingSubmission.get().getId() + "\" does not exist"));
            participation.setExercise(quizExerciseRepository.findByIdWithQuestionsElseThrow(quizExercise.getId()));
            quizExercise = (QuizExercise) participation.getExercise();
            Result result;
            if (submission.getLatestResult() == null) {
                result = new Result();
                result.setParticipation(participation);
                result.setAssessmentType(AssessmentType.AUTOMATIC);
                // set submission to calculate scores
                result.setSubmission(submission);
                // calculate scores and update result and submission accordingly
                submission.calculateAndUpdateScores(quizExercise);
                result.evaluateQuizSubmission();
                // remove submission to follow save order for ordered collections
                result.setSubmission(null);
                result = resultRepository.save(result);
                participation.setResults(Set.of(result));
                studentParticipationRepository.save(participation);
                result.setSubmission(submission);
                submission.addResult(result);
            } else {
                result = submission.getLatestResult();
                // set submission to calculate scores
                result.setSubmission(submission);
                // calculate scores and update result and submission accordingly
                submission.calculateAndUpdateScores(quizExercise);
                // prevent a lazy exception in the evaluateQuizSubmission method
                result.setParticipation(participation);
                result.evaluateQuizSubmission();
                resultRepository.save(result);
            }
            submissionRepository.save(submission);
        }
    }
}
Also used : QuizStatisticService(de.tum.in.www1.artemis.service.QuizStatisticService) java.util(java.util) Logger(org.slf4j.Logger) QuizExercise(de.tum.in.www1.artemis.domain.quiz.QuizExercise) QuizSubmission(de.tum.in.www1.artemis.domain.quiz.QuizSubmission) de.tum.in.www1.artemis.repository(de.tum.in.www1.artemis.repository) TimeLogUtil(de.tum.in.www1.artemis.service.util.TimeLogUtil) ZonedDateTime(java.time.ZonedDateTime) LoggerFactory(org.slf4j.LoggerFactory) Submission(de.tum.in.www1.artemis.domain.Submission) StudentExam(de.tum.in.www1.artemis.domain.exam.StudentExam) NotNull(javax.validation.constraints.NotNull) AssessmentType(de.tum.in.www1.artemis.domain.enumeration.AssessmentType) InitializationState(de.tum.in.www1.artemis.domain.enumeration.InitializationState) Collectors(java.util.stream.Collectors) ResultService(de.tum.in.www1.artemis.service.ResultService) Result(de.tum.in.www1.artemis.domain.Result) EntityNotFoundException(de.tum.in.www1.artemis.web.rest.errors.EntityNotFoundException) Service(org.springframework.stereotype.Service) StudentParticipation(de.tum.in.www1.artemis.domain.participation.StudentParticipation) QuizSubmission(de.tum.in.www1.artemis.domain.quiz.QuizSubmission) EntityNotFoundException(de.tum.in.www1.artemis.web.rest.errors.EntityNotFoundException) QuizExercise(de.tum.in.www1.artemis.domain.quiz.QuizExercise) Result(de.tum.in.www1.artemis.domain.Result)

Example 48 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 49 with StudentExam

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

the class ExamSubmissionService method isAllowedToSubmitDuringExam.

/**
 * Check if the user is allowed to submit (submission is in time & user's student exam has the exercise or it is a test run).
 * Note: if the exercise is not an exam, this method will return true
 *
 * @param exercise  the exercise for which a submission should be saved
 * @param user      the user that wants to submit
 * @param withGracePeriod whether the grace period should be taken into account or not
 * @return true if it is not an exam of if it is an exam and the submission is in time and the exercise is part of
 *         the user's student exam
 */
public boolean isAllowedToSubmitDuringExam(Exercise exercise, User user, boolean withGracePeriod) {
    if (isExamSubmission(exercise)) {
        // Get the student exam if it was not passed to the function
        Exam exam = exercise.getExerciseGroup().getExam();
        Optional<StudentExam> optionalStudentExam = studentExamRepository.findWithExercisesByUserIdAndExamId(user.getId(), exam.getId());
        if (optionalStudentExam.isEmpty()) {
            // unnecessary database calls
            if (!isExamTestRunSubmission(exercise, user, exam)) {
                throw new EntityNotFoundException("Student exam with for userId \"" + user.getId() + "\" and examId \"" + exam.getId() + "\" does not exist");
            }
            return true;
        }
        StudentExam studentExam = optionalStudentExam.get();
        // Check that the current user is allowed to submit to this exercise
        if (!studentExam.getExercises().contains(exercise)) {
            return false;
        }
        // if the student exam was already submitted, the user cannot save anymore
        if (Boolean.TRUE.equals(studentExam.isSubmitted()) || studentExam.getSubmissionDate() != null) {
            return false;
        }
        // Check that the submission is in time
        return isSubmissionInTime(exercise, studentExam, withGracePeriod);
    }
    return true;
}
Also used : EntityNotFoundException(de.tum.in.www1.artemis.web.rest.errors.EntityNotFoundException) 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)

Example 50 with StudentExam

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

the class RepositoryIntegrationTest method testResetNotAllowedForExamBeforeDueDate.

@Test
@WithMockUser(username = "tutor1", roles = "TA")
public void testResetNotAllowedForExamBeforeDueDate() throws Exception {
    // Create an exam programming exercise
    programmingExercise = database.addCourseExamExerciseGroupWithOneProgrammingExerciseAndTestCases();
    programmingExerciseRepository.save(programmingExercise);
    participation.setExercise(programmingExercise);
    studentParticipationRepository.save(participation);
    // Create an exam which has already started
    Exam exam = examRepository.findByIdElseThrow(programmingExercise.getExerciseGroup().getExam().getId());
    exam.setStartDate(ZonedDateTime.now().minusHours(1));
    examRepository.save(exam);
    var studentExam = database.addStudentExam(exam);
    // 2 hours
    studentExam.setWorkingTime(7200);
    studentExam.setUser(participation.getStudent().get());
    studentExam.addExercise(programmingExercise);
    studentExamRepository.save(studentExam);
    // A tutor is not allowed to reset the repository during the exam time
    assertUnchangedRepositoryStatusForForbiddenReset();
}
Also used : Exam(de.tum.in.www1.artemis.domain.exam.Exam) WithMockUser(org.springframework.security.test.context.support.WithMockUser) AbstractSpringIntegrationBambooBitbucketJiraTest(de.tum.in.www1.artemis.AbstractSpringIntegrationBambooBitbucketJiraTest) Test(org.junit.jupiter.api.Test)

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