-
Notifications
You must be signed in to change notification settings - Fork 297
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adaptive learning
: Improve competency student view
#9916
Adaptive learning
: Improve competency student view
#9916
Conversation
WalkthroughThe pull request introduces enhancements to the course competency management system by adding a new method to the Changes
Possibly related PRs
Suggested labels
Suggested reviewers
Warning There were issues while running some tools. Please review the errors and either fix the tool’s configuration or disable the tool if it’s a critical failure. 🔧 pmd (7.7.0)src/test/java/de/tum/cit/aet/artemis/atlas/competency/AbstractCompetencyPrerequisiteIntegrationTest.javaThe following rules are missing or misspelled in your ruleset file category/vm/bestpractices.xml: BooleanInstantiation, DontImportJavaLang, DuplicateImports, EmptyFinallyBlock, EmptyIfStmt, EmptyInitializer, EmptyStatementBlock, EmptyStatementNotInLoop, EmptySwitchStatements, EmptySynchronizedBlock, EmptyTryBlock, EmptyWhileStmt, ExcessiveClassLength, ExcessiveMethodLength, ImportFromSamePackage, MissingBreakInSwitch, SimplifyBooleanAssertion. Please check your ruleset configuration. src/test/java/de/tum/cit/aet/artemis/atlas/competency/CourseCompetencyIntegrationTest.javaThe following rules are missing or misspelled in your ruleset file category/vm/bestpractices.xml: BooleanInstantiation, DontImportJavaLang, DuplicateImports, EmptyFinallyBlock, EmptyIfStmt, EmptyInitializer, EmptyStatementBlock, EmptyStatementNotInLoop, EmptySwitchStatements, EmptySynchronizedBlock, EmptyTryBlock, EmptyWhileStmt, ExcessiveClassLength, ExcessiveMethodLength, ImportFromSamePackage, MissingBreakInSwitch, SimplifyBooleanAssertion. Please check your ruleset configuration. 📜 Recent review detailsConfiguration used: .coderabbit.yaml 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
Documentation and Community
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Outside diff range and nitpick comments (11)
src/main/webapp/app/overview/course-competencies/course-competencies.component.ts (2)
89-89
: Consider using a named constant for the boolean parameterWhile the service call is correct, using a magic value
true
reduces code readability. Consider introducing a named constant to clarify the parameter's purpose.-const courseCompetencyObservable = this.courseCompetencyService.getAllForCourse(this.courseId, true); +const INCLUDE_SOFT_DUE_DATE = true; +const courseCompetencyObservable = this.courseCompetencyService.getAllForCourse(this.courseId, INCLUDE_SOFT_DUE_DATE);
95-96
: Consider combining filter and sort operations for better performanceWhile the current implementation is correct, we can optimize it by using a single array traversal for both filtering and sorting operations.
-this.competencies = courseCompetenciesResponse - .filter((competency) => competency.type === CourseCompetencyType.COMPETENCY) - .sort(compareSoftDueDate); +this.competencies = courseCompetenciesResponse.reduce((acc, competency) => { + if (competency.type === CourseCompetencyType.COMPETENCY) { + const insertIndex = acc.findIndex(c => compareSoftDueDate(competency, c) < 0); + if (insertIndex === -1) { + acc.push(competency); + } else { + acc.splice(insertIndex, 0, competency); + } + } + return acc; +}, [] as Competency[]);src/main/webapp/app/entities/competency.model.ts (1)
291-296
: Documentation should specify the sorting order and null handling behavior.While the JSDoc is present, it would be more helpful to explicitly document:
- The sorting order (ascending by due date)
- How null/undefined dates are handled (placed last)
Add these details to the JSDoc:
/** * Simple comparator for sorting competencies by their soft due date + * Competencies are sorted in ascending order by their soft due date. + * Competencies without a due date are placed last in the sort order. * @param a The first competency * @param b The second competency + * @returns -1 if a comes before b, 0 if equal, 1 if a comes after b */src/main/webapp/app/course/competencies/course-competency.service.ts (2)
77-83
: Add JSDoc documentation for the new parameter.The implementation looks good and aligns with the PR objectives. However, the method should be documented to explain the purpose of the
filtered
parameter.Add JSDoc documentation:
+ /** + * Retrieves all competencies for a course. + * @param courseId - The ID of the course + * @param filtered - When true, returns only competencies that are linked to exercises or lecture units + * @returns Observable of course competencies wrapped in an HTTP response + */ getAllForCourse(courseId: number, filtered = false): Observable<EntityArrayResponseType> {
77-83
: Consider using type-safe query parameters.While the implementation is functional, using string literals for query parameters can be error-prone. Consider using
HttpParams
for better type safety and maintainability.Here's a suggested improvement:
getAllForCourse(courseId: number, filtered = false): Observable<EntityArrayResponseType> { + const params = filtered ? new HttpParams().set('filter', 'true') : new HttpParams(); return this.httpClient - .get<CourseCompetency[]>(`${this.resourceURL}/courses/${courseId}/course-competencies${filtered ? '?filter=true' : ''}`, { observe: 'response' }) + .get<CourseCompetency[]>(`${this.resourceURL}/courses/${courseId}/course-competencies`, { params, observe: 'response' }) .pipe( map((res: EntityArrayResponseType) => this.convertArrayResponseDatesFromServer(res)), tap((res: EntityArrayResponseType) => res?.body?.forEach(this.sendTitlesToEntityTitleService.bind(this))), ); }src/main/java/de/tum/cit/aet/artemis/atlas/repository/CourseCompetencyRepository.java (1)
298-304
: Consider performance optimization for SIZE checksThe current query uses SIZE function in the WHERE clause which might not perform optimally for large datasets as it requires checking collection sizes for each competency.
Consider these alternatives:
- Use EXISTS subqueries which can be more efficient:
@Query(""" SELECT c FROM CourseCompetency c WHERE c.course.id = :courseId - AND (SIZE(c.lectureUnitLinks) > 0 OR SIZE(c.exerciseLinks) > 0) + AND (EXISTS (SELECT 1 FROM c.lectureUnitLinks) OR EXISTS (SELECT 1 FROM c.exerciseLinks)) ORDER BY c.id """)
- Or use JOIN FETCH with DISTINCT if you need the relationships loaded:
@Query(""" - SELECT c + SELECT DISTINCT c FROM CourseCompetency c + LEFT JOIN FETCH c.lectureUnitLinks lul + LEFT JOIN FETCH c.exerciseLinks el WHERE c.course.id = :courseId - AND (SIZE(c.lectureUnitLinks) > 0 OR SIZE(c.exerciseLinks) > 0) + AND (lul IS NOT NULL OR el IS NOT NULL) ORDER BY c.id """)src/test/java/de/tum/cit/aet/artemis/atlas/competency/PrerequisiteIntegrationTest.java (1)
112-115
: LGTM! Consider adding a test for the unsupported operation.The implementation correctly indicates that filtering is not supported for prerequisites through an UnsupportedOperationException. This follows best practices by explicitly defining unsupported operations.
Consider adding a test case to verify this unsupported operation:
+ @Test + @WithMockUser(username = TEST_PREFIX + "student1", roles = "USER") + void shouldThrowUnsupportedOperationForFiltering() { + assertThatThrownBy(() -> getAllFilteredCall(course.getId(), HttpStatus.OK)) + .isInstanceOf(UnsupportedOperationException.class) + .hasMessage("Not implemented for prerequisites"); + }src/main/java/de/tum/cit/aet/artemis/atlas/web/CourseCompetencyResource.java (1)
170-174
: Add JavaDoc for the new filter parameter.The method implementation looks good, but the JavaDoc should be updated to document the new
filter
parameter./** * GET courses/:courseId/course-competencies : gets all the course competencies of a course * * @param courseId the id of the course for which the competencies should be fetched + * @param filter whether to filter out competencies that are not linked to any exercises or lecture units (defaults to false) * @return the ResponseEntity with status 200 (OK) and with body the found competencies */
src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CourseCompetencyService.java (1)
131-137
: Consider extracting the repository call logic to a private method.While the implementation is correct, the conditional logic could be made more maintainable by extracting it to a private method. This would align with the single responsibility principle and improve code reusability.
Consider this refactoring:
- List<CourseCompetency> competencies; - if (filter) { - competencies = courseCompetencyRepository.findByCourseIdAndLinkedToLearningObjectOrderById(courseId); - } - else { - competencies = courseCompetencyRepository.findByCourseIdOrderById(courseId); - } + List<CourseCompetency> competencies = fetchCompetenciesForCourse(courseId, filter); return findProgressForCompetenciesAndUser(competencies, userId); + } + + private List<CourseCompetency> fetchCompetenciesForCourse(long courseId, boolean filter) { + return filter + ? courseCompetencyRepository.findByCourseIdAndLinkedToLearningObjectOrderById(courseId) + : courseCompetencyRepository.findByCourseIdOrderById(courseId);src/test/java/de/tum/cit/aet/artemis/atlas/competency/CourseCompetencyIntegrationTest.java (1)
160-164
: Add test documentation to clarify the test's purpose.Consider adding a descriptive JavaDoc comment to explain what this test verifies, including:
- The expected behavior when filtering competencies
- The test's prerequisites
- The expected outcome
+/** + * Verifies that a student can successfully retrieve filtered competencies for their course. + * The filtering ensures that only competencies linked to exercises or lecture units are returned. + */ @Test @WithMockUser(username = TEST_PREFIX + "student1", roles = "USER") void shouldReturnCompetenciesForStudentOfCourseFiltered() throws Exception { super.shouldReturnCompetenciesForCourseFiltered(new Competency()); }src/test/java/de/tum/cit/aet/artemis/atlas/competency/AbstractCompetencyPrerequisiteIntegrationTest.java (1)
230-241
: Enhance test coverage for filtered competencies.While the test verifies that unlinked competencies are excluded, consider adding the following improvements:
- Add positive test cases to verify competencies that should be included in the filtered results
- Add test cases for edge cases (e.g., competencies with only exercise links, only lecture unit links)
- Consider renaming the test to be more specific, e.g.,
shouldNotReturnUnlinkedCompetenciesWhenFiltered
- void shouldReturnCompetenciesForCourseFiltered(CourseCompetency newCompetency) throws Exception { + void shouldNotReturnUnlinkedCompetenciesWhenFiltered(CourseCompetency newCompetency) throws Exception { newCompetency.setTitle("Title"); newCompetency.setDescription("Description"); newCompetency.setCourse(course); courseCompetencyRepository.save(newCompetency); List<? extends CourseCompetency> competenciesOfCourse = getAllFilteredCall(course.getId(), HttpStatus.OK); assertThat(competenciesOfCourse).noneMatch(c -> c.getId().equals(courseCompetency.getId())); + + // Add positive test case + CompetencyExerciseLink link = new CompetencyExerciseLink(newCompetency, textExercise, 1); + competencyExerciseLinkRepository.save(link); + + competenciesOfCourse = getAllFilteredCall(course.getId(), HttpStatus.OK); + assertThat(competenciesOfCourse).anyMatch(c -> c.getId().equals(newCompetency.getId())); }
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
📒 Files selected for processing (10)
src/main/java/de/tum/cit/aet/artemis/atlas/repository/CourseCompetencyRepository.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CourseCompetencyService.java
(1 hunks)src/main/java/de/tum/cit/aet/artemis/atlas/web/CourseCompetencyResource.java
(1 hunks)src/main/webapp/app/course/competencies/course-competency.service.ts
(1 hunks)src/main/webapp/app/entities/competency.model.ts
(1 hunks)src/main/webapp/app/overview/course-competencies/course-competencies.component.ts
(2 hunks)src/test/java/de/tum/cit/aet/artemis/atlas/competency/AbstractCompetencyPrerequisiteIntegrationTest.java
(1 hunks)src/test/java/de/tum/cit/aet/artemis/atlas/competency/CompetencyIntegrationTest.java
(1 hunks)src/test/java/de/tum/cit/aet/artemis/atlas/competency/CourseCompetencyIntegrationTest.java
(1 hunks)src/test/java/de/tum/cit/aet/artemis/atlas/competency/PrerequisiteIntegrationTest.java
(1 hunks)
🧰 Additional context used
📓 Path-based instructions (10)
src/main/webapp/app/entities/competency.model.ts (1)
src/main/webapp/app/course/competencies/course-competency.service.ts (1)
src/test/java/de/tum/cit/aet/artemis/atlas/competency/CompetencyIntegrationTest.java (1)
Pattern src/test/java/**/*.java
: test_naming: descriptive; test_size: small_specific; fixed_data: true; junit5_features: true; assert_use: assertThat; assert_specificity: true; archunit_use: enforce_package_rules; db_query_count_tests: track_performance; util_service_factory_pattern: true; avoid_db_access: true; mock_strategy: static_mocks; context_restart_minimize: true
src/test/java/de/tum/cit/aet/artemis/atlas/competency/PrerequisiteIntegrationTest.java (1)
Pattern src/test/java/**/*.java
: test_naming: descriptive; test_size: small_specific; fixed_data: true; junit5_features: true; assert_use: assertThat; assert_specificity: true; archunit_use: enforce_package_rules; db_query_count_tests: track_performance; util_service_factory_pattern: true; avoid_db_access: true; mock_strategy: static_mocks; context_restart_minimize: true
src/main/webapp/app/overview/course-competencies/course-competencies.component.ts (1)
src/main/java/de/tum/cit/aet/artemis/atlas/repository/CourseCompetencyRepository.java (1)
Pattern src/main/java/**/*.java
: naming:CamelCase; principles:{single_responsibility,small_methods,no_duplication}; db:{perf_queries,datetime_not_timestamp}; rest:{stateless,singleton,delegate_logic,http_only,minimal_dtos}; dtos:{java_records,no_entities,min_data,single_resp}; di:constructor_injection; kiss:simple_code; file_handling:os_indep_paths; practices:{least_access,avoid_transactions,code_reuse,static_member_ref,prefer_primitives}; sql:{param_annotation,uppercase,avoid_subqueries};java:avoid_star_imports
src/test/java/de/tum/cit/aet/artemis/atlas/competency/AbstractCompetencyPrerequisiteIntegrationTest.java (1)
Pattern src/test/java/**/*.java
: test_naming: descriptive; test_size: small_specific; fixed_data: true; junit5_features: true; assert_use: assertThat; assert_specificity: true; archunit_use: enforce_package_rules; db_query_count_tests: track_performance; util_service_factory_pattern: true; avoid_db_access: true; mock_strategy: static_mocks; context_restart_minimize: true
src/main/java/de/tum/cit/aet/artemis/atlas/web/CourseCompetencyResource.java (1)
Pattern src/main/java/**/*.java
: naming:CamelCase; principles:{single_responsibility,small_methods,no_duplication}; db:{perf_queries,datetime_not_timestamp}; rest:{stateless,singleton,delegate_logic,http_only,minimal_dtos}; dtos:{java_records,no_entities,min_data,single_resp}; di:constructor_injection; kiss:simple_code; file_handling:os_indep_paths; practices:{least_access,avoid_transactions,code_reuse,static_member_ref,prefer_primitives}; sql:{param_annotation,uppercase,avoid_subqueries};java:avoid_star_imports
src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CourseCompetencyService.java (1)
Pattern src/main/java/**/*.java
: naming:CamelCase; principles:{single_responsibility,small_methods,no_duplication}; db:{perf_queries,datetime_not_timestamp}; rest:{stateless,singleton,delegate_logic,http_only,minimal_dtos}; dtos:{java_records,no_entities,min_data,single_resp}; di:constructor_injection; kiss:simple_code; file_handling:os_indep_paths; practices:{least_access,avoid_transactions,code_reuse,static_member_ref,prefer_primitives}; sql:{param_annotation,uppercase,avoid_subqueries};java:avoid_star_imports
src/test/java/de/tum/cit/aet/artemis/atlas/competency/CourseCompetencyIntegrationTest.java (1)
Pattern src/test/java/**/*.java
: test_naming: descriptive; test_size: small_specific; fixed_data: true; junit5_features: true; assert_use: assertThat; assert_specificity: true; archunit_use: enforce_package_rules; db_query_count_tests: track_performance; util_service_factory_pattern: true; avoid_db_access: true; mock_strategy: static_mocks; context_restart_minimize: true
🔇 Additional comments (9)
src/main/webapp/app/overview/course-competencies/course-competencies.component.ts (1)
6-6
: LGTM: Import changes follow guidelines
The addition of compareSoftDueDate
to the existing competency model imports is clean and follows the TypeScript coding guidelines.
src/main/webapp/app/entities/competency.model.ts (2)
296-310
: LGTM! Implementation handles all cases correctly.
The comparator function:
- Properly handles all combinations of present/absent due dates
- Uses appropriate dayjs methods for date comparison
- Returns correct comparator values (-1, 0, 1)
- Aligns with the PR objective of sorting competencies by due date
296-310
: Verify consistent usage of the comparator function.
Let's ensure this comparator is used consistently throughout the codebase for competency sorting.
✅ Verification successful
Let me gather more context about the sorting implementations to ensure completeness of the verification.
Based on the gathered information, I can now provide the final response:
Different sorting mechanisms are used appropriately in distinct contexts
The compareSoftDueDate
function is used specifically for sorting competencies by due date in the course overview component, while the import-related components use a generic sorting service for different sorting needs. This separation of concerns is correct as:
- Course overview uses
compareSoftDueDate
to sort by due dates - Import components use
sortService.sortByProperty
for flexible column-based sorting
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Find all usages of compareSoftDueDate to ensure consistent sorting implementation
# Search for direct usage of the comparator
echo "Searching for direct usage of compareSoftDueDate..."
rg "compareSoftDueDate" --type ts
# Search for array sorting operations on competencies to verify consistent sorting
echo "Searching for competency sorting operations..."
rg "sort.*Competenc" --type ts
Length of output: 1290
Script:
#!/bin/bash
# Check the implementation of sorting in the import components
echo "Checking implementation in import components..."
rg -A 5 "sortSelected" src/main/webapp/app/course/competencies/import/import-course-competencies.component.ts
echo "Checking sortService implementation..."
rg -A 5 "sortByProperty" src/main/webapp/app/course/competencies/import-standardized-competencies/course-import-standardized-course-competencies.component.ts
# Check if there are any other competency-related sorting utilities
echo "Checking for other competency-related sorting utilities..."
ast-grep --pattern 'function $FUNC($_, $_): number { $$$' src/main/webapp/app/entities/competency.model.ts
Length of output: 1404
src/test/java/de/tum/cit/aet/artemis/atlas/competency/CompetencyIntegrationTest.java (1)
112-115
:
Implementation appears inconsistent with PR objectives
The PR objectives mention implementing filtering for competencies not linked to exercises/lecture units, but this method throws UnsupportedOperationException
. This seems to contradict the intended functionality.
Consider either:
- Implementing the filtering functionality in this method to align with PR objectives
- Adding a comment explaining why filtering is handled differently for competencies
- Creating separate test methods specifically for the new filtering functionality
Let's verify if filtering is implemented elsewhere:
src/main/java/de/tum/cit/aet/artemis/atlas/web/CourseCompetencyResource.java (1)
170-174
: LGTM! Implementation follows best practices.
The changes look good:
- Proper use of
@RequestParam
with default value - Consistent error handling through service layer
- Maintains existing security constraints
- Follows REST controller patterns
src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CourseCompetencyService.java (2)
127-130
: Good use of primitive types for better performance!
The change from Long
to long
for parameters aligns with the coding guideline to prefer primitives when null values aren't needed.
130-130
: Verify all callers are updated with the new parameter.
Let's verify that all calling code has been updated to handle the new filter
parameter.
✅ Verification successful
All callers are correctly updated with the new filter parameter
The method is only called in one place in CourseCompetencyResource.java
and it correctly passes the filter
parameter to the service method. No other usages were found in the codebase, confirming that all callers have been properly updated.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for method calls to ensure they're updated with the filter parameter
rg -A 2 "findCourseCompetenciesWithProgressForUserByCourseId\(" --type java
Length of output: 979
src/test/java/de/tum/cit/aet/artemis/atlas/competency/CourseCompetencyIntegrationTest.java (1)
155-158
: LGTM!
The implementation correctly overrides the parent method and follows the established pattern for making HTTP requests in the test class.
src/test/java/de/tum/cit/aet/artemis/atlas/competency/AbstractCompetencyPrerequisiteIntegrationTest.java (1)
228-229
: LGTM! Method signature follows established patterns.
The abstract method declaration is well-designed and consistent with other abstract methods in the class.
src/main/java/de/tum/cit/aet/artemis/atlas/repository/CourseCompetencyRepository.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tested on TS4, works as expected.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tested on TS4, all works as expected
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Tested on TS4]
Looks good here :)
1455912
Checklist
General
Server
Client
Motivation and Context
The student view of competencies shows competencies that are not linked to any resources. Furthermore the order of the competencies might be confusing.
Description
Steps for Testing
Prerequisites:
Competency without due date but linked to exercise / lecture unit
Competency with due date and linked to exercise / lecture unit
Competency with due date and linked to exercise / lecture unit
Competency that is not linked to any exercise / lecture unit
Log in to Artemis
Navigate to Course Competencies
You should not see the Competency that isn't linked to any exercise / lecture unit
You should see all the other competencies ordered by the due date (earliest first, no due date last)
Testserver States
Note
These badges show the state of the test servers.
Green = Currently available, Red = Currently locked
Click on the badges to get to the test servers.
Review Progress
Performance Review
Code Review
Manual Tests
Summary by CodeRabbit
New Features
Bug Fixes
Tests