Skip to content
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: Fix linking attachment units to competencies #9739

Merged

Conversation

JohannesStoehr
Copy link
Contributor

@JohannesStoehr JohannesStoehr commented Nov 11, 2024

Checklist

General

Server

  • Important: I implemented the changes with a very good performance and prevented too many (unnecessary) and too complex database calls.
  • I strictly followed the principle of data economy for all database calls.
  • I strictly followed the server coding and design guidelines.
  • I added multiple integration tests (Spring) related to the features (with a high test coverage).
  • I added pre-authorization annotations according to the guidelines and checked the course groups for all new REST Calls (security).
  • I documented the Java code using JavaDoc style.

Motivation and Context

Fixes this issue: #9666

Description

Credits to @badkeyy in #9684

  • Added the competencyLinks field to attachment-units GET-Request
  • Added the competencyLinks field to attachment-units PUT-Request (Updates the attachment unit)

Steps for Testing

Prerequisites:

  • 1 Instructor
  • 1 Demo Course with at least one lecture and competency
  1. Log in to Artemis
  2. Navigate to the demo lecture’s units
  3. Create a new attachment unit
  4. Try to edit the created attachment unit, add the test competency and press "Submit"
  5. After pressing edit again find the added competency in the attachment unit

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

  • I (as a reviewer) confirm that the client changes (in particular related to REST calls and UI responsiveness) are implemented with a very good performance even for very large courses with more than 2000 students.
  • I (as a reviewer) confirm that the server changes (in particular related to database calls) are implemented with a very good performance even for very large courses with more than 2000 students.

Code Review

  • Code Review 1
  • Code Review 2

Manual Tests

  • Test 1
  • Test 2

Performance Tests

  • Test 1
  • Test 2

Screenshots

video

Summary by CodeRabbit

  • New Features

    • Enhanced functionality for updating attachment units, including competency links.
    • Improved file handling for attachment updates, particularly for PDF files.
  • Bug Fixes

    • Strengthened error handling for attachment unit updates and file uploads, ensuring better validation and user feedback.
  • Tests

    • Added assertions to ensure competency links are correctly handled in integration tests for attachment units.

@JohannesStoehr JohannesStoehr added this to the 7.7.1 milestone Nov 11, 2024
@JohannesStoehr JohannesStoehr self-assigned this Nov 11, 2024
@JohannesStoehr JohannesStoehr requested a review from a team as a code owner November 11, 2024 15:11
@github-actions github-actions bot added tests server Pull requests that update Java code. (Added Automatically!) lecture Pull requests that affect the corresponding module labels Nov 11, 2024
Copy link

coderabbitai bot commented Nov 11, 2024

Walkthrough

The changes in this pull request involve significant modifications to the AttachmentUnitService and AttachmentUnitResource classes, particularly focusing on the updateAttachmentUnit method. Enhancements include the ability to update competency links, improved error handling for attachment presence, and refined file processing logic. The AttachmentUnitIntegrationTest has been updated to include assertions related to competency links. Overall, the changes aim to strengthen data handling and error management for attachment units and their associated competencies.

Changes

File Change Summary
src/main/java/de/tum/cit/aet/artemis/lecture/service/AttachmentUnitService.java Updated updateAttachmentUnit to set competency links, enhance error handling, and refine file processing logic.
src/main/java/de/tum/cit/aet/artemis/lecture/web/AttachmentUnitResource.java Modified methods to use new data retrieval logic, enforce checks, and improve error handling for file processing.
src/test/java/de/tum/cit/aet/artemis/lecture/AttachmentUnitIntegrationTest.java Added assertions and method calls related to competency links in various test cases.
src/main/java/de/tum/cit/aet/artemis/lecture/repository/AttachmentUnitRepository.java Changed method to return Optional<AttachmentUnit> and added a new method for error handling.

Possibly related PRs

Suggested labels

client, core, priority:high, ready to merge

Suggested reviewers

  • JohannesWt
  • MaximilianAnzinger
  • florian-glombik
  • BBesrour
  • pzdr7

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
src/main/java/de/tum/cit/aet/artemis/lecture/repository/AttachmentUnitRepository.java

