From 7cad252d1abb6727740cbcfb15659739b384a487 Mon Sep 17 00:00:00 2001 From: Stephan Krusche Date: Mon, 25 Nov 2024 16:42:49 +0100 Subject: [PATCH 1/8] Quiz exercises: Show all participations with filter options --- .../StudentParticipationRepository.java | 175 ++++++++++-------- .../exercise/web/ParticipationResource.java | 5 +- .../ProgrammingSubmissionRepository.java | 10 +- ...grammingExerciseParticipationResource.java | 4 +- .../participation.component.html | 50 +++-- .../ParticipationIntegrationTest.java | 12 +- 6 files changed, 137 insertions(+), 119 deletions(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/exercise/repository/StudentParticipationRepository.java b/src/main/java/de/tum/cit/aet/artemis/exercise/repository/StudentParticipationRepository.java index 1101f94d4708..bb775298efb5 100644 --- a/src/main/java/de/tum/cit/aet/artemis/exercise/repository/StudentParticipationRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/exercise/repository/StudentParticipationRepository.java @@ -59,7 +59,8 @@ public interface StudentParticipationRepository extends ArtemisJpaRepository findByCourseIdWithEagerRatedResults(@Param("courseId") long courseId); @@ -69,8 +70,10 @@ public interface StudentParticipationRepository extends ArtemisJpaRepository findByCourseIdAndStudentIdWithEagerRatedResults(@Param("courseId") long courseId, @Param("studentId") long studentId); @@ -79,7 +82,8 @@ SELECT COUNT(p.id) > 0 FROM StudentParticipation p LEFT JOIN p.team.students ts WHERE p.exercise.course.id = :courseId - AND (p.student.id = :studentId OR ts.id = :studentId) + AND (p.student.id = :studentId + OR ts.id = :studentId) """) boolean existsByCourseIdAndStudentId(@Param("courseId") long courseId, @Param("studentId") long studentId); @@ -91,7 +95,8 @@ SELECT COUNT(p.id) > 0 WHERE p.testRun = FALSE AND p.exercise.exerciseGroup.exam.id = :examId AND r.rated = TRUE - AND (s.type <> de.tum.cit.aet.artemis.exercise.domain.SubmissionType.ILLEGAL OR s.type IS NULL) + AND (s.type <> de.tum.cit.aet.artemis.exercise.domain.SubmissionType.ILLEGAL + OR s.type IS NULL) """) List findByExamIdWithEagerLegalSubmissionsRatedResults(@Param("examId") long examId); @@ -125,7 +130,8 @@ SELECT COUNT(p.id) > 0 LEFT JOIN FETCH p.submissions s WHERE p.exercise.id = :exerciseId AND p.student.login = :username - AND (s.type <> de.tum.cit.aet.artemis.exercise.domain.SubmissionType.ILLEGAL OR s.type IS NULL) + AND (s.type <> de.tum.cit.aet.artemis.exercise.domain.SubmissionType.ILLEGAL + OR s.type IS NULL) """) Optional findWithEagerLegalSubmissionsByExerciseIdAndStudentLogin(@Param("exerciseId") long exerciseId, @Param("username") String username); @@ -135,7 +141,8 @@ SELECT COUNT(p.id) > 0 LEFT JOIN FETCH p.submissions s WHERE p.exercise.id = :exerciseId AND p.student.login = :username - AND (s.type <> de.tum.cit.aet.artemis.exercise.domain.SubmissionType.ILLEGAL OR s.type IS NULL) + AND (s.type <> de.tum.cit.aet.artemis.exercise.domain.SubmissionType.ILLEGAL + OR s.type IS NULL) AND p.testRun = :testRun """) Optional findWithEagerLegalSubmissionsByExerciseIdAndStudentLoginAndTestRun(@Param("exerciseId") long exerciseId, @Param("username") String username, @@ -157,7 +164,8 @@ Optional findWithEagerLegalSubmissionsByExerciseIdAndStude LEFT JOIN FETCH t.students WHERE p.exercise.id = :exerciseId AND p.team.id = :teamId - AND (s.type <> de.tum.cit.aet.artemis.exercise.domain.SubmissionType.ILLEGAL OR s.type IS NULL) + AND (s.type <> de.tum.cit.aet.artemis.exercise.domain.SubmissionType.ILLEGAL + OR s.type IS NULL) """) Optional findWithEagerLegalSubmissionsAndTeamStudentsByExerciseIdAndTeamId(@Param("exerciseId") long exerciseId, @Param("teamId") long teamId); @@ -175,8 +183,9 @@ SELECT COUNT(p) > 0 FROM StudentParticipation p LEFT JOIN p.team.students u LEFT JOIN p.student s - WHERE p.id = :participationId AND - (s.login = :login OR u.login = :login) + WHERE p.id = :participationId + AND (s.login = :login + OR u.login = :login) """) boolean existsByIdAndParticipatingStudentLogin(@Param("participationId") long participationId, @Param("login") String login); @@ -187,7 +196,8 @@ SELECT COUNT(p) > 0 LEFT JOIN FETCH s.results WHERE p.exercise.id = :exerciseId AND p.testRun = :testRun - AND (s.type <> de.tum.cit.aet.artemis.exercise.domain.SubmissionType.ILLEGAL OR s.type IS NULL) + AND (s.type <> de.tum.cit.aet.artemis.exercise.domain.SubmissionType.ILLEGAL + OR s.type IS NULL) """) List findByExerciseIdAndTestRunWithEagerLegalSubmissionsResult(@Param("exerciseId") long exerciseId, @Param("testRun") boolean testRun); @@ -235,7 +245,10 @@ Optional findByExerciseIdAndStudentIdAndTestRunWithEagerSu LEFT JOIN FETCH r.assessmentNote WHERE p.exercise.id = :exerciseId AND ( - r.id = (SELECT MAX(p_r.id) FROM p.results p_r) + r.id = ( + SELECT MAX(p_r.id) + FROM p.results p_r + ) OR r.assessmentType <> de.tum.cit.aet.artemis.assessment.domain.AssessmentType.AUTOMATIC OR r IS NULL ) @@ -260,29 +273,16 @@ Optional findByExerciseIdAndStudentIdAndTestRunWithEagerSu LEFT JOIN FETCH t.students WHERE p.exercise.id = :exerciseId AND ( - r.id = (SELECT MAX(p_r.id) FROM p.results p_r) + r.id = ( + SELECT MAX(p_r.id) + FROM p.results p_r + ) OR r.assessmentType <> de.tum.cit.aet.artemis.assessment.domain.AssessmentType.AUTOMATIC OR r IS NULL ) """) Set findByExerciseIdWithLatestAndManualResultsWithTeamInformation(@Param("exerciseId") long exerciseId); - @Query(""" - SELECT DISTINCT p - FROM StudentParticipation p - LEFT JOIN FETCH p.results r - LEFT JOIN FETCH r.submission s - LEFT JOIN FETCH p.submissions - LEFT JOIN FETCH r.assessmentNote - WHERE p.exercise.id = :exerciseId - AND ( - r.id = (SELECT MAX(p_r.id) FROM p.results p_r WHERE p_r.rated = TRUE) - OR r.assessmentType <> de.tum.cit.aet.artemis.assessment.domain.AssessmentType.AUTOMATIC - OR r IS NULL - ) - """) - Set findByExerciseIdWithLatestAndManualRatedResultsAndAssessmentNote(@Param("exerciseId") long exerciseId); - @Query(""" SELECT DISTINCT p FROM StudentParticipation p @@ -292,7 +292,11 @@ Optional findByExerciseIdAndStudentIdAndTestRunWithEagerSu AND p.testRun = :testRun AND (s.type <> de.tum.cit.aet.artemis.exercise.domain.SubmissionType.ILLEGAL OR s.type IS NULL) AND r.assessmentType <> de.tum.cit.aet.artemis.assessment.domain.AssessmentType.AUTOMATIC - AND r.id = (SELECT MAX(r2.id) FROM p.results r2 WHERE r2.completionDate IS NOT NULL) + AND r.id = ( + SELECT MAX(r2.id) + FROM p.results r2 + WHERE r2.completionDate IS NOT NULL + ) """) Set findByExerciseIdAndTestRunWithEagerLegalSubmissionsAndLatestResultWithCompletionDate(@Param("exerciseId") long exerciseId, @Param("testRun") boolean testRun); @@ -343,16 +347,14 @@ default List findByExerciseIdWithLatestAutomaticResultAndF LEFT JOIN FETCH f.testCase LEFT JOIN FETCH r.submission s WHERE p.id = :participationId - AND (r.id = ( + AND r.id = ( SELECT MAX(pr.id) FROM p.results pr LEFT JOIN pr.submission prs WHERE pr.assessmentType = de.tum.cit.aet.artemis.assessment.domain.AssessmentType.AUTOMATIC - AND ( - prs.type <> de.tum.cit.aet.artemis.exercise.domain.SubmissionType.ILLEGAL - OR prs.type IS NULL - ) - )) + AND (prs.type <> de.tum.cit.aet.artemis.exercise.domain.SubmissionType.ILLEGAL + OR prs.type IS NULL) + ) """) Optional findByIdWithLatestAutomaticResultAndFeedbacksAndTestCases(@Param("participationId") long participationId); @@ -366,10 +368,8 @@ SELECT MAX(pr.id) LEFT JOIN FETCH r.submission s WHERE p.exercise.id = :exerciseId AND (s.type <> de.tum.cit.aet.artemis.exercise.domain.SubmissionType.ILLEGAL OR s.type IS NULL) - AND ( - r.assessmentType = de.tum.cit.aet.artemis.assessment.domain.AssessmentType.MANUAL - OR r.assessmentType = de.tum.cit.aet.artemis.assessment.domain.AssessmentType.SEMI_AUTOMATIC - ) + AND (r.assessmentType = de.tum.cit.aet.artemis.assessment.domain.AssessmentType.MANUAL + OR r.assessmentType = de.tum.cit.aet.artemis.assessment.domain.AssessmentType.SEMI_AUTOMATIC) """) List findByExerciseIdWithManualResultAndFeedbacksAndTestCases(@Param("exerciseId") long exerciseId); @@ -385,11 +385,10 @@ default List findByExerciseIdWithManualResultAndFeedbacksA LEFT JOIN FETCH f.testCase LEFT JOIN FETCH r.submission s WHERE p.id = :participationId - AND (s.type <> de.tum.cit.aet.artemis.exercise.domain.SubmissionType.ILLEGAL OR s.type IS NULL) - AND ( - r.assessmentType = de.tum.cit.aet.artemis.assessment.domain.AssessmentType.MANUAL - OR r.assessmentType = de.tum.cit.aet.artemis.assessment.domain.AssessmentType.SEMI_AUTOMATIC - ) + AND (s.type <> de.tum.cit.aet.artemis.exercise.domain.SubmissionType.ILLEGAL + OR s.type IS NULL) + AND (r.assessmentType = de.tum.cit.aet.artemis.assessment.domain.AssessmentType.MANUAL + OR r.assessmentType = de.tum.cit.aet.artemis.assessment.domain.AssessmentType.SEMI_AUTOMATIC) """) Optional findByIdWithManualResultAndFeedbacks(@Param("participationId") long participationId); @@ -399,7 +398,8 @@ default List findByExerciseIdWithManualResultAndFeedbacksA LEFT JOIN FETCH p.submissions s WHERE p.exercise.id = :exerciseId AND p.student.id = :studentId - AND (s.type <> de.tum.cit.aet.artemis.exercise.domain.SubmissionType.ILLEGAL OR s.type IS NULL) + AND (s.type <> de.tum.cit.aet.artemis.exercise.domain.SubmissionType.ILLEGAL + OR s.type IS NULL) """) List findByExerciseIdAndStudentIdWithEagerLegalSubmissions(@Param("exerciseId") long exerciseId, @Param("studentId") long studentId); @@ -427,7 +427,8 @@ default List findByExerciseIdWithManualResultAndFeedbacksA LEFT JOIN FETCH p.submissions s WHERE p.exercise.id = :exerciseId AND p.team.id = :teamId - AND (s.type <> de.tum.cit.aet.artemis.exercise.domain.SubmissionType.ILLEGAL OR s.type IS NULL) + AND (s.type <> de.tum.cit.aet.artemis.exercise.domain.SubmissionType.ILLEGAL + OR s.type IS NULL) """) List findByExerciseIdAndTeamIdWithEagerLegalSubmissions(@Param("exerciseId") long exerciseId, @Param("teamId") long teamId); @@ -451,8 +452,10 @@ default List findByExerciseIdWithManualResultAndFeedbacksA LEFT JOIN FETCH t.students WHERE p.exercise.id = :exerciseId AND p.team.id = :teamId - AND (s.type <> de.tum.cit.aet.artemis.exercise.domain.SubmissionType.ILLEGAL OR s.type IS NULL) - AND (rs.type <> de.tum.cit.aet.artemis.exercise.domain.SubmissionType.ILLEGAL OR rs.type IS NULL) + AND (s.type <> de.tum.cit.aet.artemis.exercise.domain.SubmissionType.ILLEGAL + OR s.type IS NULL) + AND (rs.type <> de.tum.cit.aet.artemis.exercise.domain.SubmissionType.ILLEGAL + OR rs.type IS NULL) """) List findByExerciseIdAndTeamIdWithEagerResultsAndLegalSubmissionsAndTeamStudents(@Param("exerciseId") long exerciseId, @Param("teamId") long teamId); @@ -471,7 +474,8 @@ SELECT MAX(pr.id) LEFT JOIN pr.submission prs WHERE prs.type <> de.tum.cit.aet.artemis.exercise.domain.SubmissionType.ILLEGAL OR prs.type IS NULL - ) OR r.id IS NULL) + ) + OR r.id IS NULL) """) Optional findByExerciseIdAndStudentIdAndTestRunWithLatestResult(@Param("exerciseId") long exerciseId, @Param("studentId") long studentId, @Param("testRun") boolean testRun); @@ -524,7 +528,10 @@ WHERE prs.assessmentType IN ( ) ) AND submission.submitted = TRUE - AND submission.id = (SELECT MAX(s.id) FROM p.submissions s) + AND submission.id = ( + SELECT MAX(s.id) + FROM p.submissions s + ) """) List findByExerciseIdWithLatestSubmissionWithoutManualResultsAndIgnoreTestRunParticipation(@Param("exerciseId") long exerciseId, @Param("correctionRound") long correctionRound); @@ -548,7 +555,10 @@ WHERE prs.assessmentType IN ( de.tum.cit.aet.artemis.assessment.domain.AssessmentType.SEMI_AUTOMATIC ) ) AND s.submitted = TRUE - AND s.id = (SELECT MAX(s.id) FROM p.submissions s) + AND s.id = ( + SELECT MAX(s.id) + FROM p.submissions s + ) """) List findByExerciseIdWithLatestSubmissionWithoutManualResultsWithPassedIndividualDueDateIgnoreTestRuns(@Param("exerciseId") long exerciseId, @Param("now") ZonedDateTime now); @@ -558,7 +568,8 @@ List findByExerciseIdWithLatestSubmissionWithoutManualResu FROM Participation p LEFT JOIN FETCH p.submissions s WHERE p.id = :participationId - AND (s.type <> de.tum.cit.aet.artemis.exercise.domain.SubmissionType.ILLEGAL OR s.type IS NULL) + AND (s.type <> de.tum.cit.aet.artemis.exercise.domain.SubmissionType.ILLEGAL + OR s.type IS NULL) """) Optional findWithEagerLegalSubmissionsById(@Param("participationId") long participationId); @@ -593,8 +604,10 @@ List findByExerciseIdWithLatestSubmissionWithoutManualResu LEFT JOIN FETCH p.team t LEFT JOIN FETCH t.students WHERE p.id = :participationId - AND (s.type <> de.tum.cit.aet.artemis.exercise.domain.SubmissionType.ILLEGAL OR s.type IS NULL) - AND (rs.type <> de.tum.cit.aet.artemis.exercise.domain.SubmissionType.ILLEGAL OR rs.type IS NULL) + AND (s.type <> de.tum.cit.aet.artemis.exercise.domain.SubmissionType.ILLEGAL + OR s.type IS NULL) + AND (rs.type <> de.tum.cit.aet.artemis.exercise.domain.SubmissionType.ILLEGAL + OR rs.type IS NULL) """) Optional findWithEagerLegalSubmissionsResultsFeedbacksById(@Param("participationId") long participationId); @@ -617,10 +630,9 @@ List findByExerciseIdWithLatestSubmissionWithoutManualResu FROM StudentParticipation p JOIN Result r ON r.participation.id = p.id WHERE p.exercise.id = :exerciseId - AND ( - p.student.firstName LIKE %:partialStudentName% - OR p.student.lastName LIKE %:partialStudentName% - ) AND r.completionDate IS NOT NULL + AND (p.student.firstName LIKE %:partialStudentName% + OR p.student.lastName LIKE %:partialStudentName%) + AND r.completionDate IS NOT NULL """) List findIdsByExerciseIdAndStudentName(@Param("exerciseId") long exerciseId, @Param("partialStudentName") String partialStudentName, Pageable pageable); @@ -632,10 +644,9 @@ SELECT COUNT(p) FROM StudentParticipation p JOIN Result r ON r.participation.id = p.id WHERE p.exercise.id = :exerciseId - AND ( - p.student.firstName LIKE %:partialStudentName% - OR p.student.lastName LIKE %:partialStudentName% - ) AND r.completionDate IS NOT NULL + AND (p.student.firstName LIKE %:partialStudentName% + OR p.student.lastName LIKE %:partialStudentName%) + AND r.completionDate IS NOT NULL """) long countByExerciseIdAndStudentName(@Param("exerciseId") long exerciseId, @Param("partialStudentName") String partialStudentName); @@ -666,8 +677,10 @@ default Page findAllWithEagerSubmissionsAndResultsByExerci LEFT JOIN FETCH p.submissions s LEFT JOIN FETCH s.results sr WHERE p.exercise.id = :exerciseId - AND (s.type <> de.tum.cit.aet.artemis.exercise.domain.SubmissionType.ILLEGAL OR s.type IS NULL) - AND (rs.type <> de.tum.cit.aet.artemis.exercise.domain.SubmissionType.ILLEGAL OR rs.type IS NULL) + AND (s.type <> de.tum.cit.aet.artemis.exercise.domain.SubmissionType.ILLEGAL + OR s.type IS NULL) + AND (rs.type <> de.tum.cit.aet.artemis.exercise.domain.SubmissionType.ILLEGAL + OR rs.type IS NULL) """) List findAllWithEagerLegalSubmissionsAndEagerResultsByExerciseId(@Param("exerciseId") long exerciseId); @@ -697,12 +710,17 @@ default Page findAllWithEagerSubmissionsAndResultsByExerci LEFT JOIN FETCH p.team WHERE p.exercise.id = :exerciseId AND p.testRun = FALSE - AND s.id = (SELECT MAX(s2.id) - FROM p.submissions s2 - WHERE s2.type <> de.tum.cit.aet.artemis.exercise.domain.SubmissionType.ILLEGAL OR s2.type IS NULL) - AND r.id = (SELECT MAX(r2.id) - FROM s.results r2 - WHERE r2.rated = TRUE) + AND s.id = ( + SELECT MAX(s2.id) + FROM p.submissions s2 + WHERE s2.type <> de.tum.cit.aet.artemis.exercise.domain.SubmissionType.ILLEGAL + OR s2.type IS NULL + ) + AND r.id = ( + SELECT MAX(r2.id) + FROM s.results r2 + WHERE r2.rated = TRUE + ) """) List findAllForPlagiarism(@Param("exerciseId") long exerciseId); @@ -713,7 +731,8 @@ default Page findAllWithEagerSubmissionsAndResultsByExerci LEFT JOIN FETCH s.results r WHERE p.student.id = :studentId AND p.exercise IN :exercises - AND (p.testRun = FALSE OR :includeTestRuns = TRUE) + AND (p.testRun = FALSE + OR :includeTestRuns = TRUE) """) Set findByStudentIdAndIndividualExercisesWithEagerSubmissionsResult(@Param("studentId") long studentId, @Param("exercises") Collection exercises, @Param("includeTestRuns") boolean includeTestRuns); @@ -786,7 +805,8 @@ List findTestRunParticipationsByStudentIdAndIndividualExer LEFT JOIN FETCH t.students teamStudent WHERE teamStudent.id = :studentId AND p.exercise IN :exercises - AND (s.type <> de.tum.cit.aet.artemis.exercise.domain.SubmissionType.ILLEGAL OR s.type IS NULL) + AND (s.type <> de.tum.cit.aet.artemis.exercise.domain.SubmissionType.ILLEGAL + OR s.type IS NULL) """) Set findByStudentIdAndTeamExercisesWithEagerLegalSubmissionsResult(@Param("studentId") long studentId, @Param("exercises") Collection exercises); @@ -799,7 +819,8 @@ Set findByStudentIdAndTeamExercisesWithEagerLegalSubmissio LEFT JOIN FETCH p.team t WHERE p.exercise.course.id = :courseId AND t.shortName = :teamShortName - AND (s.type <> de.tum.cit.aet.artemis.exercise.domain.SubmissionType.ILLEGAL OR s.type IS NULL) + AND (s.type <> de.tum.cit.aet.artemis.exercise.domain.SubmissionType.ILLEGAL + OR s.type IS NULL) """) List findAllByCourseIdAndTeamShortNameWithEagerLegalSubmissionsResult(@Param("courseId") long courseId, @Param("teamShortName") String teamShortName); @@ -831,7 +852,8 @@ SELECT p.id, COUNT(s) LEFT JOIN p.submissions s WHERE p.team.shortName = :teamShortName AND p.exercise.course.id = :courseId - AND (s.type <> de.tum.cit.aet.artemis.exercise.domain.SubmissionType.ILLEGAL OR s.type IS NULL) + AND (s.type <> de.tum.cit.aet.artemis.exercise.domain.SubmissionType.ILLEGAL + OR s.type IS NULL) GROUP BY p.id """) List countLegalSubmissionsPerParticipationByCourseIdAndTeamShortName(@Param("courseId") long courseId, @Param("teamShortName") String teamShortName); @@ -850,7 +872,8 @@ AND EXISTS ( FROM p.submissions s1 WHERE s1.participation.id = p.id AND s1.submitted = TRUE - AND (r.assessor = :assessor OR r.assessor.id IS NULL) + AND (r.assessor = :assessor + OR r.assessor.id IS NULL) ) """) List findAllByParticipationExerciseIdAndResultAssessorAndCorrectionRoundIgnoreTestRuns(@Param("exerciseId") long exerciseId, diff --git a/src/main/java/de/tum/cit/aet/artemis/exercise/web/ParticipationResource.java b/src/main/java/de/tum/cit/aet/artemis/exercise/web/ParticipationResource.java index 4d549bb3fe66..b56b2df2aed9 100644 --- a/src/main/java/de/tum/cit/aet/artemis/exercise/web/ParticipationResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/exercise/web/ParticipationResource.java @@ -66,7 +66,6 @@ import de.tum.cit.aet.artemis.core.service.messaging.InstanceMessageSendService; import de.tum.cit.aet.artemis.core.util.HeaderUtil; import de.tum.cit.aet.artemis.exercise.domain.Exercise; -import de.tum.cit.aet.artemis.exercise.domain.ExerciseType; import de.tum.cit.aet.artemis.exercise.domain.InitializationState; import de.tum.cit.aet.artemis.exercise.domain.Submission; import de.tum.cit.aet.artemis.exercise.domain.SubmissionType; @@ -595,9 +594,7 @@ public ResponseEntity> updateParticipationDueDates(@P } private Set findParticipationWithLatestResults(Exercise exercise) { - if (exercise.getExerciseType() == ExerciseType.QUIZ) { - return studentParticipationRepository.findByExerciseIdWithLatestAndManualRatedResultsAndAssessmentNote(exercise.getId()); - } + // TODO: we should reduce the amount of data fetched here and sent to the client, because submissions and results are not used at all if (exercise.isTeamMode()) { // For team exercises the students need to be eagerly fetched return studentParticipationRepository.findByExerciseIdWithLatestAndManualResultsWithTeamInformation(exercise.getId()); diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/repository/ProgrammingSubmissionRepository.java b/src/main/java/de/tum/cit/aet/artemis/programming/repository/ProgrammingSubmissionRepository.java index ffe438217b64..90d514ca4b68 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/repository/ProgrammingSubmissionRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/repository/ProgrammingSubmissionRepository.java @@ -50,7 +50,8 @@ default ProgrammingSubmission findFirstByParticipationIdAndCommitHashOrderByIdDe @Query(value = """ SELECT new de.tum.cit.aet.artemis.programming.dto.ProgrammingSubmissionIdAndSubmissionDateDTO(ps.id, ps.submissionDate) FROM ProgrammingSubmission ps - WHERE ps.participation.id = :participationId ORDER BY ps.submissionDate DESC + WHERE ps.participation.id = :participationId + ORDER BY ps.submissionDate DESC """) List findFirstIdByParticipationIdOrderBySubmissionDateDesc(@Param("participationId") long participationId, Pageable pageable); @@ -72,8 +73,8 @@ default Optional findFirstByParticipationIdWithResultsOrd if (result.isEmpty()) { return Optional.empty(); } - long id = result.getFirst().programmingSubmissionId(); - return findProgrammingSubmissionWithResultsById(id); + long submissionId = result.getFirst().programmingSubmissionId(); + return findProgrammingSubmissionWithResultsById(submissionId); } @Query(""" @@ -104,8 +105,7 @@ default Optional findFirstByParticipationIdWithResultsOrd * @return ProgrammingSubmission list (can be empty!) */ default List findGradedByParticipationIdWithResultsOrderBySubmissionDateDesc(long participationId, Pageable pageable) { - List ids = findSubmissionIdsAndDatesByParticipationId(participationId, pageable).stream().map(ProgrammingSubmissionIdAndSubmissionDateDTO::programmingSubmissionId) - .toList(); + var ids = findSubmissionIdsAndDatesByParticipationId(participationId, pageable).stream().map(ProgrammingSubmissionIdAndSubmissionDateDTO::programmingSubmissionId).toList(); if (ids.isEmpty()) { return Collections.emptyList(); diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/web/ProgrammingExerciseParticipationResource.java b/src/main/java/de/tum/cit/aet/artemis/programming/web/ProgrammingExerciseParticipationResource.java index be1c99c67be6..debe78f111da 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/web/ProgrammingExerciseParticipationResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/web/ProgrammingExerciseParticipationResource.java @@ -231,12 +231,12 @@ public ResponseEntity getLatestPendingSubmission(@PathVar @GetMapping("programming-exercises/{exerciseId}/latest-pending-submissions") @EnforceAtLeastTutor public ResponseEntity>> getLatestPendingSubmissionsByExerciseId(@PathVariable Long exerciseId) { - ProgrammingExercise programmingExercise; - programmingExercise = programmingExerciseRepository.findByIdWithTemplateAndSolutionParticipationElseThrow(exerciseId); + ProgrammingExercise programmingExercise = programmingExerciseRepository.findByIdWithTemplateAndSolutionParticipationElseThrow(exerciseId); if (!authCheckService.isAtLeastTeachingAssistantForExercise(programmingExercise)) { throw new AccessForbiddenException("exercise", exerciseId); } + // TODO: this REST call is quite slow for > 100 participations. We should consider a more efficient way to get the latest pending submissions. Map> pendingSubmissions = submissionService.getLatestPendingSubmissionsForProgrammingExercise(exerciseId); // Remove unnecessary data to make response smaller (exercise, student of participation). pendingSubmissions = pendingSubmissions.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> { diff --git a/src/main/webapp/app/exercises/shared/participation/participation.component.html b/src/main/webapp/app/exercises/shared/participation/participation.component.html index c3eadf5cd3e1..27f175995eef 100644 --- a/src/main/webapp/app/exercises/shared/participation/participation.component.html +++ b/src/main/webapp/app/exercises/shared/participation/participation.component.html @@ -4,38 +4,36 @@

{{ exercise?.title }} - {{ filteredParticipationsSize }}

- @if (exercise?.type === ExerciseType.PROGRAMMING) { -
- - +
+ + + + @if (exercise.type === ExerciseType.PROGRAMMING && afterDueDate) { - @if (exercise.type === ExerciseType.PROGRAMMING && afterDueDate) { - - } -
- } + } +
@if (exercise?.type !== ExerciseType.QUIZ && exercise?.isAtLeastInstructor) {
diff --git a/src/test/java/de/tum/cit/aet/artemis/exercise/participation/ParticipationIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/exercise/participation/ParticipationIntegrationTest.java index 8e97d56893d8..3f32fb13ca5d 100644 --- a/src/test/java/de/tum/cit/aet/artemis/exercise/participation/ParticipationIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/exercise/participation/ParticipationIntegrationTest.java @@ -916,7 +916,8 @@ void getAllParticipationsForExercise_withLatestResults_forQuizExercise() throws courseRepository.save(course); exerciseRepository.save(quizExercise); - var participation = participationUtilService.createAndSaveParticipationForExercise(quizExercise, TEST_PREFIX + "student1"); + final var login = TEST_PREFIX + "student1"; + var participation = participationUtilService.createAndSaveParticipationForExercise(quizExercise, login); var result1 = participationUtilService.createSubmissionAndResult(participation, 42, true); var notGradedResult = participationUtilService.addResultToParticipation(participation, result1.getSubmission()); notGradedResult.setRated(false); @@ -926,11 +927,10 @@ void getAllParticipationsForExercise_withLatestResults_forQuizExercise() throws params.add("withLatestResults", "true"); var participations = request.getList("/api/exercises/" + quizExercise.getId() + "/participations", HttpStatus.OK, StudentParticipation.class, params); - var receivedParticipationWithResult = participations.stream().filter(p -> ((User) p.getParticipant()).getLogin().equals(TEST_PREFIX + "student1")).findFirst() - .orElseThrow(); - assertThat(receivedParticipationWithResult.getResults()).containsOnly(result1); - assertThat(receivedParticipationWithResult.getSubmissions()).isEmpty(); - assertThat(receivedParticipationWithResult.getSubmissionCount()).isEqualTo(1); + var receivedParticipation = participations.stream().filter(p -> p.getParticipantIdentifier().equals(login)).findFirst().orElseThrow(); + assertThat(receivedParticipation.getResults()).containsOnly(notGradedResult); + assertThat(receivedParticipation.getSubmissions()).isEmpty(); + assertThat(receivedParticipation.getSubmissionCount()).isEqualTo(1); } @Test From a4c674ca5b41b75ec3144969e373522787fedcde Mon Sep 17 00:00:00 2001 From: Stephan Krusche Date: Mon, 25 Nov 2024 17:49:22 +0100 Subject: [PATCH 2/8] General: Improve performance of exercise participations view --- .../exercise/web/ParticipationResource.java | 2 +- ...xerciseStudentParticipationRepository.java | 4 ++-- .../service/ProgrammingSubmissionService.java | 19 +++++++++++-------- ...grammingExerciseParticipationResource.java | 9 +++++---- .../participation/participation.component.ts | 2 +- .../participation.component.spec.ts | 4 ++-- 6 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/exercise/web/ParticipationResource.java b/src/main/java/de/tum/cit/aet/artemis/exercise/web/ParticipationResource.java index b56b2df2aed9..05e3166ff171 100644 --- a/src/main/java/de/tum/cit/aet/artemis/exercise/web/ParticipationResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/exercise/web/ParticipationResource.java @@ -594,7 +594,7 @@ public ResponseEntity> updateParticipationDueDates(@P } private Set findParticipationWithLatestResults(Exercise exercise) { - // TODO: we should reduce the amount of data fetched here and sent to the client, because submissions and results are not used at all + // TODO: we should reduce the amount of data fetched here and sent to the client: double check which data is actually required in the exercise scores page if (exercise.isTeamMode()) { // For team exercises the students need to be eagerly fetched return studentParticipationRepository.findByExerciseIdWithLatestAndManualResultsWithTeamInformation(exercise.getId()); diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/repository/ProgrammingExerciseStudentParticipationRepository.java b/src/main/java/de/tum/cit/aet/artemis/programming/repository/ProgrammingExerciseStudentParticipationRepository.java index cc1f57c533fa..c88024f0835b 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/repository/ProgrammingExerciseStudentParticipationRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/repository/ProgrammingExerciseStudentParticipationRepository.java @@ -136,8 +136,8 @@ Optional findWithSubmissionsAndEagerStu @EntityGraph(type = LOAD, attributePaths = { "submissions", "team.students" }) List findWithSubmissionsById(long participationId); - @EntityGraph(type = LOAD, attributePaths = { "submissions" }) - List findWithSubmissionsByExerciseId(long exerciseId); + @EntityGraph(type = LOAD, attributePaths = { "submissions.results" }) + List findWithSubmissionsAndResultsByExerciseId(long exerciseId); @EntityGraph(type = LOAD, attributePaths = { "submissions", "team.students" }) List findWithSubmissionsAndTeamStudentsByExerciseId(long exerciseId); diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingSubmissionService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingSubmissionService.java index 58665d8beae4..9de4485f16b2 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingSubmissionService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingSubmissionService.java @@ -6,6 +6,7 @@ import java.time.ZonedDateTime; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Objects; @@ -341,14 +342,16 @@ public Optional getLatestPendingSubmission(Long participa * @return a Map of {[participationId]: ProgrammingSubmission | null}. Will contain an entry for every student participation of the exercise and a submission object if a * pending submission exists or null if not. */ - public Map> getLatestPendingSubmissionsForProgrammingExercise(Long programmingExerciseId) { - List participations = programmingExerciseStudentParticipationRepository.findWithSubmissionsByExerciseId(programmingExerciseId); - // TODO: find the latest pending submission directly using Java (the submissions are available now) and not with additional db queries - return participations.stream().collect(Collectors.toMap(Participation::getId, p -> findLatestPendingSubmissionForParticipation(p.getId()))); - } - - private Optional findLatestPendingSubmissionForParticipation(final long participationId) { - return findLatestPendingSubmissionForParticipation(participationId, false); + public Map> getLatestPendingSubmissionsForProgrammingExercise(Long programmingExerciseId) { + var participations = programmingExerciseStudentParticipationRepository.findWithSubmissionsAndResultsByExerciseId(programmingExerciseId); + return participations.stream().collect(Collectors.toMap(Participation::getId, p -> { + var latestSubmission = p.getSubmissions().stream().max(Comparator.comparing(Submission::getSubmissionDate)); + if (latestSubmission.isEmpty() || latestSubmission.get().getLatestResult() != null) { + // This is not an error case, it is very likely that there is no pending submission for a participation. + return Optional.empty(); + } + return latestSubmission; + })); } private Optional findLatestPendingSubmissionForParticipation(final long participationId, final boolean isGraded) { diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/web/ProgrammingExerciseParticipationResource.java b/src/main/java/de/tum/cit/aet/artemis/programming/web/ProgrammingExerciseParticipationResource.java index debe78f111da..2566e1a87fb8 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/web/ProgrammingExerciseParticipationResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/web/ProgrammingExerciseParticipationResource.java @@ -37,6 +37,7 @@ import de.tum.cit.aet.artemis.core.service.AuthorizationCheckService; import de.tum.cit.aet.artemis.exam.repository.StudentExamRepository; import de.tum.cit.aet.artemis.exam.service.ExamService; +import de.tum.cit.aet.artemis.exercise.domain.Submission; import de.tum.cit.aet.artemis.exercise.domain.participation.Participation; import de.tum.cit.aet.artemis.exercise.repository.ParticipationRepository; import de.tum.cit.aet.artemis.exercise.service.ParticipationAuthorizationCheckService; @@ -230,17 +231,17 @@ public ResponseEntity getLatestPendingSubmission(@PathVar */ @GetMapping("programming-exercises/{exerciseId}/latest-pending-submissions") @EnforceAtLeastTutor - public ResponseEntity>> getLatestPendingSubmissionsByExerciseId(@PathVariable Long exerciseId) { + public ResponseEntity>> getLatestPendingSubmissionsByExerciseId(@PathVariable Long exerciseId) { ProgrammingExercise programmingExercise = programmingExerciseRepository.findByIdWithTemplateAndSolutionParticipationElseThrow(exerciseId); if (!authCheckService.isAtLeastTeachingAssistantForExercise(programmingExercise)) { throw new AccessForbiddenException("exercise", exerciseId); } - // TODO: this REST call is quite slow for > 100 participations. We should consider a more efficient way to get the latest pending submissions. - Map> pendingSubmissions = submissionService.getLatestPendingSubmissionsForProgrammingExercise(exerciseId); + // TODO: use a different data structure than map here + Map> pendingSubmissions = submissionService.getLatestPendingSubmissionsForProgrammingExercise(exerciseId); // Remove unnecessary data to make response smaller (exercise, student of participation). pendingSubmissions = pendingSubmissions.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> { - Optional submissionOpt = entry.getValue(); + Optional submissionOpt = entry.getValue(); // Remove participation, is not needed in the response. submissionOpt.ifPresent(submission -> submission.setParticipation(null)); return submissionOpt; diff --git a/src/main/webapp/app/exercises/shared/participation/participation.component.ts b/src/main/webapp/app/exercises/shared/participation/participation.component.ts index 627a33ce8815..c3e018e4b526 100644 --- a/src/main/webapp/app/exercises/shared/participation/participation.component.ts +++ b/src/main/webapp/app/exercises/shared/participation/participation.component.ts @@ -150,7 +150,7 @@ export class ParticipationComponent implements OnInit, OnDestroy { } private loadParticipations(exerciseId: number) { - this.participationService.findAllParticipationsByExercise(exerciseId, true).subscribe((participationsResponse) => { + this.participationService.findAllParticipationsByExercise(exerciseId, false).subscribe((participationsResponse) => { this.participations = participationsResponse.body!; if (this.exercise.type === ExerciseType.PROGRAMMING) { const programmingExercise = this.exercise as ProgrammingExercise; diff --git a/src/test/javascript/spec/component/participation/participation.component.spec.ts b/src/test/javascript/spec/component/participation/participation.component.spec.ts index 6661b643ed31..62f43f1f2013 100644 --- a/src/test/javascript/spec/component/participation/participation.component.spec.ts +++ b/src/test/javascript/spec/component/participation/participation.component.spec.ts @@ -125,7 +125,7 @@ describe('ParticipationComponent', () => { expect(exerciseFindStub).toHaveBeenCalledOnce(); expect(exerciseFindStub).toHaveBeenCalledWith(theExercise.id); expect(participationFindStub).toHaveBeenCalledOnce(); - expect(participationFindStub).toHaveBeenCalledWith(theExercise.id, true); + expect(participationFindStub).toHaveBeenCalledWith(theExercise.id, false); })); it('should initialize for programming exercise', fakeAsync(() => { @@ -154,7 +154,7 @@ describe('ParticipationComponent', () => { expect(exerciseFindStub).toHaveBeenCalledOnce(); expect(exerciseFindStub).toHaveBeenCalledWith(theExercise.id); expect(participationFindStub).toHaveBeenCalledOnce(); - expect(participationFindStub).toHaveBeenCalledWith(theExercise.id, true); + expect(participationFindStub).toHaveBeenCalledWith(theExercise.id, false); expect(submissionGetStateStub).toHaveBeenCalledOnce(); expect(submissionGetStateStub).toHaveBeenCalledWith(theExercise.id); })); From d2a82ea060e88b6acb046de86539a4c4add9f2ac Mon Sep 17 00:00:00 2001 From: Asli Aykan <56061820+asliayk@users.noreply.github.com> Date: Mon, 25 Nov 2024 21:24:17 +0100 Subject: [PATCH 3/8] Communication: Fix visibility of the edit message option for non-authors (#9830) --- .../answer-post/answer-post.component.html | 7 ++- .../answer-post/answer-post.component.ts | 11 ++-- .../app/shared/metis/post/post.component.html | 7 ++- .../app/shared/metis/post/post.component.ts | 11 ++-- .../answer-post-reactions-bar.component.html | 5 +- .../answer-post-reactions-bar.component.ts | 25 +++++++--- .../post-reactions-bar.component.html | 4 +- .../post-reactions-bar.component.ts | 27 ++++++---- ...nswer-post-reactions-bar.component.spec.ts | 23 ++++++++- .../post-reactions-bar.component.spec.ts | 50 +++++++++++++++++-- 10 files changed, 132 insertions(+), 38 deletions(-) diff --git a/src/main/webapp/app/shared/metis/answer-post/answer-post.component.html b/src/main/webapp/app/shared/metis/answer-post/answer-post.component.html index 6682f5c146c0..1579e97f9a0f 100644 --- a/src/main/webapp/app/shared/metis/answer-post/answer-post.component.html +++ b/src/main/webapp/app/shared/metis/answer-post/answer-post.component.html @@ -31,7 +31,8 @@ [isThreadSidebar]="isThreadSidebar" (openPostingCreateEditModal)="createAnswerPostModal.open()" (reactionsUpdated)="onReactionsUpdated($event)" - (mayEditOrDeleteOutput)="onMayEditOrDelete($event)" + (mayDeleteOutput)="onMayDelete($event)" + (mayEditOutput)="onMayEdit($event)" (isDeleteEvent)="onDeleteEvent(true)" />
@@ -63,11 +64,13 @@ - @if (mayEditOrDelete) { + @if (mayEdit) { + } + @if (mayDelete) { } - @if (mayEditOrDelete) { + @if (mayEdit) { + } + @if (mayDelete) { } - @if (mayEditOrDelete) { + @if (mayEdit) { - + } + @if (mayDelete) {