From 395e8dfaefd4caca28e7a47d3791c3090582b2d8 Mon Sep 17 00:00:00 2001 From: Aniruddh Zaveri <92953467+az108@users.noreply.github.com> Date: Sun, 10 Nov 2024 17:30:15 +0100 Subject: [PATCH] Programming exercises: Add error categories and categorize feedback in grading analysis (#9622) --- .../dto/FeedbackAnalysisResponseDTO.java | 4 +- .../assessment/dto/FeedbackDetailDTO.java | 2 +- .../assessment/dto/FeedbackPageableDTO.java | 10 ++ .../assessment/service/ResultService.java | 89 ++++++----- .../assessment/web/ResultResource.java | 65 ++++---- .../cit/aet/artemis/core/util/PageUtil.java | 9 +- .../StudentParticipationRepository.java | 107 +++++++------ .../feedback-filter-modal.component.html | 57 ++++--- .../Modal/feedback-filter-modal.component.ts | 13 +- .../Modal/feedback-modal.component.html | 24 +-- .../Modal/feedback-modal.component.ts | 1 + .../feedback-analysis.component.html | 86 ++++++----- .../feedback-analysis.component.ts | 40 +++-- .../feedback-analysis.service.ts | 8 +- .../app/shared/sort/sort-icon.component.html | 4 + .../app/shared/sort/sort-icon.component.scss | 7 + .../app/shared/sort/sort-icon.component.ts | 22 +++ .../webapp/i18n/de/programmingExercise.json | 3 + .../webapp/i18n/en/programmingExercise.json | 3 + .../ResultServiceIntegrationTest.java | 8 +- .../feedback-analysis.component.spec.ts | 143 ++++++++++-------- .../feedback-analysis.service.spec.ts | 8 +- .../feedback-filter-modal.component.spec.ts | 23 ++- .../modals/feedback-modal.component.spec.ts | 2 +- .../util/shared/sort-icon.component.spec.ts | 39 +++++ 25 files changed, 498 insertions(+), 279 deletions(-) create mode 100644 src/main/webapp/app/shared/sort/sort-icon.component.html create mode 100644 src/main/webapp/app/shared/sort/sort-icon.component.scss create mode 100644 src/main/webapp/app/shared/sort/sort-icon.component.ts create mode 100644 src/test/javascript/spec/util/shared/sort-icon.component.spec.ts diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/dto/FeedbackAnalysisResponseDTO.java b/src/main/java/de/tum/cit/aet/artemis/assessment/dto/FeedbackAnalysisResponseDTO.java index d913f0c96e3f..e56722f079cf 100644 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/dto/FeedbackAnalysisResponseDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/dto/FeedbackAnalysisResponseDTO.java @@ -1,11 +1,13 @@ package de.tum.cit.aet.artemis.assessment.dto; import java.util.List; +import java.util.Set; import com.fasterxml.jackson.annotation.JsonInclude; import de.tum.cit.aet.artemis.core.dto.SearchResultPageDTO; @JsonInclude(JsonInclude.Include.NON_EMPTY) -public record FeedbackAnalysisResponseDTO(SearchResultPageDTO feedbackDetails, long totalItems, int totalAmountOfTasks, List testCaseNames) { +public record FeedbackAnalysisResponseDTO(SearchResultPageDTO feedbackDetails, long totalItems, Set taskNames, List testCaseNames, + List errorCategories) { } diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/dto/FeedbackDetailDTO.java b/src/main/java/de/tum/cit/aet/artemis/assessment/dto/FeedbackDetailDTO.java index 7b3fd09ad57d..6d31e250f5d5 100644 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/dto/FeedbackDetailDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/dto/FeedbackDetailDTO.java @@ -3,5 +3,5 @@ import com.fasterxml.jackson.annotation.JsonInclude; @JsonInclude(JsonInclude.Include.NON_EMPTY) -public record FeedbackDetailDTO(long count, double relativeCount, String detailText, String testCaseName, String taskNumber, String errorCategory) { +public record FeedbackDetailDTO(long count, double relativeCount, String detailText, String testCaseName, String taskName, String errorCategory) { } diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/dto/FeedbackPageableDTO.java b/src/main/java/de/tum/cit/aet/artemis/assessment/dto/FeedbackPageableDTO.java index c63f9b5540f7..b65d09545fc2 100644 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/dto/FeedbackPageableDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/dto/FeedbackPageableDTO.java @@ -14,6 +14,8 @@ public class FeedbackPageableDTO extends PageableSearchDTO { private String searchTerm; + private List filterErrorCategories; + public List getFilterTasks() { return filterTasks; } @@ -45,4 +47,12 @@ public String getSearchTerm() { public void setSearchTerm(String searchTerm) { this.searchTerm = searchTerm; } + + public List getFilterErrorCategories() { + return filterErrorCategories; + } + + public void setFilterErrorCategories(List filterErrorCategories) { + this.filterErrorCategories = filterErrorCategories; + } } diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/service/ResultService.java b/src/main/java/de/tum/cit/aet/artemis/assessment/service/ResultService.java index 21cfc87c6c31..d0c25898b6a3 100644 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/service/ResultService.java +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/service/ResultService.java @@ -547,73 +547,86 @@ private Result shouldSaveResult(@NotNull Result result, boolean shouldSave) { } /** - * Retrieves paginated and filtered aggregated feedback details for a given exercise. + * Retrieves paginated and filtered aggregated feedback details for a given exercise, including the count of each unique feedback detail text, + * test case name, task name, and error category. *
* For each feedback detail: - * 1. The relative count is calculated as a percentage of the total number of distinct results for the exercise. - * 2. The task numbers are assigned based on the associated test case names. A mapping between test cases and tasks is created using the set of tasks retrieved from the - * database. + * 1. The relative count is calculated as a percentage of the total distinct results for the exercise. + * 2. Task names are assigned based on associated test case names, with a mapping created between test cases and tasks from the exercise database. + * Feedback items not assigned to any task are labeled as "Not assigned to a task." + * 3. Error categories are classified as one of "Student Error," "Ares Error," or "AST Error," based on feedback content. *
- * Filtering: - * - **Search term**: Filters feedback details by the search term (case-insensitive). - * - **Test case names**: Filters feedback based on specific test case names (if provided). - * - **Task names**: Maps provided task numbers to task names and filters feedback based on the test cases associated with those tasks. - * - **Occurrences**: Filters feedback where the number of occurrences (COUNT) is between the provided minimum and maximum values (inclusive). + * It supports filtering by: + * - Search term: Case-insensitive filtering on feedback detail text. + * - Test case names: Filters feedback based on specific test case names. Only active test cases are included in the filtering options. + * - Task names: Filters feedback based on specified task names and includes unassigned tasks if "Not assigned to a task" is selected. + * - Occurrence range: Filters feedback where the number of occurrences (COUNT) is within the specified minimum and maximum range. + * - Error categories: Filters feedback based on selected error categories, such as "Student Error," "Ares Error," and "AST Error." *
* Pagination and sorting: * - Sorting is applied based on the specified column and order (ascending or descending). - * - The result is paginated based on the provided page number and page size. + * - The result is paginated according to the provided page number and page size. * * @param exerciseId The ID of the exercise for which feedback details should be retrieved. - * @param data The {@link FeedbackPageableDTO} containing page number, page size, search term, sorting options, and filtering parameters (task names, test cases, - * occurrence range). + * @param data The {@link FeedbackPageableDTO} containing page number, page size, search term, sorting options, and filtering parameters + * (task names, test cases, occurrence range, error categories). * @return A {@link FeedbackAnalysisResponseDTO} object containing: * - A {@link SearchResultPageDTO} of paginated feedback details. * - The total number of distinct results for the exercise. - * - The total number of tasks associated with the feedback. - * - A list of test case names included in the feedback. + * - A set of task names, including "Not assigned to a task" if applicable. + * - A list of active test case names used in the feedback. + * - A list of predefined error categories ("Student Error," "Ares Error," "AST Error") available for filtering. */ public FeedbackAnalysisResponseDTO getFeedbackDetailsOnPage(long exerciseId, FeedbackPageableDTO data) { // 1. Fetch programming exercise with associated test cases ProgrammingExercise programmingExercise = programmingExerciseRepository.findWithTestCasesByIdElseThrow(exerciseId); + // 2. Get the distinct count of results for calculating relative feedback counts long distinctResultCount = studentParticipationRepository.countDistinctResultsByExerciseId(exerciseId); - // 2. Extract test case names using streams - List testCaseNames = programmingExercise.getTestCases().stream().map(ProgrammingExerciseTestCase::getTestName).toList(); + // 3. Extract only active test case names for use in filtering options + List activeTestCaseNames = programmingExercise.getTestCases().stream().filter(ProgrammingExerciseTestCase::isActive).map(ProgrammingExerciseTestCase::getTestName) + .toList(); + // 4. Retrieve all tasks associated with the exercise and map their names List tasks = programmingExerciseTaskService.getTasksWithUnassignedTestCases(exerciseId); + Set taskNames = tasks.stream().map(ProgrammingExerciseTask::getTaskName).collect(Collectors.toSet()); - // 3. Generate filter task names directly - List filterTaskNames = data.getFilterTasks().stream().map(index -> { - int idx = Integer.parseInt(index); - return (idx > 0 && idx <= tasks.size()) ? tasks.get(idx - 1).getTaskName() : null; - }).filter(Objects::nonNull).toList(); + // 5. Include unassigned tasks if specified by the filter; otherwise, only include specified tasks + List includeUnassignedTasks = new ArrayList<>(taskNames); + if (!data.getFilterTasks().isEmpty()) { + includeUnassignedTasks.removeAll(data.getFilterTasks()); + } + else { + includeUnassignedTasks.clear(); + } - // 4. Set minOccurrence and maxOccurrence based on filterOccurrence + // 6. Define the occurrence range based on filter parameters long minOccurrence = data.getFilterOccurrence().length == 2 ? Long.parseLong(data.getFilterOccurrence()[0]) : 0; long maxOccurrence = data.getFilterOccurrence().length == 2 ? Long.parseLong(data.getFilterOccurrence()[1]) : Integer.MAX_VALUE; - // 5. Create pageable object for pagination + // 7. Define the error categories to filter based on user selection + List filterErrorCategories = data.getFilterErrorCategories(); + + // 8. Set up pagination and sorting based on input data final var pageable = PageUtil.createDefaultPageRequest(data, PageUtil.ColumnMapping.FEEDBACK_ANALYSIS); - // 6. Fetch filtered feedback from the repository + // 9. Query the database to retrieve paginated and filtered feedback final Page feedbackDetailPage = studentParticipationRepository.findFilteredFeedbackByExerciseId(exerciseId, - StringUtils.isBlank(data.getSearchTerm()) ? "" : data.getSearchTerm().toLowerCase(), data.getFilterTestCases(), filterTaskNames, minOccurrence, maxOccurrence, - pageable); - - // 7. Process feedback details - // Map to index (+1 for 1-based indexing) - List processedDetails = feedbackDetailPage.getContent().stream().map(detail -> { - String taskIndex = tasks.stream().filter(task -> task.getTaskName().equals(detail.taskNumber())).findFirst().map(task -> String.valueOf(tasks.indexOf(task) + 1)) - .orElse("0"); - return new FeedbackDetailDTO(detail.count(), (detail.count() * 100.00) / distinctResultCount, detail.detailText(), detail.testCaseName(), taskIndex, "StudentError"); - }).toList(); - - // 8. Return the response DTO containing feedback details, total elements, and test case/task info - return new FeedbackAnalysisResponseDTO(new SearchResultPageDTO<>(processedDetails, feedbackDetailPage.getTotalPages()), feedbackDetailPage.getTotalElements(), tasks.size(), - testCaseNames); + StringUtils.isBlank(data.getSearchTerm()) ? "" : data.getSearchTerm().toLowerCase(), data.getFilterTestCases(), includeUnassignedTasks, minOccurrence, + maxOccurrence, filterErrorCategories, pageable); + + // 10. Process and map feedback details, calculating relative count and assigning task names + List processedDetails = feedbackDetailPage.getContent().stream().map(detail -> new FeedbackDetailDTO(detail.count(), + (detail.count() * 100.00) / distinctResultCount, detail.detailText(), detail.testCaseName(), detail.taskName(), detail.errorCategory())).toList(); + + // 11. Predefined error categories available for filtering on the client side + final List ERROR_CATEGORIES = List.of("Student Error", "Ares Error", "AST Error"); + + // 12. Return response containing processed feedback details, task names, active test case names, and error categories + return new FeedbackAnalysisResponseDTO(new SearchResultPageDTO<>(processedDetails, feedbackDetailPage.getTotalPages()), feedbackDetailPage.getTotalElements(), taskNames, + activeTestCaseNames, ERROR_CATEGORIES); } /** diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/web/ResultResource.java b/src/main/java/de/tum/cit/aet/artemis/assessment/web/ResultResource.java index 5e28aa48b288..2a235145a04b 100644 --- a/src/main/java/de/tum/cit/aet/artemis/assessment/web/ResultResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/assessment/web/ResultResource.java @@ -283,36 +283,49 @@ public ResponseEntity createResultForExternalSubmission(@PathVariable Lo } /** - * GET /exercises/{exerciseId}/feedback-details : Retrieves paginated and filtered aggregated feedback details for a given exercise. - * The feedback details include counts and relative counts of feedback occurrences, test case names, and task numbers. - * The method allows filtering by a search term and sorting by various fields. + * GET /exercises/{exerciseId}/feedback-details : Retrieves paginated and filtered aggregated feedback details for a specified exercise. *
- * Pagination is applied based on the provided query parameters, including page number, page size, sorting order, and search term. - * Sorting is applied by the specified sorted column and sorting order. If the provided sorted column is not valid for sorting (e.g., "taskNumber" or "errorCategory"), - * the sorting defaults to "count". + * This endpoint provides detailed feedback analytics, including: + * - The count and relative count (percentage) of each unique feedback entry. + * - Associated test case names. + * - Task names, mapped from test cases. *
- * Filtering is applied based on: - * - Task numbers (mapped to task names) - * - Test case names - * - Occurrence range (minimum and maximum occurrences) - *
- * The response contains both the paginated feedback details and the total count of distinct results for the exercise. + * Pagination, sorting, and filtering options allow flexible data retrieval: + *
    + *
  • Pagination: Based on page number and page size, as specified in the request.
  • + *
  • Sorting: By column (e.g., "count" or "detailText") and sorting order (ASCENDING or DESCENDING). + * If the specified column is not valid for sorting, the default sorting column is "count".
  • + *
  • Filtering: + *
      + *
    • Task names: Filters feedback entries by specific task names, including "Not assigned to task" if unassigned feedback is requested.
    • + *
    • Test case names: Filters feedback by specified test cases, using only active test cases from the exercise.
    • + *
    • Occurrence range: Filters by the minimum and maximum number of occurrences (inclusive).
    • + *
    • Search term: Case-insensitive filter applied to feedback detail text.
    • + *
    • Error categories: Filters feedback entries by specified error categories (e.g., "Student Error," "Ares Error," and "AST Error").
    • + *
    + *
  • + *
* - * @param exerciseId The ID of the exercise for which feedback details should be retrieved. - * @param data A {@link FeedbackPageableDTO} object containing pagination and filtering parameters, such as: - * - Page number - * - Page size - * - Search term (optional) - * - Sorting order (ASCENDING or DESCENDING) - * - Sorted column - * - Filter task numbers (optional) - * - Filter test case names (optional) - * - Occurrence range (optional) + * @param exerciseId The unique identifier of the exercise for which feedback details are requested. + * @param data A {@link FeedbackPageableDTO} object containing pagination, sorting, and filtering parameters, including: + *
    + *
  • Page number and page size
  • + *
  • Search term (optional)
  • + *
  • Sorting order (ASCENDING or DESCENDING)
  • + *
  • Sorted column
  • + *
  • Filter task names (optional)
  • + *
  • Filter test case names (optional)
  • + *
  • Occurrence range (optional)
  • + *
  • Error categories (optional)
  • + *
* @return A {@link ResponseEntity} containing a {@link FeedbackAnalysisResponseDTO}, which includes: - * - {@link SearchResultPageDTO < FeedbackDetailDTO >} feedbackDetails: Paginated feedback details for the exercise. - * - long totalItems: The total number of feedback items (used for pagination). - * - int totalAmountOfTasks: The total number of tasks associated with the feedback. - * - List testCaseNames: A list of test case names included in the feedback. + *
    + *
  • {@link SearchResultPageDTO < FeedbackDetailDTO >} feedbackDetails: Paginated and filtered feedback details for the exercise.
  • + *
  • long totalItems: The total count of feedback entries (for pagination).
  • + *
  • Set taskNames: A set of task names relevant to the feedback items, including "Not assigned to task" if applicable.
  • + *
  • List testCaseNames: A list of active test case names used in the feedback.
  • + *
  • List errorCategories: The list of error categories included in the feedback details, such as "Student Error," "Ares Error," and "AST Error".
  • + *
*/ @GetMapping("exercises/{exerciseId}/feedback-details") @EnforceAtLeastInstructorInExercise diff --git a/src/main/java/de/tum/cit/aet/artemis/core/util/PageUtil.java b/src/main/java/de/tum/cit/aet/artemis/core/util/PageUtil.java index b1d3aaf5c20e..7417fe060e93 100644 --- a/src/main/java/de/tum/cit/aet/artemis/core/util/PageUtil.java +++ b/src/main/java/de/tum/cit/aet/artemis/core/util/PageUtil.java @@ -74,7 +74,14 @@ public enum ColumnMapping { FEEDBACK_ANALYSIS(Map.of( "count", "COUNT(f.id)", "detailText", "f.detailText", - "testCaseName", "f.testCase.testName" + "testCaseName", "f.testCase.testName", + "taskName", """ + COALESCE(( + SELECT MAX(t.taskName) + FROM ProgrammingExerciseTask t + JOIN t.testCases tct + WHERE t.exercise.id = :exerciseId AND tct.testName = f.testCase.testName + ), '')""" )); // @formatter:on 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 499818ace8a2..3681b3a94221 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 @@ -1199,68 +1199,85 @@ SELECT COALESCE(AVG(p.presentationScore), 0) double getAvgPresentationScoreByCourseId(@Param("courseId") long courseId); /** - * Retrieves aggregated feedback details for a given exercise, including the count of each unique feedback detail text, test case name, and task. + * Retrieves aggregated feedback details for a given exercise, including the count of each unique feedback detail text, + * test case name, task, and error category. *
* The query calculates: * - The number of occurrences of each feedback detail (COUNT). * - The relative count as a percentage of the total distinct results. * - The corresponding task name for each feedback item by checking if the feedback test case name is associated with a task. + * If a feedback item is not assigned to a task, it is labeled as "Not assigned to task." + * - The error category for each feedback item, classified as one of "Student Error", "Ares Error", or "AST Error". *
* It supports filtering by: * - Search term: Case-insensitive filtering on feedback detail text. - * - Test case names: Filters feedback based on specific test case names. + * - Test case names: Filters feedback based on specific test case names (optional). * - Task names: Filters feedback based on specific task names by mapping them to their associated test cases. - * - Occurrence range: Filters feedback based on the count of occurrences between the specified minimum and maximum values (inclusive). + * If "Not assigned to task" is specified, only feedback items without an associated task will be included. + * - Occurrence range: Filters feedback where the number of occurrences (COUNT) is between the specified minimum and maximum values (inclusive). + * - Error categories: Filters feedback based on error categories, which can be "Student Error", "Ares Error", or "AST Error". *
- * Grouping is done by feedback detail text and test case name. The occurrence count is filtered using the HAVING clause. + * Grouping is done by feedback detail text, test case name, and error category. The occurrence count is filtered using the HAVING clause. * - * @param exerciseId The ID of the exercise for which feedback details should be retrieved. - * @param searchTerm The search term used for filtering the feedback detail text (optional). - * @param filterTestCases List of test case names to filter the feedback results (optional). - * @param filterTaskNames List of task names to filter feedback results based on the associated test cases (optional). - * @param minOccurrence The minimum number of occurrences to include in the results. - * @param maxOccurrence The maximum number of occurrences to include in the results. - * @param pageable Pagination information to apply. + * @param exerciseId The ID of the exercise for which feedback details should be retrieved. + * @param searchTerm The search term used for filtering the feedback detail text (optional). + * @param filterTestCases List of active test case names to filter the feedback results (optional). + * @param filterTaskNames List of task names to filter feedback results based on the associated test cases (optional). + * If "Not assigned to task" is specified, only feedback entries without an associated task will be returned. + * @param minOccurrence The minimum number of occurrences to include in the results. + * @param maxOccurrence The maximum number of occurrences to include in the results. + * @param filterErrorCategories List of error categories to filter the feedback results. Supported categories include "Student Error", + * "Ares Error", and "AST Error". + * @param pageable Pagination information to apply. * @return A page of {@link FeedbackDetailDTO} objects representing the aggregated feedback details. */ @Query(""" - SELECT new de.tum.cit.aet.artemis.assessment.dto.FeedbackDetailDTO( - COUNT(f.id), - 0, - f.detailText, - f.testCase.testName, - COALESCE(( - SELECT t.taskName - FROM ProgrammingExerciseTask t - JOIN t.testCases tct - WHERE t.exercise.id = :exerciseId AND tct.testName = f.testCase.testName - ), ''), - '' - ) - FROM StudentParticipation p - JOIN p.results r ON r.id = ( - SELECT MAX(pr.id) - FROM p.results pr - WHERE pr.participation.id = p.id - ) - JOIN r.feedbacks f - WHERE p.exercise.id = :exerciseId - AND p.testRun = FALSE - AND f.positive = FALSE - AND (:searchTerm = '' OR LOWER(f.detailText) LIKE LOWER(CONCAT('%', REPLACE(REPLACE(:searchTerm, '%', '\\%'), '_', '\\_'), '%')) ESCAPE '\\') - AND (:#{#filterTestCases != NULL && #filterTestCases.size() < 1} = TRUE OR f.testCase.testName IN (:filterTestCases)) - AND (:#{#filterTaskNames != NULL && #filterTaskNames.size() < 1} = TRUE OR f.testCase.testName IN ( - SELECT tct.testName - FROM ProgrammingExerciseTask t - JOIN t.testCases tct - WHERE t.taskName IN (:filterTaskNames) - )) - GROUP BY f.detailText, f.testCase.testName - HAVING COUNT(f.id) BETWEEN :minOccurrence AND :maxOccurrence + SELECT new de.tum.cit.aet.artemis.assessment.dto.FeedbackDetailDTO( + COUNT(f.id), + 0, + f.detailText, + f.testCase.testName, + COALESCE(( + SELECT MAX(t.taskName) + FROM ProgrammingExerciseTask t + JOIN t.testCases tct + WHERE t.exercise.id = :exerciseId AND tct.testName = f.testCase.testName + ), 'Not assigned to task'), + CASE + WHEN f.detailText LIKE 'ARES Security Error%' THEN 'Ares Error' + WHEN f.detailText LIKE 'Unwanted Statement found%' THEN 'AST Error' + ELSE 'Student Error' + END + ) + FROM StudentParticipation p + JOIN p.results r ON r.id = ( + SELECT MAX(pr.id) + FROM p.results pr + WHERE pr.participation.id = p.id + ) + JOIN r.feedbacks f + WHERE p.exercise.id = :exerciseId + AND p.testRun = FALSE + AND f.positive = FALSE + AND (:searchTerm = '' OR LOWER(f.detailText) LIKE LOWER(CONCAT('%', REPLACE(REPLACE(:searchTerm, '%', '\\%'), '_', '\\_'), '%')) ESCAPE '\\') + AND (:#{#filterTestCases != NULL && #filterTestCases.size() < 1} = TRUE OR f.testCase.testName IN (:filterTestCases)) + AND (:#{#filterTaskNames != NULL && #filterTaskNames.size() < 1} = TRUE OR f.testCase.testName NOT IN ( + SELECT tct.testName + FROM ProgrammingExerciseTask t + JOIN t.testCases tct + WHERE t.taskName IN (:filterTaskNames) + )) + AND (:#{#filterErrorCategories != NULL && #filterErrorCategories.size() < 1} = TRUE OR CASE + WHEN f.detailText LIKE 'ARES Security Error%' THEN 'Ares Error' + WHEN f.detailText LIKE 'Unwanted Statement found%' THEN 'AST Error' + ELSE 'Student Error' + END IN (:filterErrorCategories)) + GROUP BY f.detailText, f.testCase.testName + HAVING COUNT(f.id) BETWEEN :minOccurrence AND :maxOccurrence """) Page findFilteredFeedbackByExerciseId(@Param("exerciseId") long exerciseId, @Param("searchTerm") String searchTerm, @Param("filterTestCases") List filterTestCases, @Param("filterTaskNames") List filterTaskNames, @Param("minOccurrence") long minOccurrence, - @Param("maxOccurrence") long maxOccurrence, Pageable pageable); + @Param("maxOccurrence") long maxOccurrence, @Param("filterErrorCategories") List filterErrorCategories, Pageable pageable); /** * Counts the distinct number of latest results for a given exercise, excluding those in practice mode. diff --git a/src/main/webapp/app/exercises/programming/manage/grading/feedback-analysis/Modal/feedback-filter-modal.component.html b/src/main/webapp/app/exercises/programming/manage/grading/feedback-analysis/Modal/feedback-filter-modal.component.html index 0571f1039ca6..50d4a8ec80f5 100644 --- a/src/main/webapp/app/exercises/programming/manage/grading/feedback-analysis/Modal/feedback-filter-modal.component.html +++ b/src/main/webapp/app/exercises/programming/manage/grading/feedback-analysis/Modal/feedback-filter-modal.component.html @@ -1,10 +1,10 @@