The 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/main/java/de/tum/cit/aet/artemis/lecture/web/AttachmentUnitResource.java

The 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.


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?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

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)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai or @coderabbitai title anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Outside diff range and nitpick comments (5)
src/main/java/de/tum/cit/aet/artemis/lecture/service/AttachmentUnitService.java (3)

Line range hint 119-157: Consider extracting file handling logic into a separate method.

The file handling logic within updateAttachmentUnit is quite complex and could benefit from being extracted into a dedicated method for better maintainability and readability.

Consider applying this refactor:

 public AttachmentUnit updateAttachmentUnit(AttachmentUnit existingAttachmentUnit, AttachmentUnit updateUnit, Attachment updateAttachment, MultipartFile updateFile,
         boolean keepFilename) {
     Set<CompetencyLectureUnitLink> existingCompetencyLinks = new HashSet<>(existingAttachmentUnit.getCompetencyLinks());

     existingAttachmentUnit.setDescription(updateUnit.getDescription());
     existingAttachmentUnit.setName(updateUnit.getName());
     existingAttachmentUnit.setReleaseDate(updateUnit.getReleaseDate());
     existingAttachmentUnit.setCompetencyLinks(updateUnit.getCompetencyLinks());

     AttachmentUnit savedAttachmentUnit = lectureUnitService.saveWithCompetencyLinks(existingAttachmentUnit, attachmentUnitRepository::saveAndFlush);

     Attachment existingAttachment = existingAttachmentUnit.getAttachment();
     if (existingAttachment == null) {
         throw new BadRequestAlertException("Attachment unit must be associated to an attachment", "AttachmentUnit", "attachmentMissing");
     }

     updateAttachment(existingAttachment, updateAttachment, savedAttachmentUnit);
     handleFile(updateFile, existingAttachment, keepFilename, savedAttachmentUnit.getId());
     final int revision = existingAttachment.getVersion() == null ? 1 : existingAttachment.getVersion() + 1;
     existingAttachment.setVersion(revision);
     Attachment savedAttachment = attachmentRepository.saveAndFlush(existingAttachment);
     savedAttachmentUnit.setAttachment(savedAttachment);
     prepareAttachmentUnitForClient(savedAttachmentUnit);
     evictCache(updateFile, savedAttachmentUnit);

-    if (updateFile != null) {
-        if (existingAttachmentUnit.getSlides() != null && !existingAttachmentUnit.getSlides().isEmpty()) {
-            List<Slide> slides = existingAttachmentUnit.getSlides();
-            for (Slide slide : slides) {
-                fileService.schedulePathForDeletion(FilePathService.actualPathForPublicPathOrThrow(URI.create(slide.getSlideImagePath())), 5);
-            }
-            slideRepository.deleteAll(existingAttachmentUnit.getSlides());
-        }
-        // Split the updated file into single slides only if it is a pdf
-        if (Objects.equals(FilenameUtils.getExtension(updateFile.getOriginalFilename()), "pdf")) {
-            slideSplitterService.splitAttachmentUnitIntoSingleSlides(savedAttachmentUnit);
-        }
-        if (pyrisWebhookService.isPresent() && irisSettingsRepository.isPresent()) {
-            pyrisWebhookService.get().autoUpdateAttachmentUnitsInPyris(savedAttachmentUnit.getLecture().getCourse().getId(), List.of(savedAttachmentUnit));
-        }
-    }
+    handleFileUpdate(updateFile, existingAttachmentUnit, savedAttachmentUnit);

     // Set the original competencies back to the attachment unit so that the competencyProgressService can determine which competencies changed
     existingAttachmentUnit.setCompetencyLinks(existingCompetencyLinks);
     competencyProgressService.updateProgressForUpdatedLearningObjectAsync(existingAttachmentUnit, Optional.of(updateUnit));

     return savedAttachmentUnit;
 }

