use of de.tum.in.www1.artemis.domain.participation.Participant in project ArTEMiS by ls1intum.
the class ExamIntegrationTest method testGetExamScore.
@Test
@WithMockUser(username = "instructor1", roles = "INSTRUCTOR")
public void testGetExamScore() throws Exception {
// TODO avoid duplicated code with StudentExamIntegrationTest
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);
// TODO: it would be nice if we can support programming exercises here as well
exam = database.addExerciseGroupsAndExercisesToExam(exam, false);
// 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);
assertThat(studentExams).hasSize(exam.getRegisteredUsers().size());
assertThat(studentExamRepository.findAll()).hasSize(registeredStudents.size());
// start exercises
Integer noGeneratedParticipations = request.postWithResponseBody("/api/courses/" + course.getId() + "/exams/" + exam.getId() + "/student-exams/start-exercises", Optional.empty(), Integer.class, HttpStatus.OK);
assertThat(noGeneratedParticipations).isEqualTo(registeredStudents.size() * exam.getExerciseGroups().size());
// explicitly set the user again to prevent issues in the following server call due to the use of SecurityUtils.setAuthorizationObject();
database.changeUser("instructor1");
// instructor exam checklist checks
ExamChecklistDTO examChecklistDTO = examService.getStatsForChecklist(exam, true);
assertThat(examChecklistDTO).isNotNull();
assertThat(examChecklistDTO.getNumberOfGeneratedStudentExams()).isEqualTo(15L);
assertThat(examChecklistDTO.getAllExamExercisesAllStudentsPrepared()).isTrue();
assertThat(examChecklistDTO.getNumberOfTotalParticipationsForAssessment()).isZero();
// check that an adapted version is computed for tutors
database.changeUser("tutor1");
examChecklistDTO = examService.getStatsForChecklist(exam, false);
assertThat(examChecklistDTO).isNotNull();
assertThat(examChecklistDTO.getNumberOfGeneratedStudentExams()).isNull();
assertThat(examChecklistDTO.getAllExamExercisesAllStudentsPrepared()).isFalse();
assertThat(examChecklistDTO.getNumberOfTotalParticipationsForAssessment()).isZero();
database.changeUser("instructor1");
// 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).toList();
for (var exercise : exercisesInExam) {
List<StudentParticipation> participations = studentParticipationRepository.findByExerciseIdWithEagerLegalSubmissionsResult(exercise.getId());
exercise.setStudentParticipations(new HashSet<>(participations));
participationCounter += exercise.getStudentParticipations().size();
}
assertEquals(participationCounter, noGeneratedParticipations);
// Score used for all exercise results
Double resultScore = 75D;
// Assign results to participations and submissions
for (var exercise : exercisesInExam) {
for (var participation : exercise.getStudentParticipations()) {
Submission submission;
// Programming exercises don't have a submission yet
if (exercise instanceof ProgrammingExercise) {
assertThat(participation.getSubmissions()).isEmpty();
submission = new ProgrammingSubmission();
submission.setParticipation(participation);
submission = submissionRepository.save(submission);
} else {
// There should only be one submission for text, quiz, modeling and file upload
assertThat(participation.getSubmissions()).hasSize(1);
submission = participation.getSubmissions().iterator().next();
}
// Create results
var result = new Result().score(resultScore).rated(true).resultString("Good").completionDate(ZonedDateTime.now().minusMinutes(5));
result.setParticipation(participation);
result.setAssessor(instructor);
result = resultRepository.save(result);
result.setSubmission(submission);
submission.addResult(result);
submission.submitted(true);
submission.setSubmissionDate(ZonedDateTime.now().minusMinutes(6));
submissionRepository.save(submission);
}
}
// explicitly set the user again to prevent issues in the following server call due to the use of SecurityUtils.setAuthorizationObject();
database.changeUser("instructor1");
final var exerciseWithNoUsers = ModelFactory.generateTextExerciseForExam(exam.getExerciseGroups().get(0));
exerciseWithNoUsers.setKnowledge(textAssessmentKnowledgeService.createNewKnowledge());
exerciseRepo.save(exerciseWithNoUsers);
GradingScale gradingScale = new GradingScale();
gradingScale.setExam(exam);
gradingScale.setGradeType(GradeType.GRADE);
gradingScale.setGradeSteps(database.generateGradeStepSet(gradingScale, true));
gradingScaleRepository.save(gradingScale);
var response = request.get("/api/courses/" + course.getId() + "/exams/" + exam.getId() + "/scores", HttpStatus.OK, ExamScoresDTO.class);
// Compare generated results to data in ExamScoresDTO
// Compare top-level DTO properties
assertThat(response.maxPoints).isEqualTo(exam.getMaxPoints());
// For calculation assume that all exercises within an exerciseGroups have the same max points
double calculatedAverageScore = 0.0;
for (var exerciseGroup : exam.getExerciseGroups()) {
var exercise = exerciseGroup.getExercises().stream().findAny().get();
if (exercise.getIncludedInOverallScore().equals(IncludedInOverallScore.NOT_INCLUDED)) {
continue;
}
calculatedAverageScore += Math.round(exercise.getMaxPoints() * resultScore / 100.00 * 10) / 10.0;
}
assertThat(response.averagePointsAchieved).isEqualTo(calculatedAverageScore);
assertThat(response.title).isEqualTo(exam.getTitle());
assertThat(response.examId).isEqualTo(exam.getId());
// Ensure that all exerciseGroups of the exam are present in the DTO
List<Long> exerciseGroupIdsInDTO = response.exerciseGroups.stream().map(exerciseGroup -> exerciseGroup.id).collect(Collectors.toList());
List<Long> exerciseGroupIdsInExam = exam.getExerciseGroups().stream().map(ExerciseGroup::getId).collect(Collectors.toList());
assertThat(exerciseGroupIdsInExam).containsExactlyInAnyOrderElementsOf(exerciseGroupIdsInDTO);
// Compare exerciseGroups in DTO to exam exerciseGroups
// Tolerated absolute difference for floating-point number comparisons
double EPSILON = 0000.1;
for (var exerciseGroupDTO : response.exerciseGroups) {
// Find the original exerciseGroup of the exam using the id in ExerciseGroupId
ExerciseGroup originalExerciseGroup = exam.getExerciseGroups().stream().filter(exerciseGroup -> exerciseGroup.getId().equals(exerciseGroupDTO.id)).findFirst().get();
// Assume that all exercises in a group have the same max score
Double groupMaxScoreFromExam = originalExerciseGroup.getExercises().stream().findAny().get().getMaxPoints();
assertThat(exerciseGroupDTO.maxPoints).isEqualTo(originalExerciseGroup.getExercises().stream().findAny().get().getMaxPoints());
assertEquals(exerciseGroupDTO.maxPoints, groupMaxScoreFromExam, EPSILON);
// Compare exercise information
long noOfExerciseGroupParticipations = 0;
for (var originalExercise : originalExerciseGroup.getExercises()) {
// Find the corresponding ExerciseInfo object
var exerciseDTO = exerciseGroupDTO.containedExercises.stream().filter(exerciseInfo -> exerciseInfo.exerciseId.equals(originalExercise.getId())).findFirst().get();
// Check the exercise title
assertThat(originalExercise.getTitle()).isEqualTo(exerciseDTO.title);
// Check the max points of the exercise
assertThat(originalExercise.getMaxPoints()).isEqualTo(exerciseDTO.maxPoints);
// Check the number of exercise participants and update the group participant counter
var noOfExerciseParticipations = originalExercise.getStudentParticipations().size();
noOfExerciseGroupParticipations += noOfExerciseParticipations;
assertThat(Long.valueOf(originalExercise.getStudentParticipations().size())).isEqualTo(exerciseDTO.numberOfParticipants);
}
assertThat(noOfExerciseGroupParticipations).isEqualTo(exerciseGroupDTO.numberOfParticipants);
}
// Ensure that all registered students have a StudentResult
List<Long> studentIdsWithStudentResults = response.studentResults.stream().map(studentResult -> studentResult.userId).collect(Collectors.toList());
List<Long> registeredUsersIds = exam.getRegisteredUsers().stream().map(DomainObject::getId).collect(Collectors.toList());
assertThat(studentIdsWithStudentResults).containsExactlyInAnyOrderElementsOf(registeredUsersIds);
// Compare StudentResult with the generated results
for (var studentResult : response.studentResults) {
// Find the original user using the id in StudentResult
User originalUser = exam.getRegisteredUsers().stream().filter(users -> users.getId().equals(studentResult.userId)).findFirst().get();
StudentExam studentExamOfUser = studentExams.stream().filter(studentExam -> studentExam.getUser().equals(originalUser)).findFirst().get();
assertThat(studentResult.name).isEqualTo(originalUser.getName());
assertThat(studentResult.eMail).isEqualTo(originalUser.getEmail());
assertThat(studentResult.login).isEqualTo(originalUser.getLogin());
assertThat(studentResult.registrationNumber).isEqualTo(originalUser.getRegistrationNumber());
// Calculate overall points achieved
var calculatedOverallPoints = studentExamOfUser.getExercises().stream().filter(exercise -> !exercise.getIncludedInOverallScore().equals(IncludedInOverallScore.NOT_INCLUDED)).map(Exercise::getMaxPoints).reduce(0.0, (total, maxScore) -> (Math.round((total + maxScore * resultScore / 100) * 10) / 10.0));
assertEquals(studentResult.overallPointsAchieved, calculatedOverallPoints, EPSILON);
// Calculate overall score achieved
var calculatedOverallScore = calculatedOverallPoints / response.maxPoints * 100;
assertEquals(studentResult.overallScoreAchieved, calculatedOverallScore, EPSILON);
assertThat(studentResult.overallGrade).isNotNull();
assertThat(studentResult.hasPassed).isNotNull();
// Ensure that the exercise ids of the student exam are the same as the exercise ids in the students exercise results
List<Long> exerciseIdsOfStudentResult = studentResult.exerciseGroupIdToExerciseResult.values().stream().map(exerciseResult -> exerciseResult.exerciseId).collect(Collectors.toList());
List<Long> exerciseIdsInStudentExam = studentExamOfUser.getExercises().stream().map(DomainObject::getId).collect(Collectors.toList());
assertThat(exerciseIdsOfStudentResult).containsExactlyInAnyOrderElementsOf(exerciseIdsInStudentExam);
for (Map.Entry<Long, ExamScoresDTO.ExerciseResult> entry : studentResult.exerciseGroupIdToExerciseResult.entrySet()) {
var exerciseResult = entry.getValue();
// Find the original exercise using the id in ExerciseResult
Exercise originalExercise = studentExamOfUser.getExercises().stream().filter(exercise -> exercise.getId().equals(exerciseResult.exerciseId)).findFirst().get();
// Check that the key is associated with the exerciseGroup which actually contains the exercise in the exerciseResult
assertThat(originalExercise.getExerciseGroup().getId()).isEqualTo(entry.getKey());
assertThat(exerciseResult.title).isEqualTo(originalExercise.getTitle());
assertThat(exerciseResult.maxScore).isEqualTo(originalExercise.getMaxPoints());
assertThat(exerciseResult.achievedScore).isEqualTo(resultScore);
assertEquals(exerciseResult.achievedPoints, originalExercise.getMaxPoints() * resultScore / 100, EPSILON);
}
}
// change back to instructor user
database.changeUser("instructor1");
// check if stats are set correctly for the instructor
examChecklistDTO = examService.getStatsForChecklist(exam, true);
assertThat(examChecklistDTO).isNotNull();
assertThat(examChecklistDTO.getNumberOfGeneratedStudentExams()).isEqualTo(15);
assertThat(examChecklistDTO.getNumberOfExamsSubmitted()).isEqualTo(15);
assertThat(examChecklistDTO.getNumberOfExamsStarted()).isEqualTo(15);
assertThat(examChecklistDTO.getAllExamExercisesAllStudentsPrepared()).isTrue();
assertThat(examChecklistDTO.getNumberOfTotalParticipationsForAssessment()).isEqualTo(75);
assertThat(examChecklistDTO.getNumberOfTestRuns()).isZero();
assertThat(examChecklistDTO.getNumberOfTotalExamAssessmentsFinishedByCorrectionRound()).hasSize(1).containsAll((Collections.singletonList(90L)));
// change to a tutor
database.changeUser("tutor1");
// check that a modified version is returned
// check if stats are set correctly for the instructor
examChecklistDTO = examService.getStatsForChecklist(exam, false);
assertThat(examChecklistDTO).isNotNull();
assertThat(examChecklistDTO.getNumberOfGeneratedStudentExams()).isNull();
assertThat(examChecklistDTO.getNumberOfExamsSubmitted()).isNull();
assertThat(examChecklistDTO.getNumberOfExamsStarted()).isNull();
assertThat(examChecklistDTO.getAllExamExercisesAllStudentsPrepared()).isFalse();
assertThat(examChecklistDTO.getNumberOfTotalParticipationsForAssessment()).isEqualTo(75);
assertThat(examChecklistDTO.getNumberOfTestRuns()).isNull();
assertThat(examChecklistDTO.getNumberOfTotalExamAssessmentsFinishedByCorrectionRound()).hasSize(1).containsExactly(90L);
// change back to instructor user
database.changeUser("instructor1");
// Make sure delete also works if so many objects have been created before
request.delete("/api/courses/" + course.getId() + "/exams/" + exam.getId(), HttpStatus.OK);
}
use of de.tum.in.www1.artemis.domain.participation.Participant in project ArTEMiS by ls1intum.
the class ParticipantScoreIntegrationTest method getParticipantScoresOfCourse_asInstructorOfCourse_shouldReturnParticipantScores.
@Test
@WithMockUser(username = "instructor1", roles = "INSTRUCTOR")
public void getParticipantScoresOfCourse_asInstructorOfCourse_shouldReturnParticipantScores() throws Exception {
List<ParticipantScoreDTO> participantScoresOfCourse = request.getList("/api/courses/" + idOfCourse + "/participant-scores", HttpStatus.OK, ParticipantScoreDTO.class);
assertThat(participantScoresOfCourse).hasSize(2);
ParticipantScoreDTO student1Result = participantScoresOfCourse.stream().filter(participantScoreDTO -> participantScoreDTO.userId != null).findFirst().get();
ParticipantScoreDTO team1Result = participantScoresOfCourse.stream().filter(participantScoreDTO -> participantScoreDTO.teamId != null).findFirst().get();
assertParticipantScoreDTOStructure(student1Result, idOfStudent1, null, idOfIndividualTextExercise, 50D, 50D, 5.0, 5.0);
assertParticipantScoreDTOStructure(team1Result, null, idOfTeam1, idOfTeamTextExercise, 50D, 50D, 5.0, 5.0);
}
use of de.tum.in.www1.artemis.domain.participation.Participant in project ArTEMiS by ls1intum.
the class ParticipantScoreIntegrationTest method getAverageScoreOfParticipantInExam_asInstructorOfCourse_shouldReturnAverageParticipantScores.
@Test
@WithMockUser(username = "instructor1", roles = "INSTRUCTOR")
public void getAverageScoreOfParticipantInExam_asInstructorOfCourse_shouldReturnAverageParticipantScores() throws Exception {
List<ParticipantScoreAverageDTO> participantScoreAverageDTOS = request.getList("/api/exams/" + idOfExam + "/participant-scores/average-participant", HttpStatus.OK, ParticipantScoreAverageDTO.class);
assertThat(participantScoreAverageDTOS).hasSize(1);
ParticipantScoreAverageDTO student1Result = participantScoreAverageDTOS.stream().filter(participantScoreAverageDTO -> participantScoreAverageDTO.userName != null).findFirst().get();
assertAverageParticipantScoreDTOStructure(student1Result, "student1", null, 50.0, 50.0, 5.0, 5.0);
}
use of de.tum.in.www1.artemis.domain.participation.Participant in project ArTEMiS by ls1intum.
the class LearningGoalIntegrationTest method createParticipationSubmissionAndResult.
private void createParticipationSubmissionAndResult(Long idOfExercise, Participant participant, Double pointsOfExercise, Double bonusPointsOfExercise, long scoreAwarded, boolean rated) {
Exercise exercise = exerciseRepository.findById(idOfExercise).get();
if (!exercise.getMaxPoints().equals(pointsOfExercise)) {
exercise.setMaxPoints(pointsOfExercise);
}
if (!exercise.getBonusPoints().equals(bonusPointsOfExercise)) {
exercise.setBonusPoints(bonusPointsOfExercise);
}
exercise = exerciseRepository.save(exercise);
StudentParticipation studentParticipation = participationService.startExercise(exercise, participant, false);
Submission submission;
if (exercise instanceof ProgrammingExercise) {
submission = new ProgrammingSubmission();
} else if (exercise instanceof ModelingExercise) {
submission = new ModelingSubmission();
} else if (exercise instanceof TextExercise) {
submission = new TextSubmission();
} else if (exercise instanceof FileUploadExercise) {
submission = new FileUploadSubmission();
} else if (exercise instanceof QuizExercise) {
submission = new QuizSubmission();
} else {
throw new RuntimeException("Unsupported exercise type: " + exercise);
}
submission.setType(SubmissionType.MANUAL);
submission.setParticipation(studentParticipation);
submission = submissionRepository.save(submission);
// result
Result result = ModelFactory.generateResult(rated, scoreAwarded);
result.setParticipation(studentParticipation);
result.setCompletionDate(ZonedDateTime.now());
result = resultRepository.save(result);
submission.addResult(result);
result.setSubmission(submission);
submissionRepository.save(submission);
}
use of de.tum.in.www1.artemis.domain.participation.Participant in project Artemis by ls1intum.
the class ScoreService method removeOrUpdateAssociatedParticipantScore.
/**
* Either updates or removes an existing participant score when a result is removed
* The annotation "@Transactional" is ok because it means that this method does not support run in an outer transactional context, instead the outer transaction is paused
*
* @param resultToBeDeleted result that will be removes
*/
// ok (see JavaDoc)
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void removeOrUpdateAssociatedParticipantScore(Result resultToBeDeleted) {
// In this method we use custom @Query methods that will fail if no authentication is available, therefore
// we check this here and set a dummy authentication if none is available (this is the case in a scheduled service or
// websocket)
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
SecurityUtils.setAuthorizationObject();
}
Optional<ParticipantScore> associatedParticipantScoreOptional;
if (resultToBeDeleted.isRated() != null && resultToBeDeleted.isRated()) {
associatedParticipantScoreOptional = participantScoreRepository.findParticipantScoreByLastRatedResult(resultToBeDeleted);
} else {
associatedParticipantScoreOptional = participantScoreRepository.findParticipantScoresByLastResult(resultToBeDeleted);
}
if (associatedParticipantScoreOptional.isEmpty()) {
return;
}
// There is a participant score connected to the result that will be deleted
ParticipantScore associatedParticipantScore = associatedParticipantScoreOptional.get();
Exercise exercise = associatedParticipantScore.getExercise();
String originalParticipantScoreStructure = associatedParticipantScore.toString();
// There are two possibilities now:
// A: Another result exists for the exercise and the student / team -> update participant score with the newest one
// B: No other result exists for the exercise and the student / team -> remove participant score
tryToFindNewLastResult(resultToBeDeleted, associatedParticipantScore, exercise);
if (associatedParticipantScore.getLastResult() == null && associatedParticipantScore.getLastRatedResult() == null) {
participantScoreRepository.deleteById(associatedParticipantScore.getId());
logger.info("Deleted an existing participant score: " + originalParticipantScoreStructure);
} else {
ParticipantScore updatedParticipantScore = participantScoreRepository.saveAndFlush(associatedParticipantScore);
logger.info("Updated an existing participant score. Was: " + originalParticipantScoreStructure + ". Is: " + updatedParticipantScore);
}
}
Aggregations