From 3baba5d90613585685a42e2f54ab34236394368b Mon Sep 17 00:00:00 2001 From: Michal Kawka Date: Sat, 16 Nov 2024 12:45:37 +0100 Subject: [PATCH 01/49] add count dtos --- .../core/dto/NonLatestNonRatedResultsCleanupCountDTO.java | 4 ++++ .../core/dto/NonLatestRatedResultsCleanupCountDTO.java | 4 ++++ .../tum/cit/aet/artemis/core/dto/OrphanCleanupCountDTO.java | 6 ++++++ .../core/dto/PlagiarismComparisonCleanupCountDTO.java | 4 ++++ 4 files changed, 18 insertions(+) create mode 100644 src/main/java/de/tum/cit/aet/artemis/core/dto/NonLatestNonRatedResultsCleanupCountDTO.java create mode 100644 src/main/java/de/tum/cit/aet/artemis/core/dto/NonLatestRatedResultsCleanupCountDTO.java create mode 100644 src/main/java/de/tum/cit/aet/artemis/core/dto/OrphanCleanupCountDTO.java create mode 100644 src/main/java/de/tum/cit/aet/artemis/core/dto/PlagiarismComparisonCleanupCountDTO.java diff --git a/src/main/java/de/tum/cit/aet/artemis/core/dto/NonLatestNonRatedResultsCleanupCountDTO.java b/src/main/java/de/tum/cit/aet/artemis/core/dto/NonLatestNonRatedResultsCleanupCountDTO.java new file mode 100644 index 000000000000..2303ed7b9d6b --- /dev/null +++ b/src/main/java/de/tum/cit/aet/artemis/core/dto/NonLatestNonRatedResultsCleanupCountDTO.java @@ -0,0 +1,4 @@ +package de.tum.cit.aet.artemis.core.dto; + +public record NonLatestNonRatedResultsCleanupCountDTO(int longFeedbackText, int textBlock, int feedback) { +} diff --git a/src/main/java/de/tum/cit/aet/artemis/core/dto/NonLatestRatedResultsCleanupCountDTO.java b/src/main/java/de/tum/cit/aet/artemis/core/dto/NonLatestRatedResultsCleanupCountDTO.java new file mode 100644 index 000000000000..71ac76bc24b4 --- /dev/null +++ b/src/main/java/de/tum/cit/aet/artemis/core/dto/NonLatestRatedResultsCleanupCountDTO.java @@ -0,0 +1,4 @@ +package de.tum.cit.aet.artemis.core.dto; + +public record NonLatestRatedResultsCleanupCountDTO(int longFeedbackText, int textBlock, int feedback) { +} diff --git a/src/main/java/de/tum/cit/aet/artemis/core/dto/OrphanCleanupCountDTO.java b/src/main/java/de/tum/cit/aet/artemis/core/dto/OrphanCleanupCountDTO.java new file mode 100644 index 000000000000..6037253d88c5 --- /dev/null +++ b/src/main/java/de/tum/cit/aet/artemis/core/dto/OrphanCleanupCountDTO.java @@ -0,0 +1,6 @@ +package de.tum.cit.aet.artemis.core.dto; + +public record OrphanCleanupCountDTO(int orphanFeedback, int orphanLongFeedbackText, int orphanTextBlock, int orphanStudentScore, int orphanTeamScore, + int orphanFeedbackForOrphanResults, int orphanLongFeedbackTextForOrphanResults, int orphanTextBlockForOrphanResults, int orphanRating, + int orphanResultsWithoutParticipation) { +} diff --git a/src/main/java/de/tum/cit/aet/artemis/core/dto/PlagiarismComparisonCleanupCountDTO.java b/src/main/java/de/tum/cit/aet/artemis/core/dto/PlagiarismComparisonCleanupCountDTO.java new file mode 100644 index 000000000000..c6d76ec79c5f --- /dev/null +++ b/src/main/java/de/tum/cit/aet/artemis/core/dto/PlagiarismComparisonCleanupCountDTO.java @@ -0,0 +1,4 @@ +package de.tum.cit.aet.artemis.core.dto; + +public record PlagiarismComparisonCleanupCountDTO(int plagiarismComparison, int plagiarismElements, int plagiarismSubmissions, int plagiarismMatches) { +} From c89b8317d6d14ab10cb84e9e58548111738f692d Mon Sep 17 00:00:00 2001 From: Michal Kawka Date: Sat, 16 Nov 2024 12:46:11 +0100 Subject: [PATCH 02/49] add count routes to AdminCleanupResource --- .../core/web/admin/AdminCleanupResource.java | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/src/main/java/de/tum/cit/aet/artemis/core/web/admin/AdminCleanupResource.java b/src/main/java/de/tum/cit/aet/artemis/core/web/admin/AdminCleanupResource.java index 60d865abdb0d..c9f03e6176af 100644 --- a/src/main/java/de/tum/cit/aet/artemis/core/web/admin/AdminCleanupResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/core/web/admin/AdminCleanupResource.java @@ -16,6 +16,10 @@ import org.springframework.web.bind.annotation.RestController; import de.tum.cit.aet.artemis.core.dto.CleanupServiceExecutionRecordDTO; +import de.tum.cit.aet.artemis.core.dto.NonLatestNonRatedResultsCleanupCountDTO; +import de.tum.cit.aet.artemis.core.dto.NonLatestRatedResultsCleanupCountDTO; +import de.tum.cit.aet.artemis.core.dto.OrphanCleanupCountDTO; +import de.tum.cit.aet.artemis.core.dto.PlagiarismComparisonCleanupCountDTO; import de.tum.cit.aet.artemis.core.security.annotations.EnforceAdmin; import de.tum.cit.aet.artemis.core.service.cleanup.DataCleanupService; @@ -50,6 +54,19 @@ public ResponseEntity deleteOrphans() { return ResponseEntity.ok().body(result); } + /** + * GET admin/cleanup/orphans/count + * Counts the number of orphaned data entries that would be deleted. + * + * @return a {@link ResponseEntity} containing the count of orphaned entries + */ + @GetMapping("orphans/count") + public ResponseEntity countOrphans() { + log.info("REST request to count orphaned data in Artemis database"); + OrphanCleanupCountDTO result = dataCleanupService.countOrphans(); + return ResponseEntity.ok().body(result); + } + /** * DELETE admin/cleanup/plagiarism-comparisons * Deletes plagiarism comparisons within the specified date range. @@ -66,6 +83,22 @@ public ResponseEntity deletePlagiarismComparis return ResponseEntity.ok().body(result); } + /** + * GET admin/cleanup/plagiarism-comparisons/count + * Counts the number of plagiarism comparisons and related entries that would be deleted within the specified date range. + * + * @param deleteFrom the start date of the counting range + * @param deleteTo the end date of the counting range + * @return a {@link ResponseEntity} containing the count of affected entries + */ + @GetMapping("plagiarism-comparisons/count") + public ResponseEntity countPlagiarismComparisons(@RequestParam("deleteFrom") ZonedDateTime deleteFrom, + @RequestParam("deleteTo") ZonedDateTime deleteTo) { + log.info("REST request to count plagiarism comparisons between {} and {}", deleteFrom, deleteTo); + PlagiarismComparisonCleanupCountDTO result = dataCleanupService.countPlagiarismComparisons(deleteFrom, deleteTo); + return ResponseEntity.ok().body(result); + } + /** * DELETE admin/cleanup/non-rated-results * Deletes non-rated results within the specified date range. @@ -82,6 +115,22 @@ public ResponseEntity deleteNonRatedResults(@R return ResponseEntity.ok().body(result); } + /** + * GET admin/cleanup/non-rated-results/count + * Counts the number of non-rated results and related entries that would be deleted within the specified date range. + * + * @param deleteFrom the start date of the counting range + * @param deleteTo the end date of the counting range + * @return a {@link ResponseEntity} containing the count of affected entries + */ + @GetMapping("non-rated-results/count") + public ResponseEntity countNonRatedResults(@RequestParam("deleteFrom") ZonedDateTime deleteFrom, + @RequestParam("deleteTo") ZonedDateTime deleteTo) { + log.info("REST request to count non-rated results between {} and {}", deleteFrom, deleteTo); + NonLatestNonRatedResultsCleanupCountDTO result = dataCleanupService.countNonLatestNonRatedResults(deleteFrom, deleteTo); + return ResponseEntity.ok().body(result); + } + /** * DELETE admin/cleanup/old-rated-results * Deletes old rated results within the specified date range. @@ -98,6 +147,22 @@ public ResponseEntity deleteOldRatedResults(@R return ResponseEntity.ok().body(result); } + /** + * GET admin/cleanup/old-rated-results/count + * Counts the number of old rated results and related entries that would be deleted within the specified date range. + * + * @param deleteFrom the start date of the counting range + * @param deleteTo the end date of the counting range + * @return a {@link ResponseEntity} containing the count of affected entries + */ + @GetMapping("old-rated-results/count") + public ResponseEntity countOldRatedResults(@RequestParam("deleteFrom") ZonedDateTime deleteFrom, + @RequestParam("deleteTo") ZonedDateTime deleteTo) { + log.info("REST request to count old rated results between {} and {}", deleteFrom, deleteTo); + NonLatestRatedResultsCleanupCountDTO result = dataCleanupService.countNonLatestRatedResults(deleteFrom, deleteTo); + return ResponseEntity.ok().body(result); + } + /** * GET admin/cleanup/last-executions * Retrieves the last execution records of the data cleanup operations. From a0a7ee4a3ea605bcde21434de95d76ee6172ff79 Mon Sep 17 00:00:00 2001 From: Michal Kawka Date: Sat, 16 Nov 2024 12:46:31 +0100 Subject: [PATCH 03/49] add count methods to FeedbackCleanupRepository --- .../cleanup/FeedbackCleanupRepository.java | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/repository/cleanup/FeedbackCleanupRepository.java b/src/main/java/de/tum/cit/aet/artemis/assessment/repository/cleanup/FeedbackCleanupRepository.java index e0fa96965e4a..47e6e2e2f406 100644 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/repository/cleanup/FeedbackCleanupRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/repository/cleanup/FeedbackCleanupRepository.java @@ -43,6 +43,23 @@ WHERE f.result IN ( """) int deleteFeedbackForOrphanResults(); + /** + * Counts {@link Feedback} entries where the associated {@link Result} has no submission and no participation. + * + * @return the number of entities that would be deleted + */ + @Query(""" + SELECT COUNT(f) + FROM Feedback f + WHERE f.result IN ( + SELECT r + FROM Result r + WHERE r.submission IS NULL + AND r.participation IS NULL + ) + """) + int countFeedbackForOrphanResults(); + /** * Deletes {@link Feedback} entries with a {@code null} result. * @@ -56,6 +73,18 @@ WHERE f.result IN ( """) int deleteOrphanFeedback(); + /** + * Counts {@link Feedback} entries with a {@code null} result. + * + * @return the number of entities that would be deleted + */ + @Query(""" + SELECT COUNT(f) + FROM Feedback f + WHERE f.result IS NULL + """) + int countOrphanFeedback(); + /** * Deletes {@link Feedback} entries associated with rated {@link Result} that are not the latest rated result * for a {@link Participation}, within courses conducted between the specified date range. @@ -89,6 +118,36 @@ SELECT MAX(r2.id) """) int deleteOldFeedbackThatAreNotLatestRatedResultsWhereCourseDateBetween(@Param("deleteFrom") ZonedDateTime deleteFrom, @Param("deleteTo") ZonedDateTime deleteTo); + /** + * Counts {@link Feedback} entries associated with rated {@link Result} that are not the latest rated result + * for a {@link Participation}, within courses conducted between the specified date range. + * + * @param deleteFrom the start date for selecting courses + * @param deleteTo the end date for selecting courses + * @return the number of entities that would be deleted + */ + @Query(""" + SELECT COUNT(f) + FROM Feedback f + WHERE f.result IN ( + SELECT r + FROM Result r + LEFT JOIN r.participation p + LEFT JOIN p.exercise e + LEFT JOIN e.course c + WHERE r.id NOT IN ( + SELECT MAX(r2.id) + FROM Result r2 + WHERE r2.participation.id = p.id + AND r2.rated = TRUE + ) + AND r.rated = TRUE + AND c.endDate < :deleteTo + AND c.startDate > :deleteFrom + ) + """) + int countOldFeedbackThatAreNotLatestRatedResultsWhereCourseDateBetween(@Param("deleteFrom") ZonedDateTime deleteFrom, @Param("deleteTo") ZonedDateTime deleteTo); + /** * Deletes non-rated {@link Feedback} entries that are not the latest non-rated result, where the associated course's start and end dates * are between the specified date range. @@ -120,4 +179,33 @@ SELECT MAX(r2.id) ) """) int deleteOldNonRatedFeedbackWhereCourseDateBetween(@Param("deleteFrom") ZonedDateTime deleteFrom, @Param("deleteTo") ZonedDateTime deleteTo); + + /** + * Counts non-rated {@link Feedback} entries that are not the latest non-rated result, where the associated course's start and end dates + * are between the specified date range. + * + * @param deleteFrom the start date for selecting courses + * @param deleteTo the end date for selecting courses + * @return the number of entities that would be deleted + */ + @Query(""" + SELECT COUNT(f) + FROM Feedback f + WHERE f.result IN ( + SELECT r + FROM Result r + LEFT JOIN r.participation p + LEFT JOIN p.exercise e + LEFT JOIN e.course c + WHERE r.id NOT IN ( + SELECT MAX(r2.id) + FROM Result r2 + WHERE r2.participation.id = p.id + ) + AND r.rated = FALSE + AND c.endDate < :deleteTo + AND c.startDate > :deleteFrom + ) + """) + int countOldNonRatedFeedbackWhereCourseDateBetween(@Param("deleteFrom") ZonedDateTime deleteFrom, @Param("deleteTo") ZonedDateTime deleteTo); } From a90946e3510f0c7bc177adb2fde7928062300565 Mon Sep 17 00:00:00 2001 From: Michal Kawka Date: Sat, 16 Nov 2024 12:46:53 +0100 Subject: [PATCH 04/49] add count methods to LongFeedbackTextCleanupRepository --- .../LongFeedbackTextCleanupRepository.java | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/repository/cleanup/LongFeedbackTextCleanupRepository.java b/src/main/java/de/tum/cit/aet/artemis/assessment/repository/cleanup/LongFeedbackTextCleanupRepository.java index 09dbf01baff2..f91bac77e415 100644 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/repository/cleanup/LongFeedbackTextCleanupRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/repository/cleanup/LongFeedbackTextCleanupRepository.java @@ -44,6 +44,24 @@ WHERE lft.feedback.id IN ( """) int deleteLongFeedbackTextForOrphanResult(); + /** + * Counts {@link LongFeedbackText} entries linked to {@link Feedback} where the associated + * {@link Result} has no participation and no submission. + * + * @return the number of entities that would be deleted + */ + @Query(""" + SELECT COUNT(lft) + FROM LongFeedbackText lft + WHERE lft.feedback.id IN ( + SELECT f.id + FROM Feedback f + WHERE f.result.participation IS NULL + AND f.result.submission IS NULL + ) + """) + int countLongFeedbackTextForOrphanResult(); + /** * Deletes {@link LongFeedbackText} linked to {@link Feedback} with a {@code null} result. * @@ -61,6 +79,22 @@ WHERE lft.feedback IN ( """) int deleteLongFeedbackTextForOrphanedFeedback(); + /** + * Counts {@link LongFeedbackText} linked to {@link Feedback} with a {@code null} result. + * + * @return the number of entities that would be deleted + */ + @Query(""" + SELECT COUNT(lft) + FROM LongFeedbackText lft + WHERE lft.feedback IN ( + SELECT f + FROM Feedback f + WHERE f.result IS NULL + ) + """) + int countLongFeedbackTextForOrphanedFeedback(); + /** * Deletes {@link LongFeedbackText} entries associated with rated {@link Result} that are not the latest rated result * for a {@link Participation}, within courses conducted between the specified date range. @@ -95,6 +129,37 @@ SELECT MAX(r2.id) """) int deleteLongFeedbackTextForRatedResultsWhereCourseDateBetween(@Param("deleteFrom") ZonedDateTime deleteFrom, @Param("deleteTo") ZonedDateTime deleteTo); + /** + * Counts {@link LongFeedbackText} entries associated with rated {@link Result} that are not the latest rated result + * for a {@link Participation}, within courses conducted between the specified date range. + * + * @param deleteFrom the start date for selecting courses + * @param deleteTo the end date for selecting courses + * @return the number of entities that would be deleted + */ + @Query(""" + SELECT COUNT(lft) + FROM LongFeedbackText lft + WHERE lft.feedback IN ( + SELECT f + FROM Feedback f + LEFT JOIN f.result r + LEFT JOIN r.participation p + LEFT JOIN p.exercise e + LEFT JOIN e.course c + WHERE f.result.id NOT IN ( + SELECT MAX(r2.id) + FROM Result r2 + WHERE r2.participation.id = p.id + AND r2.rated = TRUE + ) + AND c.endDate < :deleteTo + AND c.startDate > :deleteFrom + AND r.rated = TRUE + ) + """) + int countLongFeedbackTextForRatedResultsWhereCourseDateBetween(@Param("deleteFrom") ZonedDateTime deleteFrom, @Param("deleteTo") ZonedDateTime deleteTo); + /** * Deletes {@link LongFeedbackText} entries linked to non-rated {@link Feedback} that are not the latest non-rated result where the associated course's start * and end dates are between the specified date range. @@ -128,4 +193,35 @@ SELECT MAX(r2.id) ) """) int deleteLongFeedbackTextForNonRatedResultsWhereCourseDateBetween(@Param("deleteFrom") ZonedDateTime deleteFrom, @Param("deleteTo") ZonedDateTime deleteTo); + + /** + * Counts {@link LongFeedbackText} entries linked to non-rated {@link Feedback} that are not the latest non-rated result where the associated course's start + * and end dates are between the specified date range. + * + * @param deleteFrom the start date for selecting courses + * @param deleteTo the end date for selecting courses + * @return the number of entities that would be deleted + */ + @Query(""" + SELECT COUNT(lft) + FROM LongFeedbackText lft + WHERE lft.feedback IN ( + SELECT f + FROM Feedback f + LEFT JOIN f.result r + LEFT JOIN r.participation p + LEFT JOIN p.exercise e + LEFT JOIN e.course c + WHERE f.result.id NOT IN ( + SELECT MAX(r2.id) + FROM Result r2 + WHERE r2.participation.id = p.id + AND r2.rated = FALSE + ) + AND r.rated = FALSE + AND c.endDate < :deleteTo + AND c.startDate > :deleteFrom + ) + """) + int countLongFeedbackTextForNonRatedResultsWhereCourseDateBetween(@Param("deleteFrom") ZonedDateTime deleteFrom, @Param("deleteTo") ZonedDateTime deleteTo); } From 949762cfa63c1da11a4dafb9488e9e55f8558c30 Mon Sep 17 00:00:00 2001 From: Michal Kawka Date: Sat, 16 Nov 2024 12:47:10 +0100 Subject: [PATCH 05/49] add count methods to ParticipantScoreCleanupRepository --- .../ParticipantScoreCleanupRepository.java | 104 ++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/repository/cleanup/ParticipantScoreCleanupRepository.java b/src/main/java/de/tum/cit/aet/artemis/assessment/repository/cleanup/ParticipantScoreCleanupRepository.java index 50fcdd5b14b3..f4583759ccb5 100644 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/repository/cleanup/ParticipantScoreCleanupRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/repository/cleanup/ParticipantScoreCleanupRepository.java @@ -57,6 +57,32 @@ SELECT MAX(r2.id) """) int deleteParticipantScoresForNonLatestLastResultsWhereCourseDateBetween(@Param("deleteFrom") ZonedDateTime deleteFrom, @Param("deleteTo") ZonedDateTime deleteTo); + /** + * Counts {@link ParticipantScore} entries where the associated {@link Result} is not the latest rated result + * for a {@link Participation}, within courses conducted between the specified date range. + */ + @Query(""" + SELECT COUNT(ps) + FROM ParticipantScore ps + WHERE ps.lastResult IN ( + SELECT r + FROM Result r + LEFT JOIN r.participation p + LEFT JOIN p.exercise e + LEFT JOIN e.course c + WHERE r.id NOT IN ( + SELECT MAX(r2.id) + FROM Result r2 + WHERE r2.participation.id = p.id + AND r2.rated = TRUE + ) + AND r.rated = TRUE + AND c.endDate < :deleteTo + AND c.startDate > :deleteFrom + ) + """) + int countParticipantScoresForNonLatestLastResultsWhereCourseDateBetween(@Param("deleteFrom") ZonedDateTime deleteFrom, @Param("deleteTo") ZonedDateTime deleteTo); + /** * Deletes {@link ParticipantScore} entries where the associated last rated {@link Result} is not the latest rated result * for a {@link Participation}, within courses conducted between the specified date range. @@ -90,6 +116,32 @@ SELECT MAX(r2.id) """) int deleteParticipantScoresForNonLatestLastRatedResultsWhereCourseDateBetween(@Param("deleteFrom") ZonedDateTime deleteFrom, @Param("deleteTo") ZonedDateTime deleteTo); + /** + * Counts {@link ParticipantScore} entries where the associated last rated {@link Result} is not the latest rated result + * for a {@link Participation}, within courses conducted between the specified date range. + */ + @Query(""" + SELECT COUNT(ps) + FROM ParticipantScore ps + WHERE ps.lastRatedResult IN ( + SELECT r + FROM Result r + LEFT JOIN r.participation p + LEFT JOIN p.exercise e + LEFT JOIN e.course c + WHERE r.id NOT IN ( + SELECT MAX(r2.id) + FROM Result r2 + WHERE r2.participation.id = p.id + AND r2.rated = TRUE + ) + AND r.rated = TRUE + AND c.endDate < :deleteTo + AND c.startDate > :deleteFrom + ) + """) + int countParticipantScoresForNonLatestLastRatedResultsWhereCourseDateBetween(@Param("deleteFrom") ZonedDateTime deleteFrom, @Param("deleteTo") ZonedDateTime deleteTo); + /** * Deletes {@link ParticipantScore} entries where the associated {@link Result} is not the latest result and is non-rated, * and the course's start and end dates are between the specified date range. @@ -122,6 +174,32 @@ SELECT MAX(r2.id) """) int deleteParticipantScoresForLatestNonRatedResultsWhereCourseDateBetween(@Param("deleteFrom") ZonedDateTime deleteFrom, @Param("deleteTo") ZonedDateTime deleteTo); + /** + * Counts {@link ParticipantScore} entries where the associated {@link Result} is not the latest result and is non-rated, + * and the course's start and end dates are between the specified date range. + */ + @Query(""" + SELECT COUNT(ps) + FROM ParticipantScore ps + WHERE ps.lastResult IN ( + SELECT r + FROM Result r + LEFT JOIN r.participation p + LEFT JOIN p.exercise e + LEFT JOIN e.course c + WHERE r.id NOT IN ( + SELECT MAX(r2.id) + FROM Result r2 + WHERE r2.participation.id = p.id + AND r2.rated = FALSE + ) + AND r.rated = FALSE + AND c.endDate < :deleteTo + AND c.startDate > :deleteFrom + ) + """) + int countParticipantScoresForLatestNonRatedResultsWhereCourseDateBetween(@Param("deleteFrom") ZonedDateTime deleteFrom, @Param("deleteTo") ZonedDateTime deleteTo); + /** * Deletes {@link ParticipantScore} entries where the associated {@link Result} is not latest and is non-rated, even though * it is marked as the last rated result, to prevent potential integrity violations. @@ -155,4 +233,30 @@ SELECT MAX(r2.id) ) """) int deleteParticipantScoresForNonRatedResultsWhereCourseDateBetween(@Param("deleteFrom") ZonedDateTime deleteFrom, @Param("deleteTo") ZonedDateTime deleteTo); + + /** + * Counts {@link ParticipantScore} entries where the associated {@link Result} is not latest and is non-rated, even though + * it is marked as the last rated result. + */ + @Query(""" + SELECT COUNT(ps) + FROM ParticipantScore ps + WHERE ps.lastRatedResult IN ( + SELECT r + FROM Result r + LEFT JOIN r.participation p + LEFT JOIN p.exercise e + LEFT JOIN e.course c + WHERE r.id NOT IN ( + SELECT MAX(r2.id) + FROM Result r2 + WHERE r2.participation.id = p.id + AND r2.rated = FALSE + ) + AND r.rated = FALSE + AND c.endDate < :deleteTo + AND c.startDate > :deleteFrom + ) + """) + int countParticipantScoresForNonRatedResultsWhereCourseDateBetween(@Param("deleteFrom") ZonedDateTime deleteFrom, @Param("deleteTo") ZonedDateTime deleteTo); } From 0744a0ec477e269137515cf4c714530b1c6d174d Mon Sep 17 00:00:00 2001 From: Michal Kawka Date: Sat, 16 Nov 2024 12:47:28 +0100 Subject: [PATCH 06/49] add count methods to PlagiarismComparisonCleanupRepository --- ...PlagiarismComparisonCleanupRepository.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/repository/cleanup/PlagiarismComparisonCleanupRepository.java b/src/main/java/de/tum/cit/aet/artemis/assessment/repository/cleanup/PlagiarismComparisonCleanupRepository.java index a93ba9604223..e0742c3b71d9 100644 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/repository/cleanup/PlagiarismComparisonCleanupRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/repository/cleanup/PlagiarismComparisonCleanupRepository.java @@ -32,6 +32,13 @@ public interface PlagiarismComparisonCleanupRepository extends ArtemisJpaReposit """) int deleteByIdsIn(@Param("ids") List ids); + @Query(""" + SELECT COUNT(pc) + FROM PlagiarismComparison pc + WHERE pc.id IN :ids + """) + int countByIdsIn(@Param("ids") List ids); + @Modifying @Transactional // ok because of delete @Query(""" @@ -41,6 +48,13 @@ public interface PlagiarismComparisonCleanupRepository extends ArtemisJpaReposit """) int deletePlagiarismSubmissionElementsByComparisonIdsIn(@Param("ids") List ids); + @Query(""" + SELECT COUNT(e) + FROM PlagiarismSubmissionElement e + WHERE e.plagiarismSubmission.plagiarismComparison.id IN :ids + """) + int countPlagiarismSubmissionElementsByComparisonIdsIn(@Param("ids") List ids); + @Modifying @Transactional // ok because of delete @Query(""" @@ -50,6 +64,13 @@ public interface PlagiarismComparisonCleanupRepository extends ArtemisJpaReposit """) int deletePlagiarismSubmissionsByComparisonIdsIn(@Param("ids") List ids); + @Query(""" + SELECT COUNT(s) + FROM PlagiarismSubmission s + WHERE s.plagiarismComparison.id IN :ids + """) + int countPlagiarismSubmissionsByComparisonIdsIn(@Param("ids") List ids); + @Modifying @Transactional // ok because of modifying @Query(""" @@ -59,6 +80,13 @@ public interface PlagiarismComparisonCleanupRepository extends ArtemisJpaReposit """) int setPlagiarismSubmissionsToNullInComparisonsWithIds(@Param("ids") List ids); + @Query(""" + SELECT COUNT(pc) + FROM PlagiarismComparison pc + WHERE pc.id IN :ids AND (pc.submissionA IS NOT NULL OR pc.submissionB IS NOT NULL) + """) + int countPlagiarismSubmissionsNotNullInComparisonsWithIds(@Param("ids") List ids); + @Modifying @Transactional // ok because of delete @Query(nativeQuery = true, value = """ @@ -68,6 +96,13 @@ public interface PlagiarismComparisonCleanupRepository extends ArtemisJpaReposit """) int deletePlagiarismComparisonMatchesByComparisonIdsIn(@Param("ids") List ids); + @Query(nativeQuery = true, value = """ + SELECT COUNT(*) + FROM plagiarism_comparison_matches m + WHERE m.plagiarism_comparison_id IN :ids + """) + int countPlagiarismComparisonMatchesByComparisonIdsIn(@Param("ids") List ids); + /** * Retrieves a list of unnecessary plagiarism comparison IDs based on the associated course's date range. * A plagiarism comparison is considered unnecessary if its status is 'NONE' and the related course's From 1f8cf774d09d3af5f938be7547b27bdf85f907d1 Mon Sep 17 00:00:00 2001 From: Michal Kawka Date: Sat, 16 Nov 2024 12:48:52 +0100 Subject: [PATCH 07/49] remove redundant method --- .../PlagiarismComparisonCleanupRepository.java | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/repository/cleanup/PlagiarismComparisonCleanupRepository.java b/src/main/java/de/tum/cit/aet/artemis/assessment/repository/cleanup/PlagiarismComparisonCleanupRepository.java index e0742c3b71d9..543c6255639b 100644 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/repository/cleanup/PlagiarismComparisonCleanupRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/repository/cleanup/PlagiarismComparisonCleanupRepository.java @@ -32,13 +32,6 @@ public interface PlagiarismComparisonCleanupRepository extends ArtemisJpaReposit """) int deleteByIdsIn(@Param("ids") List ids); - @Query(""" - SELECT COUNT(pc) - FROM PlagiarismComparison pc - WHERE pc.id IN :ids - """) - int countByIdsIn(@Param("ids") List ids); - @Modifying @Transactional // ok because of delete @Query(""" @@ -80,13 +73,6 @@ SELECT COUNT(s) """) int setPlagiarismSubmissionsToNullInComparisonsWithIds(@Param("ids") List ids); - @Query(""" - SELECT COUNT(pc) - FROM PlagiarismComparison pc - WHERE pc.id IN :ids AND (pc.submissionA IS NOT NULL OR pc.submissionB IS NOT NULL) - """) - int countPlagiarismSubmissionsNotNullInComparisonsWithIds(@Param("ids") List ids); - @Modifying @Transactional // ok because of delete @Query(nativeQuery = true, value = """ From cd27c3358bb1df6dcab86bcadae2de83f9b3de13 Mon Sep 17 00:00:00 2001 From: Michal Kawka Date: Sat, 16 Nov 2024 12:49:17 +0100 Subject: [PATCH 08/49] add count methods in RatingCleanupRepository --- .../cleanup/RatingCleanupRepository.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/repository/cleanup/RatingCleanupRepository.java b/src/main/java/de/tum/cit/aet/artemis/assessment/repository/cleanup/RatingCleanupRepository.java index 07f10ec6d3d3..c923cffbcef6 100644 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/repository/cleanup/RatingCleanupRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/repository/cleanup/RatingCleanupRepository.java @@ -38,4 +38,21 @@ WHERE rt.result IN ( ) """) int deleteOrphanRating(); + + /** + * Counts {@link Rating} entries where the associated {@link Result} has no submission and no participation. + * + * @return the number of entities that would be deleted + */ + @Query(""" + SELECT COUNT(rt) + FROM Rating rt + WHERE rt.result IN ( + SELECT r + FROM Result r + WHERE r.submission IS NULL + AND r.participation IS NULL + ) + """) + int countOrphanRating(); } From 1ddb9f12289d28af76df4e623119302345ef3fd7 Mon Sep 17 00:00:00 2001 From: Michal Kawka Date: Sat, 16 Nov 2024 12:49:36 +0100 Subject: [PATCH 09/49] add count methods in ResultCleanupRepository --- .../cleanup/ResultCleanupRepository.java | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/repository/cleanup/ResultCleanupRepository.java b/src/main/java/de/tum/cit/aet/artemis/assessment/repository/cleanup/ResultCleanupRepository.java index 7d9e413aa5e7..7bf86fe0c730 100644 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/repository/cleanup/ResultCleanupRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/repository/cleanup/ResultCleanupRepository.java @@ -39,6 +39,19 @@ public interface ResultCleanupRepository extends ArtemisJpaRepository :deleteFrom + ) + AND r.id NOT IN ( + SELECT max_id + FROM ( + SELECT MAX(r2.id) AS max_id + FROM Result r2 + WHERE r2.rated = FALSE + GROUP BY r2.participation.id + ) + ) + """) + int countNonLatestNonRatedResultsWhereCourseDateBetween(@Param("deleteFrom") ZonedDateTime deleteFrom, @Param("deleteTo") ZonedDateTime deleteTo); + /** * Deletes rated {@link Result} entries that are not the latest rated result for a {@link Participation}, within courses * conducted between the specified date range. @@ -112,4 +159,38 @@ SELECT MAX(r2.id) AS max_id ) """) int deleteNonLatestRatedResultsWhereCourseDateBetween(@Param("deleteFrom") ZonedDateTime deleteFrom, @Param("deleteTo") ZonedDateTime deleteTo); + + /** + * Counts rated {@link Result} entries that are not the latest rated result for a {@link Participation}, within courses + * conducted between the specified date range. + * + * @param deleteFrom the start date for selecting courses + * @param deleteTo the end date for selecting courses + * @return the number of entities that would be deleted + */ + @Query(""" + SELECT COUNT(r) + FROM Result r + WHERE r.rated = TRUE + AND r.participation IS NOT NULL + AND r.participation.exercise IS NOT NULL + AND EXISTS ( + SELECT 1 + FROM Course c + LEFT JOIN c.exercises e + WHERE e = r.participation.exercise + AND c.endDate < :deleteTo + AND c.startDate > :deleteFrom + ) + AND r.id NOT IN ( + SELECT max_id + FROM ( + SELECT MAX(r2.id) AS max_id + FROM Result r2 + WHERE r2.rated = TRUE + GROUP BY r2.participation.id + ) + ) + """) + int countNonLatestRatedResultsWhereCourseDateBetween(@Param("deleteFrom") ZonedDateTime deleteFrom, @Param("deleteTo") ZonedDateTime deleteTo); } From 2443c4537f51e69905b57c9794ffc6dbc0b2dfc1 Mon Sep 17 00:00:00 2001 From: Michal Kawka Date: Sat, 16 Nov 2024 12:49:56 +0100 Subject: [PATCH 10/49] add count methods in StudentScoreCleanupRepository --- .../cleanup/StudentScoreCleanupRepository.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/repository/cleanup/StudentScoreCleanupRepository.java b/src/main/java/de/tum/cit/aet/artemis/assessment/repository/cleanup/StudentScoreCleanupRepository.java index d45bf8243e50..e7c679fe34cd 100644 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/repository/cleanup/StudentScoreCleanupRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/repository/cleanup/StudentScoreCleanupRepository.java @@ -31,4 +31,16 @@ public interface StudentScoreCleanupRepository extends ArtemisJpaRepository Date: Sat, 16 Nov 2024 12:50:15 +0100 Subject: [PATCH 11/49] add count methods in TeamScoreCleanupRepository --- .../cleanup/TeamScoreCleanupRepository.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/repository/cleanup/TeamScoreCleanupRepository.java b/src/main/java/de/tum/cit/aet/artemis/assessment/repository/cleanup/TeamScoreCleanupRepository.java index 2c1009f481b8..b1a17076fb47 100644 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/repository/cleanup/TeamScoreCleanupRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/repository/cleanup/TeamScoreCleanupRepository.java @@ -31,4 +31,16 @@ public interface TeamScoreCleanupRepository extends ArtemisJpaRepository Date: Sat, 16 Nov 2024 12:50:33 +0100 Subject: [PATCH 12/49] add count methods in TextBlockCleanupRepository --- .../cleanup/TextBlockCleanupRepository.java | 100 +++++++++++++++++- 1 file changed, 98 insertions(+), 2 deletions(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/repository/cleanup/TextBlockCleanupRepository.java b/src/main/java/de/tum/cit/aet/artemis/assessment/repository/cleanup/TextBlockCleanupRepository.java index 132fcc4f9726..8e2c7062515e 100644 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/repository/cleanup/TextBlockCleanupRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/repository/cleanup/TextBlockCleanupRepository.java @@ -45,6 +45,25 @@ WHERE tb.feedback IN ( """) int deleteTextBlockForOrphanResults(); + /** + * Counts {@link TextBlock} entries linked to {@link Feedback} where the associated {@link Result} + * has no submission and no participation. + * + * @return the number of entities that would be deleted + */ + @Query(""" + SELECT COUNT(tb) + FROM TextBlock tb + WHERE tb.feedback IN ( + SELECT f + FROM Feedback f + LEFT JOIN f.result r + WHERE r.submission IS NULL + AND r.participation IS NULL + ) + """) + int countTextBlockForOrphanResults(); + /** * Deletes {@link TextBlock} entries linked to {@link Feedback} with a {@code null} result. * @@ -62,11 +81,25 @@ WHERE tb.feedback IN ( """) int deleteTextBlockForEmptyFeedback(); + /** + * Counts {@link TextBlock} entries linked to {@link Feedback} with a {@code null} result. + * + * @return the number of entities that would be deleted + */ + @Query(""" + SELECT COUNT(tb) + FROM TextBlock tb + WHERE tb.feedback IN ( + SELECT f + FROM Feedback f + WHERE f.result IS NULL + ) + """) + int countTextBlockForEmptyFeedback(); + /** * Deletes {@link TextBlock} entries associated with rated {@link Result} that are not the latest rated result * for a {@link Participation}, within courses conducted between the specified date range. - * This query removes old text blocks that are not part of the latest rated results, for courses whose - * end date is before {@code deleteTo} and start date is after {@code deleteFrom}. * * @param deleteFrom the start date for selecting courses * @param deleteTo the end date for selecting courses @@ -96,6 +129,37 @@ SELECT MAX(r2.id) """) int deleteTextBlockForRatedResultsWhereCourseDateBetween(@Param("deleteFrom") ZonedDateTime deleteFrom, @Param("deleteTo") ZonedDateTime deleteTo); + /** + * Counts {@link TextBlock} entries associated with rated {@link Result} that are not the latest rated result + * for a {@link Participation}, within courses conducted between the specified date range. + * + * @param deleteFrom the start date for selecting courses + * @param deleteTo the end date for selecting courses + * @return the number of entities that would be deleted + */ + @Query(""" + SELECT COUNT(tb) + FROM TextBlock tb + WHERE tb.feedback IN ( + SELECT f + FROM Feedback f + LEFT JOIN f.result r + LEFT JOIN r.participation p + LEFT JOIN p.exercise e + LEFT JOIN e.course c + WHERE f.result.id NOT IN ( + SELECT MAX(r2.id) + FROM Result r2 + WHERE r2.participation.id = p.id + AND r2.rated = TRUE + ) + AND r.rated = TRUE + AND c.endDate < :deleteTo + AND c.startDate > :deleteFrom + ) + """) + int countTextBlockForRatedResultsWhereCourseDateBetween(@Param("deleteFrom") ZonedDateTime deleteFrom, @Param("deleteTo") ZonedDateTime deleteTo); + /** * Deletes {@link TextBlock} entries linked to non-rated {@link Result} that are not the latest non-rated result * for a {@link Participation}, where the associated course's start and end dates @@ -130,4 +194,36 @@ SELECT MAX(r2.id) ) """) int deleteTextBlockForNonRatedResultsWhereCourseDateBetween(@Param("deleteFrom") ZonedDateTime deleteFrom, @Param("deleteTo") ZonedDateTime deleteTo); + + /** + * Counts {@link TextBlock} entries linked to non-rated {@link Result} that are not the latest non-rated result + * for a {@link Participation}, where the associated course's start and end dates + * are between the specified date range. + * + * @param deleteFrom the start date for selecting courses + * @param deleteTo the end date for selecting courses + * @return the number of entities that would be deleted + */ + @Query(""" + SELECT COUNT(tb) + FROM TextBlock tb + WHERE tb.feedback IN ( + SELECT f + FROM Feedback f + LEFT JOIN f.result r + LEFT JOIN r.participation p + LEFT JOIN p.exercise e + LEFT JOIN e.course c + WHERE f.result.id NOT IN ( + SELECT MAX(r2.id) + FROM Result r2 + WHERE r2.participation.id = p.id + AND r2.rated = FALSE + ) + AND r.rated = FALSE + AND c.endDate < :deleteTo + AND c.startDate > :deleteFrom + ) + """) + int countTextBlockForNonRatedResultsWhereCourseDateBetween(@Param("deleteFrom") ZonedDateTime deleteFrom, @Param("deleteTo") ZonedDateTime deleteTo); } From 7610d882aec80463717bc1d4067b56bb88d45529 Mon Sep 17 00:00:00 2001 From: Michal Kawka Date: Sat, 16 Nov 2024 12:51:10 +0100 Subject: [PATCH 13/49] add methods to count affected entities to DataCleanupService --- .../service/cleanup/DataCleanupService.java | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/main/java/de/tum/cit/aet/artemis/core/service/cleanup/DataCleanupService.java b/src/main/java/de/tum/cit/aet/artemis/core/service/cleanup/DataCleanupService.java index a89a6f6ef207..611e616c7258 100644 --- a/src/main/java/de/tum/cit/aet/artemis/core/service/cleanup/DataCleanupService.java +++ b/src/main/java/de/tum/cit/aet/artemis/core/service/cleanup/DataCleanupService.java @@ -23,6 +23,10 @@ import de.tum.cit.aet.artemis.core.domain.CleanupJobExecution; import de.tum.cit.aet.artemis.core.domain.CleanupJobType; import de.tum.cit.aet.artemis.core.dto.CleanupServiceExecutionRecordDTO; +import de.tum.cit.aet.artemis.core.dto.NonLatestNonRatedResultsCleanupCountDTO; +import de.tum.cit.aet.artemis.core.dto.NonLatestRatedResultsCleanupCountDTO; +import de.tum.cit.aet.artemis.core.dto.OrphanCleanupCountDTO; +import de.tum.cit.aet.artemis.core.dto.PlagiarismComparisonCleanupCountDTO; import de.tum.cit.aet.artemis.core.repository.cleanup.CleanupJobExecutionRepository; @Profile(PROFILE_CORE) @@ -208,6 +212,80 @@ public CleanupServiceExecutionRecordDTO deleteNonLatestRatedResultsFeedback(Zone return CleanupServiceExecutionRecordDTO.of(createCleanupJobExecution(CleanupJobType.RATED_RESULTS, deleteFrom, deleteTo)); } + /** + * Counts orphaned entities that are no longer associated with valid results or participations. + * This includes feedback, text blocks, and scores that reference null results, participations, or submissions. + * + * @return an {@link OrphanCleanupCountDTO} representing the counts of orphaned entities that would be deleted + */ + public OrphanCleanupCountDTO countOrphans() { + int orphanFeedbackCount = feedbackCleanupRepository.countOrphanFeedback(); + int orphanLongFeedbackTextCount = longFeedbackTextCleanupRepository.countLongFeedbackTextForOrphanedFeedback(); + int orphanTextBlockCount = textBlockCleanupRepository.countTextBlockForEmptyFeedback(); + int orphanStudentScoreCount = studentScoreCleanupRepository.countOrphanStudentScore(); + int orphanTeamScoreCount = teamScoreCleanupRepository.countOrphanTeamScore(); + int orphanLongFeedbackTextForOrphanResultsCount = longFeedbackTextCleanupRepository.countLongFeedbackTextForOrphanResult(); + int orphanTextBlockForOrphanResultsCount = textBlockCleanupRepository.countTextBlockForOrphanResults(); + int orphanFeedbackForOrphanResultsCount = feedbackCleanupRepository.countFeedbackForOrphanResults(); + int orphanRatingCount = ratingCleanupRepository.countOrphanRating(); + int orphanResultsWithoutParticipationCount = resultCleanupRepository.countResultWithoutParticipationAndSubmission(); + + return new OrphanCleanupCountDTO(orphanFeedbackCount, orphanLongFeedbackTextCount, orphanTextBlockCount, orphanStudentScoreCount, orphanTeamScoreCount, + orphanFeedbackForOrphanResultsCount, orphanLongFeedbackTextForOrphanResultsCount, orphanTextBlockForOrphanResultsCount, orphanRatingCount, + orphanResultsWithoutParticipationCount); + } + + /** + * Counts plagiarism comparisons with a status of "None" that belong to courses within the specified date range. + * It retrieves the IDs of the plagiarism comparisons matching the criteria and counts the related data, + * including plagiarism elements, submissions, and matches. + * + * @param deleteFrom the start date for selecting plagiarism comparisons + * @param deleteTo the end date for selecting plagiarism comparisons + * @return a {@link PlagiarismComparisonCleanupCountDTO} representing the counts of entities related to plagiarism comparisons + */ + public PlagiarismComparisonCleanupCountDTO countPlagiarismComparisons(ZonedDateTime deleteFrom, ZonedDateTime deleteTo) { + var pcIds = plagiarismComparisonCleanupRepository.findPlagiarismComparisonIdWithStatusNoneThatBelongToCourseWithDates(deleteFrom, deleteTo); + int plagiarismComparisonCount = pcIds.size(); + int plagiarismElementsCount = plagiarismComparisonCleanupRepository.countPlagiarismSubmissionElementsByComparisonIdsIn(pcIds); + int plagiarismSubmissionsCount = plagiarismComparisonCleanupRepository.countPlagiarismSubmissionsByComparisonIdsIn(pcIds); + int plagiarismMatchesCount = plagiarismComparisonCleanupRepository.countPlagiarismComparisonMatchesByComparisonIdsIn(pcIds); + + return new PlagiarismComparisonCleanupCountDTO(plagiarismComparisonCount, plagiarismElementsCount, plagiarismSubmissionsCount, plagiarismMatchesCount); + } + + /** + * Counts non-rated results that are not the latest non-rated result for each participation, within the specified date range. + * This includes associated long feedback texts, text blocks, and feedback items that would be affected. + * + * @param deleteFrom The start of the date range for counting non-rated results. + * @param deleteTo The end of the date range for counting non-rated results. + * @return a {@link NonLatestNonRatedResultsCleanupCountDTO} representing the counts of entities related to non-latest non-rated results + */ + public NonLatestNonRatedResultsCleanupCountDTO countNonLatestNonRatedResults(ZonedDateTime deleteFrom, ZonedDateTime deleteTo) { + int longFeedbackTextCount = longFeedbackTextCleanupRepository.countLongFeedbackTextForNonRatedResultsWhereCourseDateBetween(deleteFrom, deleteTo); + int textBlockCount = textBlockCleanupRepository.countTextBlockForNonRatedResultsWhereCourseDateBetween(deleteFrom, deleteTo); + int feedbackCount = feedbackCleanupRepository.countOldNonRatedFeedbackWhereCourseDateBetween(deleteFrom, deleteTo); + + return new NonLatestNonRatedResultsCleanupCountDTO(longFeedbackTextCount, textBlockCount, feedbackCount); + } + + /** + * Counts rated results that are not the latest rated result for each participation, for courses conducted within the specified date range. + * This includes associated long feedback texts, text blocks, and feedback items that would be affected. + * + * @param deleteFrom The start of the date range for counting rated results. + * @param deleteTo The end of the date range for counting rated results. + * @return a {@link NonLatestRatedResultsCleanupCountDTO} representing the counts of entities related to non-latest rated results + */ + public NonLatestRatedResultsCleanupCountDTO countNonLatestRatedResults(ZonedDateTime deleteFrom, ZonedDateTime deleteTo) { + int longFeedbackTextCount = longFeedbackTextCleanupRepository.countLongFeedbackTextForRatedResultsWhereCourseDateBetween(deleteFrom, deleteTo); + int textBlockCount = textBlockCleanupRepository.countTextBlockForRatedResultsWhereCourseDateBetween(deleteFrom, deleteTo); + int feedbackCount = feedbackCleanupRepository.countOldFeedbackThatAreNotLatestRatedResultsWhereCourseDateBetween(deleteFrom, deleteTo); + + return new NonLatestRatedResultsCleanupCountDTO(longFeedbackTextCount, textBlockCount, feedbackCount); + } + /** * Retrieves the last execution record for each cleanup job type. * This method returns the most recent execution of each cleanup job type by querying From cbb287bf32fce8b8c36fb1e206a7fe35e2b6b51f Mon Sep 17 00:00:00 2001 From: Michal Kawka Date: Sat, 16 Nov 2024 12:52:07 +0100 Subject: [PATCH 14/49] add interfaces and http methods in the data-cleanup.service.ts --- .../cleanup-service/data-cleanup.service.ts | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/src/main/webapp/app/admin/cleanup-service/data-cleanup.service.ts b/src/main/webapp/app/admin/cleanup-service/data-cleanup.service.ts index 4782b5055674..2ba79cabbd8b 100644 --- a/src/main/webapp/app/admin/cleanup-service/data-cleanup.service.ts +++ b/src/main/webapp/app/admin/cleanup-service/data-cleanup.service.ts @@ -9,6 +9,42 @@ export interface CleanupServiceExecutionRecordDTO { jobType: string; } +export interface CleanupCount { + totalCount: number; +} + +export interface OrphanCleanupCountDTO extends CleanupCount { + orphanFeedback: number; + orphanLongFeedbackText: number; + orphanTextBlock: number; + orphanStudentScore: number; + orphanTeamScore: number; + orphanFeedbackForOrphanResults: number; + orphanLongFeedbackTextForOrphanResults: number; + orphanTextBlockForOrphanResults: number; + orphanRating: number; + orphanResultsWithoutParticipation: number; +} + +export interface PlagiarismComparisonCleanupCountDTO extends CleanupCount { + plagiarismComparison: number; + plagiarismElements: number; + plagiarismSubmissions: number; + plagiarismMatches: number; +} + +export interface NonLatestNonRatedResultsCleanupCountDTO extends CleanupCount { + longFeedbackText: number; + textBlock: number; + feedback: number; +} + +export interface NonLatestRatedResultsCleanupCountDTO extends CleanupCount { + longFeedbackText: number; + textBlock: number; + feedback: number; +} + @Injectable({ providedIn: 'root' }) export class DataCleanupService { private readonly adminResourceUrl = 'api/admin/cleanup'; @@ -75,4 +111,59 @@ export class DataCleanupService { observe: 'response', }); } + + /** + * Send GET request to count orphaned data. + * @returns An observable of type HttpResponse. + */ + countOrphans(): Observable> { + return this.http.get(`${this.adminResourceUrl}/orphans/count`, { + observe: 'response', + }); + } + + /** + * Send GET request to count plagiarism comparisons within a specific date range. + * @param deleteFrom the start date for counting + * @param deleteTo the end date for counting + * @returns An observable of type HttpResponse. + */ + countPlagiarismComparisons(deleteFrom: dayjs.Dayjs, deleteTo: dayjs.Dayjs): Observable> { + const deleteFromString = convertDateFromClient(deleteFrom)!; + const deleteToString = convertDateFromClient(deleteTo)!; + return this.http.get(`${this.adminResourceUrl}/plagiarism-comparisons/count`, { + params: { deleteFrom: deleteFromString, deleteTo: deleteToString }, + observe: 'response', + }); + } + + /** + * Send GET request to count non-rated results within a specific date range. + * @param deleteFrom the start date for counting + * @param deleteTo the end date for counting + * @returns An observable of type HttpResponse. + */ + countNonRatedResults(deleteFrom: dayjs.Dayjs, deleteTo: dayjs.Dayjs): Observable> { + const deleteFromString = convertDateFromClient(deleteFrom)!; + const deleteToString = convertDateFromClient(deleteTo)!; + return this.http.get(`${this.adminResourceUrl}/non-rated-results/count`, { + params: { deleteFrom: deleteFromString, deleteTo: deleteToString }, + observe: 'response', + }); + } + + /** + * Send GET request to count old rated results within a specific date range. + * @param deleteFrom the start date for counting + * @param deleteTo the end date for counting + * @returns An observable of type HttpResponse. + */ + countOldRatedResults(deleteFrom: dayjs.Dayjs, deleteTo: dayjs.Dayjs): Observable> { + const deleteFromString = convertDateFromClient(deleteFrom)!; + const deleteToString = convertDateFromClient(deleteTo)!; + return this.http.get(`${this.adminResourceUrl}/old-rated-results/count`, { + params: { deleteFrom: deleteFromString, deleteTo: deleteToString }, + observe: 'response', + }); + } } From 7bf9e5f71c00b4c47e7f7ad9eb3c12b57d3965cf Mon Sep 17 00:00:00 2001 From: Michal Kawka Date: Sat, 16 Nov 2024 12:52:34 +0100 Subject: [PATCH 15/49] add cleanup operation modal --- .../cleanup-operation-modal.component.html | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 src/main/webapp/app/admin/cleanup-service/cleanup-operation-modal.component.html diff --git a/src/main/webapp/app/admin/cleanup-service/cleanup-operation-modal.component.html b/src/main/webapp/app/admin/cleanup-service/cleanup-operation-modal.component.html new file mode 100644 index 000000000000..c183dc28cf6b --- /dev/null +++ b/src/main/webapp/app/admin/cleanup-service/cleanup-operation-modal.component.html @@ -0,0 +1,36 @@ + + + From 177e192ed21301977cd5c12834a56a005c628e11 Mon Sep 17 00:00:00 2001 From: Michal Kawka Date: Sat, 16 Nov 2024 12:53:53 +0100 Subject: [PATCH 16/49] add cleanup operation modal component --- .../cleanup-operation-modal.component.ts | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 src/main/webapp/app/admin/cleanup-service/cleanup-operation-modal.component.ts diff --git a/src/main/webapp/app/admin/cleanup-service/cleanup-operation-modal.component.ts b/src/main/webapp/app/admin/cleanup-service/cleanup-operation-modal.component.ts new file mode 100644 index 000000000000..445997dd88be --- /dev/null +++ b/src/main/webapp/app/admin/cleanup-service/cleanup-operation-modal.component.ts @@ -0,0 +1,114 @@ +import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; +import { Component, Input, OnInit, inject } from '@angular/core'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { CleanupOperation } from 'app/admin/cleanup-service/cleanup-operation.model'; +import { CleanupCount, CleanupServiceExecutionRecordDTO, DataCleanupService } from 'app/admin/cleanup-service/data-cleanup.service'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ArtemisSharedModule } from 'app/shared/shared.module'; +import { convertDateFromServer } from 'app/utils/date.utils'; +import { Observable, Observer, Subject } from 'rxjs'; +import { faCheckCircle, faTimes } from '@fortawesome/free-solid-svg-icons'; + +@Component({ + selector: 'jhi-cleanup-operation-modal', + templateUrl: './cleanup-operation-modal.component.html', + standalone: true, + imports: [TranslateDirective, ArtemisSharedModule], +}) +export class CleanupOperationModalComponent implements OnInit { + @Input() operation: CleanupOperation; + beforeExecutionCounts: CleanupCount; + afterExecutionCounts: CleanupCount | undefined = undefined; + + private dialogErrorSource = new Subject(); + dialogError = this.dialogErrorSource.asObservable(); + + public activeModal: NgbActiveModal = inject(NgbActiveModal); + private dataCleanupService: DataCleanupService = inject(DataCleanupService); + + // Icons + faTimes = faTimes; + faCheckCircle = faCheckCircle; + + cleanupKeys(counts: CleanupCount): (keyof CleanupCount)[] { + return Object.keys(counts) as (keyof CleanupCount)[]; + } + + close(): void { + this.activeModal.close(); + } + + ngOnInit(): void { + this.fetchOperationCounts(this.operation).subscribe({ + next: (response: HttpResponse) => { + this.beforeExecutionCounts = response.body!; + }, + error: () => this.dialogErrorSource.next('An unexpected error occurred.'), + }); + } + + executeCleanupOperation(): void { + const subscriptionHandler = this.handleResponse(this.operation); + + switch (this.operation.name) { + case 'deleteOrphans': + this.dataCleanupService.deleteOrphans().subscribe(subscriptionHandler); + break; + case 'deletePlagiarismComparisons': + this.dataCleanupService.deletePlagiarismComparisons(this.operation.deleteFrom, this.operation.deleteTo).subscribe(subscriptionHandler); + break; + case 'deleteNonRatedResults': + this.dataCleanupService.deleteNonRatedResults(this.operation.deleteFrom, this.operation.deleteTo).subscribe(subscriptionHandler); + break; + case 'deleteOldRatedResults': + this.dataCleanupService.deleteOldRatedResults(this.operation.deleteFrom, this.operation.deleteTo).subscribe(subscriptionHandler); + break; + } + } + + private fetchOperationCounts(operation: CleanupOperation): Observable> { + switch (operation.name) { + case 'deleteOrphans': + return this.dataCleanupService.countOrphans(); + case 'deletePlagiarismComparisons': + return this.dataCleanupService.countPlagiarismComparisons(operation.deleteFrom, operation.deleteTo); + case 'deleteNonRatedResults': + return this.dataCleanupService.countNonRatedResults(operation.deleteFrom, operation.deleteTo); + case 'deleteOldRatedResults': + return this.dataCleanupService.countOldRatedResults(operation.deleteFrom, operation.deleteTo); + default: + throw new Error(`Unsupported operation name: ${operation.name}`); + } + } + + private handleResponse(operation: CleanupOperation): Observer> { + return { + next: (response: HttpResponse) => { + this.dialogErrorSource.next(''); + const executionDateFromServer = convertDateFromServer(response.body!.executionDate); + operation.lastExecuted = executionDateFromServer; + this.fetchOperationCounts(operation).subscribe({ + next: (countResponse: HttpResponse) => { + if (this.afterExecutionCounts) { + this.beforeExecutionCounts = this.afterExecutionCounts; + } + this.afterExecutionCounts = countResponse.body!; + }, + error: () => { + this.dialogErrorSource.next('An error occurred while fetching post-execution counts.'); + }, + }); + }, + error: (error: any) => { + if (error instanceof HttpErrorResponse) { + this.dialogErrorSource.next(error.message); + } else { + this.dialogErrorSource.next('An unexpected error occurred.'); + } + }, + complete: () => {}, + }; + } + + protected readonly Object = Object; +} From ab18d1308e8ed39bf66bd13473e17be9eb0926c6 Mon Sep 17 00:00:00 2001 From: Michal Kawka Date: Sat, 16 Nov 2024 12:54:14 +0100 Subject: [PATCH 17/49] add tooltip in the cleanup-service --- .../cleanup-service.component.html | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/main/webapp/app/admin/cleanup-service/cleanup-service.component.html b/src/main/webapp/app/admin/cleanup-service/cleanup-service.component.html index aa1e021f066e..f53a572ae74c 100644 --- a/src/main/webapp/app/admin/cleanup-service/cleanup-service.component.html +++ b/src/main/webapp/app/admin/cleanup-service/cleanup-service.component.html @@ -18,6 +18,7 @@