+/**
+ * Handles the update of file-related operations including slide management and Pyris integration.
+ *
+ * @param updateFile The file to update
+ * @param existingAttachmentUnit The existing attachment unit
+ * @param savedAttachmentUnit The saved attachment unit
+ */
+private void handleFileUpdate(MultipartFile updateFile, AttachmentUnit existingAttachmentUnit, AttachmentUnit savedAttachmentUnit) {
+    if (updateFile == null) {
+        return;
+    }
+    
+    if (existingAttachmentUnit.getSlides() != null && !existingAttachmentUnit.getSlides().isEmpty()) {
+        List<Slide> slides = existingAttachmentUnit.getSlides();
+        for (Slide slide : slides) {
+            fileService.schedulePathForDeletion(FilePathService.actualPathForPublicPathOrThrow(URI.create(slide.getSlideImagePath())), 5);
+        }
+        slideRepository.deleteAll(existingAttachmentUnit.getSlides());
+    }
+    
+    // Split the updated file into single slides only if it is a pdf
+    if (Objects.equals(FilenameUtils.getExtension(updateFile.getOriginalFilename()), "pdf")) {
+        slideSplitterService.splitAttachmentUnitIntoSingleSlides(savedAttachmentUnit);
+    }
+    
+    if (pyrisWebhookService.isPresent() && irisSettingsRepository.isPresent()) {
+        pyrisWebhookService.get().autoUpdateAttachmentUnitsInPyris(savedAttachmentUnit.getLecture().getCourse().getId(), List.of(savedAttachmentUnit));
+    }
+}

Line range hint 182-186: Fix duplicate setReleaseDate call in updateAttachment method.

The setReleaseDate method is called twice with the same parameter.

Apply this fix:

 private void updateAttachment(Attachment existingAttachment, Attachment updateAttachment, AttachmentUnit attachmentUnit) {
     // Make sure that the original references are preserved.
     existingAttachment.setAttachmentUnit(attachmentUnit);
-    existingAttachment.setReleaseDate(updateAttachment.getReleaseDate());
     existingAttachment.setName(updateAttachment.getName());
     existingAttachment.setReleaseDate(updateAttachment.getReleaseDate());
     existingAttachment.setAttachmentType(updateAttachment.getAttachmentType());
 }

Line range hint 195-203: Consider using Path.resolve() for path construction.

The current implementation uses string concatenation for path construction. Using Path.resolve() would be more idiomatic and safer.

Apply this refactor:

 private void handleFile(MultipartFile file, Attachment attachment, boolean keepFilename, Long attachmentUnitId) {
     if (file != null && !file.isEmpty()) {
-        Path basePath = FilePathService.getAttachmentUnitFilePath().resolve(attachmentUnitId.toString());
+        Path basePath = FilePathService.getAttachmentUnitFilePath().resolve(String.valueOf(attachmentUnitId));
         Path savePath = fileService.saveFile(file, basePath, keepFilename);
         attachment.setLink(FilePathService.publicPathForActualPathOrThrow(savePath, attachmentUnitId).toString());
         attachment.setUploadDate(ZonedDateTime.now());
     }
 }
src/test/java/de/tum/cit/aet/artemis/lecture/AttachmentUnitIntegrationTest.java (2)

281-281: Consider enhancing assertion specificity for competency links.

While the current assertion verifies the presence of the competency link, consider making it more specific by checking the complete set of competency links and their properties.

-        assertThat(attachmentUnit1.getCompetencyLinks()).anyMatch(link -> link.getCompetency().getId().equals(competency.getId()));
+        assertThat(attachmentUnit1.getCompetencyLinks())
+            .hasSize(1)
+            .element(0)
+            .satisfies(link -> {
+                assertThat(link.getCompetency().getId()).isEqualTo(competency.getId());
+                assertThat(link.getWeight()).isEqualTo(1);
+            });

338-338: Enhance test readability and assertion coverage.

While the test correctly verifies competency linking, consider:

  1. Adding comments to clearly separate test phases (Arrange-Act-Assert)
  2. Expanding assertions to verify all properties of the competency link
