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);
}
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);
}
}
}
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;
}
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;
}
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();
}
Aggregations