+ @if (operation.name !== 'deleteOrphans') { @@ -60,17 +61,9 @@

} - + } From a2072207d1b936562175a4d71f9abccd7cd74ee6 Mon Sep 17 00:00:00 2001 From: Michal Kawka Date: Sat, 16 Nov 2024 12:54:42 +0100 Subject: [PATCH 18/49] move methods from cleanup-service.component.ts --- .../cleanup-service.component.ts | 54 +++++-------------- 1 file changed, 14 insertions(+), 40 deletions(-) diff --git a/src/main/webapp/app/admin/cleanup-service/cleanup-service.component.ts b/src/main/webapp/app/admin/cleanup-service/cleanup-service.component.ts index fab6e37e6217..ddc20979a251 100644 --- a/src/main/webapp/app/admin/cleanup-service/cleanup-service.component.ts +++ b/src/main/webapp/app/admin/cleanup-service/cleanup-service.component.ts @@ -3,23 +3,26 @@ import dayjs from 'dayjs/esm'; import { CleanupOperation } from 'app/admin/cleanup-service/cleanup-operation.model'; import { convertDateFromServer } from 'app/utils/date.utils'; import { Subject } from 'rxjs'; -import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; +import { HttpResponse } from '@angular/common/http'; import { CleanupServiceExecutionRecordDTO, DataCleanupService } from 'app/admin/cleanup-service/data-cleanup.service'; -import { Observer } from 'rxjs'; import { FormDateTimePickerModule } from 'app/shared/date-time-picker/date-time-picker.module'; import { ArtemisSharedModule } from 'app/shared/shared.module'; +import { ArtemisSharedComponentModule } from 'app/shared/components/shared-component.module'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { CleanupOperationModalComponent } from 'app/admin/cleanup-service/cleanup-operation-modal.component'; @Component({ selector: 'jhi-cleanup-service', templateUrl: './cleanup-service.component.html', standalone: true, - imports: [FormDateTimePickerModule, ArtemisSharedModule], + imports: [FormDateTimePickerModule, ArtemisSharedModule, ArtemisSharedComponentModule], }) export class CleanupServiceComponent implements OnInit { private dialogErrorSource = new Subject(); dialogError = this.dialogErrorSource.asObservable(); private dataCleanupService: DataCleanupService = inject(DataCleanupService); + private modalService: NgbModal = inject(NgbModal); cleanupOperations: CleanupOperation[] = [ { @@ -70,45 +73,16 @@ export class CleanupServiceComponent implements OnInit { }); } - executeCleanupOperation(operation: CleanupOperation): void { - const subscriptionHandler = this.handleResponse(operation); - - switch (operation.name) { - case 'deleteOrphans': - this.dataCleanupService.deleteOrphans().subscribe(subscriptionHandler); - break; - case 'deletePlagiarismComparisons': - this.dataCleanupService.deletePlagiarismComparisons(operation.deleteFrom, operation.deleteTo).subscribe(subscriptionHandler); - break; - case 'deleteNonRatedResults': - this.dataCleanupService.deleteNonRatedResults(operation.deleteFrom, operation.deleteTo).subscribe(subscriptionHandler); - break; - case 'deleteOldRatedResults': - this.dataCleanupService.deleteOldRatedResults(operation.deleteFrom, operation.deleteTo).subscribe(subscriptionHandler); - break; - } - } - - private handleResponse(operation: CleanupOperation): Observer> { - return { - next: (response: HttpResponse) => { - this.dialogErrorSource.next(''); - const executionDateFromServer = convertDateFromServer(response.body!.executionDate); - operation.lastExecuted = executionDateFromServer; - }, - error: (error: any) => { - if (error instanceof HttpErrorResponse) { - this.dialogErrorSource.next(error.message); - } else { - this.dialogErrorSource.next('An unexpected error occurred.'); - } - }, - complete: () => {}, - }; - } - validateDates(operation: CleanupOperation): void { const datesValid = operation.deleteFrom && operation.deleteTo && dayjs(operation.deleteTo).isAfter(dayjs(operation.deleteFrom)); operation.datesValid.set(datesValid); } + + /** + * Handles displaying the modal with operation details and counts. + */ + openCleanupOperationModal(operation: CleanupOperation): void { + const modalRef = this.modalService.open(CleanupOperationModalComponent, { size: 'lg', backdrop: 'static' }); + modalRef.componentInstance.operation = operation; + } } From 211cc9ebd4cc0bd8e37561e441adcc0835cad6ef Mon Sep 17 00:00:00 2001 From: Michal Kawka Date: Sat, 16 Nov 2024 12:55:09 +0100 Subject: [PATCH 19/49] add english translations --- src/main/webapp/i18n/en/cleanupService.json | 40 +++++++++++++++++++-- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/src/main/webapp/i18n/en/cleanupService.json b/src/main/webapp/i18n/en/cleanupService.json index 569ca3932aa1..40230bbecdcc 100644 --- a/src/main/webapp/i18n/en/cleanupService.json +++ b/src/main/webapp/i18n/en/cleanupService.json @@ -10,18 +10,52 @@ "operation": { "deleteOrphans": "Delete orphans", "deletePlagiarismComparisons": "Delete plagiarism comparisons", - "deleteNonRatedResults": "Delete non-rated results", - "deleteOldRatedResults": "Delete old rated results", + "deleteNonRatedResults": "Delete feedback of old non-rated results", + "deleteOldRatedResults": "Delete feedback of old rated results", + "deleteOldSubmissionVersions": "Delete old submission versions", + "deleteOldFeedback": "Delete old feedback versions" + }, + "tooltip": { + "deleteOrphans": "Deletes orphaned entities that are no longer associated with valid results or participations. This includes feedback, text blocks, and scores that reference null results, participations, or submissions", + "deletePlagiarismComparisons": "Deletes plagiarism comparisons with a status of \"None\" that belong to courses within the specified date range.", + "deleteNonRatedResults": "Deletes non-rated results, excluding the latest non-rated result for each participation (to be able to compute Competencies Scores), within the specified date range, along with associated long feedback texts, text blocks, feedback items, and participant scores.", + "deleteOldRatedResults": "Deletes rated results, excluding the latest rated result for each participation, for courses conducted within the specified date range. Also deletes associated long feedback texts, text blocks, feedback items, and participant scores.", "deleteOldSubmissionVersions": "Delete old submission versions", "deleteOldFeedback": "Delete old feedback versions" }, "execute": { - "question": "Are you sure you want to execute the operation {{ title }}? This action can NOT be undone!", + "question": "Are you sure you want to execute the operation {{ operationName }} for the period from {{deleteFrom}} to {{deleteTo}}? This action cannot be undone!? This action can NOT be undone!", "typeOperationNameToConfirm": "Please type in the name of the operation to confirm." }, + "modal": { + "affectedEntities": "This operation will delete the following entities:" + }, "notRunYet": "This operation has not been run yet", "error": { "datesInvalid": "'Delete to' must be after 'Delete from'" + }, + "entities": { + "orphanFeedback": "Orphan feedback", + "orphanLongFeedbackText": "Orphan long feedback text", + "orphanTextBlock": "Orphan text block", + "orphanStudentScore": "Orphan student score", + "orphanTeamScore": "Orphan team score", + "orphanFeedbackForOrphanResults": "Orphan feedback for orphan results", + "orphanLongFeedbackTextForOrphanResults": "Orphan long feedback text for orphan results", + "orphanTextBlockForOrphanResults": "Orphan text block for orphan results", + "orphanRating": "Orphan rating", + "orphanResultsWithoutParticipation": "Orphan results without participation", + "plagiarismComparison": "Plagiarism comparisons", + "plagiarismElements": "Plagiarism elements", + "plagiarismSubmissions": "Plagiarism submissions", + "plagiarismMatches": "Plagiarism matches", + "longFeedbackText": "Long feedback text", + "textBlock": "Text block", + "feedback": "Feedback" + }, + "button": { + "execute": "Execute", + "close": "Close" } } } From cee76927cf3d2e64f1f27f806ad9185c41b139f6 Mon Sep 17 00:00:00 2001 From: Michal Kawka Date: Tue, 26 Nov 2024 22:15:52 +0100 Subject: [PATCH 20/49] add german translations --- src/main/webapp/i18n/de/cleanupService.json | 38 ++++++++++++++++++++- src/main/webapp/i18n/en/cleanupService.json | 6 ++-- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/src/main/webapp/i18n/de/cleanupService.json b/src/main/webapp/i18n/de/cleanupService.json index aa509515921c..2eae50b5df54 100644 --- a/src/main/webapp/i18n/de/cleanupService.json +++ b/src/main/webapp/i18n/de/cleanupService.json @@ -15,13 +15,49 @@ "deleteOldSubmissionVersions": "Alte Teilnahmeversionen löschen", "deleteOldFeedback": "Altes Feedback löschen" }, + "tooltip": { + "deleteOrphans": "Löscht verwaiste Entitäten, die nicht mehr mit gültigen Ergebnissen oder Teilnahmen verbunden sind. Dazu gehören Feedback, Textblöcke und Bewertungen, die sich auf ungültige Ergebnisse, Teilnahmen oder Einreichungen beziehen.", + "deletePlagiarismComparisons": "Löscht Plagiatsvergleiche mit dem Status 'Keine', die zu Kursen innerhalb des angegebenen Datumsbereichs gehören.", + "deleteNonRatedResults": "Löscht nicht bewertete Ergebnisse, mit Ausnahme des neuesten nicht bewerteten Ergebnisses für jede Teilnahme (um Kompetenzergebnisse berechnen zu können), innerhalb des angegebenen Datumsbereichs, zusammen mit zugehörigen langen Feedback-Texten, Textblöcken, Feedback-Elementen und Teilnehmerbewertungen.", + "deleteOldRatedResults": "Löscht bewertete Ergebnisse, mit Ausnahme des neuesten bewerteten Ergebnisses für jede Teilnahme, für Kurse, die innerhalb des angegebenen Datumsbereichs durchgeführt wurden. Löscht auch zugehörige lange Feedback-Texte, Textblöcke, Feedback-Elemente und Teilnehmerbewertungen.", + "deleteOldSubmissionVersions": "Löscht alte Teilnahmeversionen.", + "deleteOldFeedback": "Löscht alte Feedback-Versionen." + }, "execute": { - "question": "Bist du sicher, dass du die Operation {{ title }} ausführen möchtest? Diese Aktion kann NICHT rückgängig gemacht werden!", + "question": "Bist du sicher, dass du die Operation {{ operationName }} für den Zeitraum von {{deleteFrom}} bis {{deleteTo}} ausführen möchtest? Diese Aktion kann NICHT rückgängig gemacht werden!", + "questionWithoutDates": "Bist du sicher, dass du die Operation {{ operationName }} ausführen möchtest? Diese Aktion kann NICHT rückgängig gemacht werden!", "typeOperationNameToConfirm": "Bitte gib den Namen der Operation ein, um zu bestätigen." }, + "modal": { + "affectedEntities": "Diese Operation wird die folgenden Entitäten löschen:", + "noEntriesToDelete": "Keine Einträge zum Löschen vorhanden." + }, "notRunYet": "Diese Operation wurde noch nicht ausgeführt", "error": { "datesInvalid": "'Löschen bis' muss nach 'Löschen von' liegen" + }, + "entities": { + "orphanFeedback": "Verwaistes Feedback", + "orphanLongFeedbackText": "Verwaister langer Feedback-Text", + "orphanTextBlock": "Verwaister Textblock", + "orphanStudentScore": "Verwaiste Studentenbewertung", + "orphanTeamScore": "Verwaiste Teambewertung", + "orphanFeedbackForOrphanResults": "Verwaistes Feedback für verwaiste Ergebnisse", + "orphanLongFeedbackTextForOrphanResults": "Verwaister langer Feedback-Text für verwaiste Ergebnisse", + "orphanTextBlockForOrphanResults": "Verwaister Textblock für verwaiste Ergebnisse", + "orphanRating": "Verwaiste Bewertung", + "orphanResultsWithoutParticipation": "Verwaiste Ergebnisse ohne Teilnahme", + "plagiarismComparison": "Plagiatsvergleiche", + "plagiarismElements": "Plagiatselemente", + "plagiarismSubmissions": "Plagiatseingaben", + "plagiarismMatches": "Plagiatsübereinstimmungen", + "longFeedbackText": "Langer Feedback-Text", + "textBlock": "Textblock", + "feedback": "Feedback" + }, + "button": { + "execute": "Ausführen", + "close": "Schließen" } } } diff --git a/src/main/webapp/i18n/en/cleanupService.json b/src/main/webapp/i18n/en/cleanupService.json index 40230bbecdcc..601af4cb3fc7 100644 --- a/src/main/webapp/i18n/en/cleanupService.json +++ b/src/main/webapp/i18n/en/cleanupService.json @@ -24,11 +24,13 @@ "deleteOldFeedback": "Delete old feedback versions" }, "execute": { - "question": "Are you sure you want to execute the operation {{ operationName }} for the period from {{deleteFrom}} to {{deleteTo}}? This action cannot be undone!? This action can NOT be undone!", + "question": "Are you sure you want to execute the operation {{ operationName }} for the period from {{deleteFrom}} to {{deleteTo}}? This action cannot be undone!", + "questionWithoutDates": "Are you sure you want to execute the operation {{ operationName }}? This action cannot be undone!", "typeOperationNameToConfirm": "Please type in the name of the operation to confirm." }, "modal": { - "affectedEntities": "This operation will delete the following entities:" + "affectedEntities": "This operation will delete the following entities:", + "noEntriesToDelete": "No entries to delete." }, "notRunYet": "This operation has not been run yet", "error": { From be7224cd59b2227d748687bf4db92ff1d236abfd Mon Sep 17 00:00:00 2001 From: Michal Kawka Date: Tue, 26 Nov 2024 22:16:12 +0100 Subject: [PATCH 21/49] disable button if there are no entities to delete --- .../cleanup-operation-modal.component.html | 42 ++++--- .../cleanup-operation-modal.component.ts | 104 +++++++++--------- 2 files changed, 81 insertions(+), 65 deletions(-) diff --git a/src/main/webapp/app/admin/cleanup-service/cleanup-operation-modal.component.html b/src/main/webapp/app/admin/cleanup-service/cleanup-operation-modal.component.html index c183dc28cf6b..99149b61e4a0 100644 --- a/src/main/webapp/app/admin/cleanup-service/cleanup-operation-modal.component.html +++ b/src/main/webapp/app/admin/cleanup-service/cleanup-operation-modal.component.html @@ -3,34 +3,46 @@