+        // Arrange: Setup competency link
         competencyUtilService.linkLectureUnitToCompetency(competency, attachmentUnit);

         // 1. check the database call directly
         this.attachmentUnit = this.attachmentUnitRepository.findByIdElseThrow(this.attachmentUnit.getId());
         assertThat(this.attachmentUnit.getAttachment()).isEqualTo(this.attachment);

         // 2. check the REST call
         this.attachmentUnit = request.get("/api/lectures/" + lecture1.getId() + "/attachment-units/" + this.attachmentUnit.getId(), HttpStatus.OK, AttachmentUnit.class);
         assertThat(this.attachmentUnit.getAttachment()).isEqualTo(this.attachment);
-        assertThat(this.attachmentUnit.getCompetencyLinks()).anyMatch(link -> link.getCompetency().getId().equals(competency.getId()));
+        // Verify: Check competency link properties
+        assertThat(this.attachmentUnit.getCompetencyLinks())
+            .hasSize(1)
+            .element(0)
+            .satisfies(link -> {
+                assertThat(link.getCompetency().getId()).isEqualTo(competency.getId());
+                assertThat(link.getLectureUnit()).isEqualTo(this.attachmentUnit);
+                assertThat(link.getWeight()).isEqualTo(1);
+            });

Also applies to: 347-347

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between de36813 and 0c2e6a4.

📒 Files selected for processing (3)
  • src/main/java/de/tum/cit/aet/artemis/lecture/service/AttachmentUnitService.java (1 hunks)
  • src/main/java/de/tum/cit/aet/artemis/lecture/web/AttachmentUnitResource.java (1 hunks)
  • src/test/java/de/tum/cit/aet/artemis/lecture/AttachmentUnitIntegrationTest.java (3 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
src/main/java/de/tum/cit/aet/artemis/lecture/service/AttachmentUnitService.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/lecture/web/AttachmentUnitResource.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/lecture/AttachmentUnitIntegrationTest.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 (3)
src/main/java/de/tum/cit/aet/artemis/lecture/service/AttachmentUnitService.java (1)

120-120: LGTM: Competency links update is properly implemented.

The implementation correctly updates the competency links while maintaining the original links for progress tracking. The original links are preserved and later used by competencyProgressService to determine changes.

src/test/java/de/tum/cit/aet/artemis/lecture/AttachmentUnitIntegrationTest.java (1)

281-281: LGTM! Test changes effectively cover competency link functionality.

The added assertions properly verify the competency linking feature while maintaining good test practices. The suggestions above are optional improvements for better test maintainability.

Also applies to: 338-338, 347-347

src/main/java/de/tum/cit/aet/artemis/lecture/web/AttachmentUnitResource.java (1)

103-103: LGTM: Fetching slides and competencies as required

The use of findOneWithSlidesAndCompetencies ensures that the AttachmentUnit is retrieved along with its associated slides and competencies. This is appropriate if these associations are needed for the response or further processing.

coderabbitai[bot]
coderabbitai bot previously approved these changes Nov 11, 2024
sachmii
sachmii previously approved these changes Nov 11, 2024
Copy link

@sachmii sachmii left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested on TS6, after editing changes show up correctly.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Outside diff range and nitpick comments (2)
src/main/java/de/tum/cit/aet/artemis/lecture/repository/AttachmentUnitRepository.java (1)

68-70: Add JavaDoc documentation for the new method.

While the implementation is correct, adding JavaDoc would improve clarity by documenting:

  • Method purpose
  • Parameter description
  • Return value
  • Exception scenarios

Example documentation:

/**
 * Retrieves an attachment unit with its slides and competencies by ID.
 *
 * @param attachmentUnitId the ID of the attachment unit to retrieve
 * @return the attachment unit with its associated slides and competencies
 * @throws EntityNotFoundException if no attachment unit is found with the given ID
 */
default AttachmentUnit findWithSlidesAndCompetenciesByIdElseThrow(long attachmentUnitId) {
    return getValueElseThrow(findWithSlidesAndCompetenciesById(attachmentUnitId), attachmentUnitId);
}
src/main/java/de/tum/cit/aet/artemis/lecture/web/AttachmentUnitResource.java (1)

Line range hint 128-137: LGTM! Consider enhancing error handling for file uploads.

The change to use findWithSlidesAndCompetenciesByIdElseThrow is consistent with the performance optimization pattern. However, the file upload handling could be improved.

Consider wrapping the file handling in a try-catch block to provide more specific error messages:

         AttachmentUnit existingAttachmentUnit = attachmentUnitRepository.findWithSlidesAndCompetenciesByIdElseThrow(attachmentUnitId);
         checkAttachmentUnitCourseAndLecture(existingAttachmentUnit, lectureId);
         authorizationCheckService.checkHasAtLeastRoleInCourseElseThrow(Role.EDITOR, existingAttachmentUnit.getLecture().getCourse(), null);

-        AttachmentUnit savedAttachmentUnit = attachmentUnitService.updateAttachmentUnit(existingAttachmentUnit, attachmentUnit, attachment, file, keepFilename);
+        AttachmentUnit savedAttachmentUnit;
+        try {
+            savedAttachmentUnit = attachmentUnitService.updateAttachmentUnit(existingAttachmentUnit, attachmentUnit, attachment, file, keepFilename);
+        } catch (IOException e) {
+            log.error("Failed to process file upload for attachment unit {}: {}", attachmentUnitId, e.getMessage());
+            throw new InternalServerErrorException("Failed to process file upload");
+        }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 0c2e6a4 and 54f04f4.

📒 Files selected for processing (2)
  • src/main/java/de/tum/cit/aet/artemis/lecture/repository/AttachmentUnitRepository.java (2 hunks)
  • src/main/java/de/tum/cit/aet/artemis/lecture/web/AttachmentUnitResource.java (2 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
src/main/java/de/tum/cit/aet/artemis/lecture/repository/AttachmentUnitRepository.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/lecture/web/AttachmentUnitResource.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

🔇 Additional comments (4)
src/main/java/de/tum/cit/aet/artemis/lecture/repository/AttachmentUnitRepository.java (3)

6-6: LGTM!

The Optional import is correctly added and follows Java import guidelines.


66-66: Well-structured query with proper relationship handling!

The implementation demonstrates several good practices:

  • Proper use of LEFT JOIN FETCH for efficient loading of relationships
  • Clear query structure that includes competency-related joins
  • Return type change to Optional follows Java best practices for nullable results

66-66: Verify query performance with larger datasets.

The query joins multiple tables (attachmentUnit, slides, competencyLinks, competency). While the LEFT JOIN FETCH is appropriate for eager loading, we should verify performance with larger datasets.

✅ Verification successful

Let me gather more information about the query implementation and related entities.


Let me check the actual query implementation and entity relationships one more time.


Based on the results, I can now generate a final response as we have enough information about the entity relationships and query implementation.

LEFT JOIN FETCH is appropriate for this use case

The implementation is well-designed for the following reasons:

  • The AttachmentUnit entity uses lazy loading (fetch = FetchType.LAZY) for its relationships
  • The query is used for a single entity lookup by ID, not for bulk operations
  • The fetch joins are necessary to avoid N+1 queries when accessing related entities

The current implementation follows JPA best practices for eager loading specific relationships in a single query. Performance impact would be minimal since:

  1. It's an ID-based lookup returning at most one record
  2. The JOIN operations are LEFT joins, appropriate for optional relationships
  3. The query is used in a focused context for loading a complete entity graph
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for potential N+1 queries or performance issues
rg -A 10 "AttachmentUnit.*findWithSlidesAndCompetenciesById" src/test/java/ || echo "No existing performance tests found"

# Look for similar query patterns in the codebase for consistency
ast-grep --pattern 'LEFT JOIN FETCH $_.competencyLinks'

Length of output: 216


Script:

#!/bin/bash
# Check the query implementation
rg -A 5 "@Query.*findWithSlidesAndCompetenciesById" src/

# Look for entity relationships and fetch types
ast-grep --pattern 'class AttachmentUnit {
  $$$
}'

# Check for similar fetch patterns in other repositories
rg -l "LEFT JOIN FETCH" src/

Length of output: 7388


Script:

#!/bin/bash
# Check the actual query implementation in AttachmentUnitRepository
rg -B 2 -A 5 "findWithSlidesAndCompetenciesById" src/main/java/de/tum/cit/aet/artemis/lecture/repository/AttachmentUnitRepository.java

# Look for entity relationships in AttachmentUnit
rg -B 2 -A 10 "class AttachmentUnit" src/main/java/de/tum/cit/aet/artemis/lecture/

# Check if there are any existing performance tests for similar queries
rg "performance.*test.*LEFT JOIN FETCH" src/test/

Length of output: 5038

src/main/java/de/tum/cit/aet/artemis/lecture/web/AttachmentUnitResource.java (1)

103-103: LGTM! Performance optimization for competency links.

The change to use findWithSlidesAndCompetenciesByIdElseThrow improves performance by eager loading slides and competencies in a single query.

Let's verify the repository method implementation:

✅ Verification successful

Repository method verified and properly implemented

The findWithSlidesAndCompetenciesByIdElseThrow method is correctly implemented in the repository:

  • It's a default method that wraps findWithSlidesAndCompetenciesById
  • Returns an AttachmentUnit with proper error handling via getValueElseThrow
  • The base method returns Optional<AttachmentUnit> as expected for JPA repositories
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the repository method implementation
# Expected: Method should exist in AttachmentUnitRepository with proper JPA query

ast-grep --pattern 'interface AttachmentUnitRepository {
  $$$
  $_ findWithSlidesAndCompetenciesByIdElseThrow($_) {
    $$$
  }
  $$$
}'

Length of output: 263


Script:

#!/bin/bash
# Let's try a different approach to find the repository interface and method
# First, find the repository file
fd "AttachmentUnitRepository.java" --type f

# Then let's use ripgrep to search for the method as interfaces don't have method bodies
rg "findWithSlidesAndCompetenciesByIdElseThrow" -A 2

Length of output: 1800


Script:

#!/bin/bash
# Let's check the findWithSlidesAndCompetenciesById method to understand the full implementation
rg "findWithSlidesAndCompetenciesById\(" -A 5 src/main/java/de/tum/cit/aet/artemis/lecture/repository/AttachmentUnitRepository.java

Length of output: 456

Copy link
Contributor

@coolchock coolchock left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

code LGTM

Copy link
Contributor

@SimonEntholzer SimonEntholzer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Works on TS6

Copy link
Contributor

@JohannesWt JohannesWt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code 👍

Copy link

@HawKhiem HawKhiem left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested on TS6. Works as described

Copy link
Member

@BBesrour BBesrour left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested on TS3.
Not sure If I did the steps correctly but the issue still persists for me on Firefox.
I created this unit
image
Then linked a competency (competency was not newly created)
image
Competency still not linked when clicking on edit again
image

It is linked if I go back and click on edit again or if I refresh the page
image

@JohannesStoehr
Copy link
Contributor Author

It is linked if I go back and click on edit again or if I refresh the page

That's ok, we only fix the persistence on the server. This PR is not about the returned object to the client

Copy link
Collaborator

@MaximilianAnzinger MaximilianAnzinger left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maintainer approved

@MaximilianAnzinger MaximilianAnzinger added the maintainer-approved The feature maintainer has approved the PR label Nov 12, 2024
Copy link
Member

@BBesrour BBesrour left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tested on ts3, lgtm

@krusche krusche merged commit 1b50466 into develop Nov 12, 2024
71 of 77 checks passed
@krusche krusche deleted the bugfix/adaptive-learning/fix-attachment-unit-competency-links branch November 12, 2024 22:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bugfix lecture Pull requests that affect the corresponding module maintainer-approved The feature maintainer has approved the PR ready to merge server Pull requests that update Java code. (Added Automatically!) small tests
Projects
Status: Merged
Status: Done
Development

Successfully merging this pull request may close these issues.

9 participants