Skip to content

Conversation

@krusche
Copy link
Member

@krusche krusche commented Nov 14, 2025

This PR fixes many issues when using lecture units:

  1. It can happen that lecture units are not related to a lecture anymore. Those orphans live in the database and cannot be tracked back to their origin.
  2. It can happen that the lecture unit order column is null in the database, which leads to issues in the user interface, in particular unexpected behavior and unresolvable bugs (an admin would need to fix this directly in the database).
  3. Developers have used ugly workarounds when storing data in the database (e.g. first set the parent lecture to null, save, then set a proper parent lecture and save again). Those presumably led to the issues 1 and 2 on production systems.
  4. The client code sends many unnecessary requests (e.g. fetching multiple times lecture details, fetching a course and lecture even using a Resolve even when the data is not used at all or manipulating the lecture unit order on destroy even though it has not changed).
  5. The server code did not follow coding guidelines.

To improve the situation, I carefully redesigned the Java relationship between Lecture and LectureUnit:

  1. The database now uses nullable = false for lecture_unit.lectureId and lecture_unit.lecture_unit_order to prevent the above issues 1 and 2.
  2. Unfortunately, Hibernate's @OrderColumn does not support this, see https://hibernate.atlassian.net/browse/HHH-13287.
  3. Therefore, we had to change to maintain the lecture_unit_order manually. Developers who interact won't notice this because this is hidden in the corresponding methods addLectureUnit, removeLectureUnit, setLectureUnits, and reorderLectureUnits. JUnit and ArchUnit tests make sure this is implemented correctly, and developers cannot use it wrongly.
  4. As Hibernate treats Lists without @OrderColumn as Bags, we must change to Set with a LinkedHashSet. This way, existing queries, e.g. fetching lecture + lecture unit + competency links, are safe and do not return more elements than available in the database. LinkedHashSet maintains an order, and the Lecture class provides getters and setters with the List interface for developers who need to interact with it.

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

Client

  • Important: I implemented the changes with very good performance, prevented too many (unnecessary) REST calls, and made sure the UI is responsive, even with large data (e.g. using paging).
  • I strictly followed the principle of data economy for all client-server REST calls.
  • I strictly followed the client coding guidelines.
  • I updated multiple integration tests (Jest) related to the features (with a high test coverage), while following the test guidelines.
  • I added authorities to all new routes and checked the course groups for displaying navigation elements (links, buttons).
  • I documented the TypeScript code using JSDoc style.

Steps for Testing

Prerequisites:

  • 1 Instructor
  • 1 Student
  1. Create a lecture with arbitrary lecture units, modify and delete them, and make sure all operations work seamlessly. Also, make sure students can view them correctly.
  2. Create competencies and link them during the creation + modification of lecture units, delete them again, and make sure everything works without issues.
  3. Use the feature Automatic unit processing when creating a lecture and make sure it works without issues.
  4. Import a lecture with existing lecture units and make sure there are no issues.

Testserver States

You can manage test servers using Helios. Check environment statuses in the environment list. To deploy to a test server, go to the CI/CD page, find your PR or branch, and trigger the deployment.

Review Progress

Code Review

  • Code Review 1
  • Code Review 2

Manual Tests

  • Test 1
  • Test 2

Test Coverage

No changes

Screenshots

No noticeable changes (only very small improvements in the Attachments view)

Summary by CodeRabbit

  • New Features

    • Stable lecture‑unit ordering with add/remove APIs and improved two‑pass PDF splitting for uploads.
    • PDF preview, thumbnail and date‑box now use a consistent courseId input.
    • New DTOs for lecture units and competency links enabling richer UI interactions.
  • Bug Fixes

    • Prevented duplicate lecture listings, fixed stale competency links and improved lecture‑unit JSON handling.
  • Refactor

    • Scoped role enforcement to course/lecture contexts and simplified lecture‑unit creation/persistence flows.

@github-project-automation github-project-automation bot moved this to Work In Progress in Artemis Development Nov 14, 2025
@github-actions github-actions bot added tests server Pull requests that update Java code. (Added Automatically!) documentation database Pull requests that update the database. (Added Automatically!). Require a CRITICAL deployment. config-change Pull requests that change the config in a way that they require a deployment via Ansible. assessment Pull requests that affect the corresponding module atlas Pull requests that affect the corresponding module core Pull requests that affect the corresponding module iris Pull requests that affect the corresponding module lecture Pull requests that affect the corresponding module programming Pull requests that affect the corresponding module labels Nov 14, 2025
@github-actions
Copy link

End-to-End (E2E) Test Results Summary

TestsPassed ☑️Skipped ⚠️Failed ❌️Time ⏱
End-to-End (E2E) Test Report205 ran201 passed3 skipped1 failed1h 13m 32s 71ms
TestResultTime ⏱
End-to-End (E2E) Test Report
e2e/exercise/quiz-exercise/QuizExerciseDropLocation.spec.ts
ts.Quiz Exercise Drop Location Spec › DnD Quiz drop locations › Checks drop locations❌ failure2m 3s 353ms

@github-actions
Copy link

End-to-End (E2E) Test Results Summary

TestsPassed ☑️Skipped ⚠️Failed ❌️Time ⏱
End-to-End (E2E) Test Report205 ran201 passed3 skipped1 failed1h 14m 15s 717ms
TestResultTime ⏱
End-to-End (E2E) Test Report
e2e/exercise/quiz-exercise/QuizExerciseDropLocation.spec.ts
ts.Quiz Exercise Drop Location Spec › DnD Quiz drop locations › Checks drop locations❌ failure2m 3s 171ms

@github-actions
Copy link

End-to-End (E2E) Test Results Summary

TestsPassed ☑️Skipped ⚠️Failed ❌️Time ⏱
End-to-End (E2E) Test Report205 ran199 passed3 skipped3 failed1h 18m 59s 718ms
TestResultTime ⏱
End-to-End (E2E) Test Report
e2e/exam/ExamDateVerification.spec.ts
ts.Exam date verification › Exam timing › Student can start after start Date❌ failure2m 4s 588ms
e2e/exercise/quiz-exercise/QuizExerciseDropLocation.spec.ts
ts.Quiz Exercise Drop Location Spec › DnD Quiz drop locations › Checks drop locations❌ failure2m 3s 419ms
e2e/exam/test-exam/TestExamParticipation.spec.ts
ts.Test exam participation › Early Hand-in › Using exercise overview to navigate within exam❌ failure3m 46s 693ms

@github-actions
Copy link

End-to-End (E2E) Test Results Summary

TestsPassed ☑️Skipped ⚠️Failed ❌️Time ⏱
End-to-End (E2E) Test Report205 ran201 passed3 skipped1 failed1h 7m 20s 930ms
TestResultTime ⏱
End-to-End (E2E) Test Report
e2e/exercise/quiz-exercise/QuizExerciseDropLocation.spec.ts
ts.Quiz Exercise Drop Location Spec › DnD Quiz drop locations › Checks drop locations❌ failure2m 3s 129ms

avoid unnecessary REST calls to the server
We now use DTOs *yeah*.
This prevents that the lecture unit order gets destroyed
It also properly supports adding, updating or deleting competency links
Copy link
Contributor

@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: 2

♻️ Duplicate comments (1)
src/main/java/de/tum/cit/aet/artemis/lecture/web/TextUnitResource.java (1)

221-245: Avoid unsafe getLast() cast; reuse the managed textUnit instance instead

The pattern

Lecture updatedLecture = lectureRepository.saveAndFlush(lecture);
TextUnit persistedUnit = (TextUnit) updatedLecture.getLectureUnits().getLast();

is still fragile:

  • It assumes the last lecture unit is always the newly created TextUnit.
  • It performs an unchecked cast that will throw ClassCastException if the last unit is of another subtype.
  • It relies on collection ordering semantics that might change with future refactors.

After saveAndFlush(lecture), the original textUnit instance should already be managed and have its ID populated, so you can safely use it directly and avoid depending on collection order.

A safer implementation would be:

-        lecture.addLectureUnit(textUnit);
-        Lecture updatedLecture = lectureRepository.saveAndFlush(lecture);
-        TextUnit persistedUnit = (TextUnit) updatedLecture.getLectureUnits().getLast();
-        // From now on, only use persistedUnit
-        lectureUnitService.saveWithCompetencyLinks(persistedUnit, textUnitRepository::saveAndFlush);
-        competencyProgressApi.ifPresent(api -> api.updateProgressByLearningObjectAsync(persistedUnit));
+        lecture.addLectureUnit(textUnit);
+        lectureRepository.saveAndFlush(lecture);
+        // From now on, only use textUnit (now managed with an ID)
+        lectureUnitService.saveWithCompetencyLinks(textUnit, textUnitRepository::saveAndFlush);
+        competencyProgressApi.ifPresent(api -> api.updateProgressByLearningObjectAsync(textUnit));
@@
-        lectureUnitService.disconnectCompetencyLectureUnitLinks(persistedUnit);
-        return ResponseEntity.created(new URI("/api/text-units/" + persistedUnit.getId())).body(persistedUnit);
+        lectureUnitService.disconnectCompetencyLectureUnitLinks(textUnit);
+        return ResponseEntity.created(new URI("/api/text-units/" + textUnit.getId())).body(textUnit);

This removes the unsafe cast and the dependency on getLast() while preserving the intended behavior.

#!/bin/bash
# Verify that no other resources rely on `getLectureUnits().getLast()` in a similar way.
rg -n "getLectureUnits\(\).*getLast\(" src/main/java || true
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a7b8606 and a48966d.

📒 Files selected for processing (3)
  • src/main/java/de/tum/cit/aet/artemis/lecture/domain/LectureUnit.java (3 hunks)
  • src/main/java/de/tum/cit/aet/artemis/lecture/repository/TextUnitRepository.java (0 hunks)
  • src/main/java/de/tum/cit/aet/artemis/lecture/web/TextUnitResource.java (5 hunks)
💤 Files with no reviewable changes (1)
  • src/main/java/de/tum/cit/aet/artemis/lecture/repository/TextUnitRepository.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/java/de/tum/cit/aet/artemis/lecture/domain/LectureUnit.java
🧰 Additional context used
📓 Path-based instructions (1)
src/main/java/**/*.java

⚙️ CodeRabbit configuration file

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

Files:

  • src/main/java/de/tum/cit/aet/artemis/lecture/web/TextUnitResource.java
🧠 Learnings (4)
📓 Common learnings
Learnt from: marlonnienaber
Repo: ls1intum/Artemis PR: 11436
File: src/main/webapp/app/lecture/manage/lecture-series-create/lecture-series-create.component.ts:356-361
Timestamp: 2025-10-10T17:12:35.471Z
Learning: In the LectureSeriesCreateComponent (src/main/webapp/app/lecture/manage/lecture-series-create/lecture-series-create.component.ts), the rawExistingLectures input contains only persisted Lecture entities from the database, so their id field is guaranteed to be non-null despite the Lecture type having id?: number. The cast to ExistingLecture at line 359 is therefore safe.
Learnt from: marlonnienaber
Repo: ls1intum/Artemis PR: 11436
File: src/main/java/de/tum/cit/aet/artemis/iris/web/IrisLectureChatSessionResource.java:79-81
Timestamp: 2025-10-10T13:22:16.754Z
Learning: The `visibleDate` property of the `Lecture` entity in the Artemis codebase is deprecated and no longer supported. Visibility checks based on `lecture.isVisibleToStudents()` or `lecture.getVisibleDate()` should not be flagged as missing security controls, as this functionality has been intentionally removed across the codebase (tracked in issue #11479).
Learnt from: marlonnienaber
Repo: ls1intum/Artemis PR: 11436
File: src/main/java/de/tum/cit/aet/artemis/iris/web/IrisLectureChatSessionResource.java:138-140
Timestamp: 2025-10-10T13:22:13.454Z
Learning: In the IrisLectureChatSessionResource.java file, the checkWhetherLectureIsVisibleToStudentsElseThrow visibility checks are intentionally commented out as part of deprecating the Lecture.visibleDate property. This deprecation is temporary to monitor user feedback before full removal (tracked in issue #11479).
📚 Learning: 2025-10-10T17:12:35.471Z
Learnt from: marlonnienaber
Repo: ls1intum/Artemis PR: 11436
File: src/main/webapp/app/lecture/manage/lecture-series-create/lecture-series-create.component.ts:356-361
Timestamp: 2025-10-10T17:12:35.471Z
Learning: In the LectureSeriesCreateComponent (src/main/webapp/app/lecture/manage/lecture-series-create/lecture-series-create.component.ts), the rawExistingLectures input contains only persisted Lecture entities from the database, so their id field is guaranteed to be non-null despite the Lecture type having id?: number. The cast to ExistingLecture at line 359 is therefore safe.

Applied to files:

  • src/main/java/de/tum/cit/aet/artemis/lecture/web/TextUnitResource.java
📚 Learning: 2024-10-08T15:35:42.972Z
Learnt from: JohannesStoehr
Repo: ls1intum/Artemis PR: 8679
File: src/main/java/de/tum/in/www1/artemis/web/rest/tutorialgroups/TutorialGroupSessionResource.java:37-37
Timestamp: 2024-10-08T15:35:42.972Z
Learning: The DTOs `CompetencyProgressForLearningPathDTO`, `ProgrammingExerciseResetOptionsDTO`, and `CourseWithIdDTO` do not contain nullable values or `Optional` types, making the `JsonInclude` annotation unnecessary for them.

Applied to files:

  • src/main/java/de/tum/cit/aet/artemis/lecture/web/TextUnitResource.java
📚 Learning: 2024-06-10T19:44:09.116Z
Learnt from: julian-christl
Repo: ls1intum/Artemis PR: 8052
File: src/test/java/de/tum/in/www1/artemis/lecture/CompetencyIntegrationTest.java:310-310
Timestamp: 2024-06-10T19:44:09.116Z
Learning: Modifications to parameters in `competencyProgressUtilService.createCompetencyProgress` for debugging purposes are considered irrelevant to the test outcomes but helpful for clarity during debugging.

Applied to files:

  • src/main/java/de/tum/cit/aet/artemis/lecture/web/TextUnitResource.java
🧬 Code graph analysis (1)
src/main/java/de/tum/cit/aet/artemis/lecture/web/TextUnitResource.java (4)
src/main/webapp/app/atlas/shared/entities/competency.model.ts (1)
  • CompetencyLectureUnitLink (112-119)
src/main/webapp/app/lecture/shared/entities/lecture.model.ts (1)
  • Lecture (9-26)
src/main/webapp/app/lecture/manage/lecture-units/edit-text-unit/edit-text-unit.component.ts (1)
  • updateTextUnit (58-78)
src/main/webapp/app/lecture/manage/lecture-units/create-text-unit/create-text-unit.component.ts (1)
  • createTextUnit (39-66)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: Build and Push Docker Image / Build linux/amd64 Docker Image for ls1intum/artemis
  • GitHub Check: server-tests
  • GitHub Check: Build and Push Docker Image / Build linux/arm64 Docker Image for ls1intum/artemis
  • GitHub Check: server-style
  • GitHub Check: client-tests
  • GitHub Check: bean-instantiation-check
  • GitHub Check: submit-gradle
🔇 Additional comments (2)
src/main/java/de/tum/cit/aet/artemis/lecture/web/TextUnitResource.java (2)

64-76: Constructor wiring and new dependencies look correct

The added LectureUnitRepository and CompetencyRepository fields are final, injected via constructor, and used below; this aligns with the DI and coding guidelines. No issues here.


188-210: DTO and interface design is clean and aligns with guidelines

Using nested records for TextUnitDTO, CompetencyLinkDTO, and CompetencyDTO, plus the LectureUnitDTO interface abstraction, keeps the API surface minimal and strongly typed. The @JsonInclude(JsonInclude.Include.NON_EMPTY) and @JsonIgnoreProperties(ignoreUnknown = true) annotations are reasonable here for payload minimization and forward compatibility. No change requested.

Copy link
Contributor

@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: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/main/java/de/tum/cit/aet/artemis/lecture/service/LectureUnitService.java (1)

234-241: Critical: Unsafe cast will cause ClassCastException.

The cast to AttachmentVideoUnit at line 239 is performed without an instanceof check, despite the JavaDoc stating that a BAD_REQUEST should be returned if the lecture unit is not of this type. This will throw ClassCastException (500 error) instead of the documented 400 error.

Apply this diff to add the type check:

 public ResponseEntity<Void> ingestLectureUnitInPyris(LectureUnit lectureUnit) {
     if (irisLectureApi.isEmpty()) {
         log.error("Could not send Lecture Unit to Pyris: Pyris webhook service is not available, check if IRIS is enabled.");
         return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).build();
     }
+    if (!(lectureUnit instanceof AttachmentVideoUnit)) {
+        log.warn("Cannot ingest non-AttachmentVideoUnit into Pyris: {}", lectureUnit.getClass().getSimpleName());
+        return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
+    }
     boolean isIngested = irisLectureApi.get().addLectureUnitToPyrisDB((AttachmentVideoUnit) lectureUnit) != null;
     return ResponseEntity.status(isIngested ? HttpStatus.OK : HttpStatus.BAD_REQUEST).build();
 }
♻️ Duplicate comments (2)
src/main/java/de/tum/cit/aet/artemis/lecture/web/TextUnitResource.java (2)

107-107: Critical: Returns 500 instead of 404 for missing text unit.

Line 107 uses orElseThrow() which throws NoSuchElementException, resulting in a 500 error. This is inconsistent with getTextUnit (line 83) which throws EntityNotFoundException for a proper 404 response.

Apply this diff to fix the error handling:

-        var existingTextUnit = textUnitRepository.findByIdWithCompetencies(textUnitDto.id()).orElseThrow();
+        var existingTextUnit = textUnitRepository.findByIdWithCompetencies(textUnitDto.id())
+                .orElseThrow(() -> new EntityNotFoundException("TextUnit"));

147-169: Major: Unsafe cast remains despite past comment claiming it was addressed.

Lines 161-162 still use the unsafe cast pattern flagged in previous reviews. The past comment states this was "Addressed in commit 89dc376", but the fragile getLast() cast to TextUnit without an instanceof check remains in the current code.

The safer approach is to use the textUnit reference directly:

 lecture.addLectureUnit(textUnit);
-Lecture updatedLecture = lectureRepository.saveAndFlush(lecture);
-TextUnit persistedUnit = (TextUnit) updatedLecture.getLectureUnits().getLast();
+lectureRepository.saveAndFlush(lecture);
 // From now on, only use persistedUnit
-lectureUnitService.saveWithCompetencyLinks(persistedUnit, textUnitRepository::saveAndFlush);
-competencyProgressApi.ifPresent(api -> api.updateProgressByLearningObjectAsync(persistedUnit));
+lectureUnitService.saveWithCompetencyLinks(textUnit, textUnitRepository::saveAndFlush);
+competencyProgressApi.ifPresent(api -> api.updateProgressByLearningObjectAsync(textUnit));
 
 // TODO: return a DTO instead to avoid manipulation of the entity before sending it to the client
-lectureUnitService.disconnectCompetencyLectureUnitLinks(persistedUnit);
-return ResponseEntity.created(new URI("/api/text-units/" + persistedUnit.getId())).body(persistedUnit);
+lectureUnitService.disconnectCompetencyLectureUnitLinks(textUnit);
+return ResponseEntity.created(new URI("/api/text-units/" + textUnit.getId())).body(textUnit);
🧹 Nitpick comments (1)
src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CompetencyProgressService.java (1)

157-157: Clarify or remove the ambiguous comment.

The comment // Alternative is unclear. Consider either removing it or replacing it with a more descriptive explanation of when to use this overload versus the other one (e.g., "Use this overload when original competency IDs are known but the original learning object is not available").

Apply this diff to improve clarity:

-    // Alternative
+    /**
+     * Asynchronously update the existing progress for all changed competencies linked to the given learning object.
+     * Use this overload when the original competency IDs are known but the original learning object is not available.
+     *
+     * @param originalCompetencyIds The IDs of competencies linked to the learning object before the update
+     * @param updatedLearningObject The updated learning object after the update
+     */
     @Async
     public void updateProgressForUpdatedLearningObjectAsync(Set<Long> originalCompetencyIds, LearningObject updatedLearningObject) {
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a48966d and 89dc376.

📒 Files selected for processing (11)
  • src/main/java/de/tum/cit/aet/artemis/atlas/api/CompetencyProgressApi.java (1 hunks)
  • src/main/java/de/tum/cit/aet/artemis/atlas/api/CompetencyRepositoryApi.java (1 hunks)
  • src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CompetencyProgressService.java (2 hunks)
  • src/main/java/de/tum/cit/aet/artemis/lecture/dto/CompetencyDTO.java (1 hunks)
  • src/main/java/de/tum/cit/aet/artemis/lecture/dto/CompetencyLinkDTO.java (1 hunks)
  • src/main/java/de/tum/cit/aet/artemis/lecture/dto/LectureUnitDTO.java (1 hunks)
  • src/main/java/de/tum/cit/aet/artemis/lecture/dto/OnlineUnitDTO.java (1 hunks)
  • src/main/java/de/tum/cit/aet/artemis/lecture/dto/TextUnitDTO.java (1 hunks)
  • src/main/java/de/tum/cit/aet/artemis/lecture/service/LectureUnitService.java (9 hunks)
  • src/main/java/de/tum/cit/aet/artemis/lecture/web/OnlineUnitResource.java (5 hunks)
  • src/main/java/de/tum/cit/aet/artemis/lecture/web/TextUnitResource.java (5 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
src/main/java/**/*.java

⚙️ CodeRabbit configuration file

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

Files:

  • src/main/java/de/tum/cit/aet/artemis/atlas/api/CompetencyProgressApi.java
  • src/main/java/de/tum/cit/aet/artemis/lecture/dto/TextUnitDTO.java
  • src/main/java/de/tum/cit/aet/artemis/lecture/dto/CompetencyLinkDTO.java
  • src/main/java/de/tum/cit/aet/artemis/lecture/dto/OnlineUnitDTO.java
  • src/main/java/de/tum/cit/aet/artemis/atlas/api/CompetencyRepositoryApi.java
  • src/main/java/de/tum/cit/aet/artemis/lecture/service/LectureUnitService.java
  • src/main/java/de/tum/cit/aet/artemis/lecture/dto/CompetencyDTO.java
  • src/main/java/de/tum/cit/aet/artemis/lecture/dto/LectureUnitDTO.java
  • src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CompetencyProgressService.java
  • src/main/java/de/tum/cit/aet/artemis/lecture/web/OnlineUnitResource.java
  • src/main/java/de/tum/cit/aet/artemis/lecture/web/TextUnitResource.java
🧠 Learnings (13)
📓 Common learnings
Learnt from: marlonnienaber
Repo: ls1intum/Artemis PR: 11436
File: src/main/java/de/tum/cit/aet/artemis/iris/web/IrisLectureChatSessionResource.java:138-140
Timestamp: 2025-10-10T13:22:13.454Z
Learning: In the IrisLectureChatSessionResource.java file, the checkWhetherLectureIsVisibleToStudentsElseThrow visibility checks are intentionally commented out as part of deprecating the Lecture.visibleDate property. This deprecation is temporary to monitor user feedback before full removal (tracked in issue #11479).
Learnt from: marlonnienaber
Repo: ls1intum/Artemis PR: 11436
File: src/main/webapp/app/lecture/manage/lecture-series-create/lecture-series-create.component.ts:356-361
Timestamp: 2025-10-10T17:12:35.471Z
Learning: In the LectureSeriesCreateComponent (src/main/webapp/app/lecture/manage/lecture-series-create/lecture-series-create.component.ts), the rawExistingLectures input contains only persisted Lecture entities from the database, so their id field is guaranteed to be non-null despite the Lecture type having id?: number. The cast to ExistingLecture at line 359 is therefore safe.
Learnt from: marlonnienaber
Repo: ls1intum/Artemis PR: 11436
File: src/main/java/de/tum/cit/aet/artemis/iris/web/IrisLectureChatSessionResource.java:79-81
Timestamp: 2025-10-10T13:22:16.754Z
Learning: The `visibleDate` property of the `Lecture` entity in the Artemis codebase is deprecated and no longer supported. Visibility checks based on `lecture.isVisibleToStudents()` or `lecture.getVisibleDate()` should not be flagged as missing security controls, as this functionality has been intentionally removed across the codebase (tracked in issue #11479).
📚 Learning: 2024-06-10T19:44:09.116Z
Learnt from: julian-christl
Repo: ls1intum/Artemis PR: 8052
File: src/test/java/de/tum/in/www1/artemis/lecture/CompetencyIntegrationTest.java:310-310
Timestamp: 2024-06-10T19:44:09.116Z
Learning: Modifications to parameters in `competencyProgressUtilService.createCompetencyProgress` for debugging purposes are considered irrelevant to the test outcomes but helpful for clarity during debugging.

Applied to files:

  • src/main/java/de/tum/cit/aet/artemis/atlas/api/CompetencyProgressApi.java
  • src/main/java/de/tum/cit/aet/artemis/lecture/service/LectureUnitService.java
  • src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CompetencyProgressService.java
  • src/main/java/de/tum/cit/aet/artemis/lecture/web/TextUnitResource.java
📚 Learning: 2024-10-08T15:35:42.972Z
Learnt from: JohannesStoehr
Repo: ls1intum/Artemis PR: 8679
File: src/main/java/de/tum/in/www1/artemis/web/rest/tutorialgroups/TutorialGroupSessionResource.java:37-37
Timestamp: 2024-10-08T15:35:42.972Z
Learning: The DTOs `CompetencyProgressForLearningPathDTO`, `ProgrammingExerciseResetOptionsDTO`, and `CourseWithIdDTO` do not contain nullable values or `Optional` types, making the `JsonInclude` annotation unnecessary for them.

Applied to files:

  • src/main/java/de/tum/cit/aet/artemis/lecture/dto/TextUnitDTO.java
  • src/main/java/de/tum/cit/aet/artemis/lecture/dto/CompetencyLinkDTO.java
  • src/main/java/de/tum/cit/aet/artemis/lecture/dto/OnlineUnitDTO.java
  • src/main/java/de/tum/cit/aet/artemis/lecture/dto/CompetencyDTO.java
📚 Learning: 2025-05-24T16:06:41.454Z
Learnt from: tobias-lippert
Repo: ls1intum/Artemis PR: 10812
File: src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyRepository.java:70-88
Timestamp: 2025-05-24T16:06:41.454Z
Learning: In CompetencyRepository JPQL queries that join from LearningPath to course competencies, DISTINCT is not necessary because a learning path belongs to exactly one course, preventing duplicate competencies in the result set.

Applied to files:

  • src/main/java/de/tum/cit/aet/artemis/atlas/api/CompetencyRepositoryApi.java
  • src/main/java/de/tum/cit/aet/artemis/lecture/service/LectureUnitService.java
  • src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CompetencyProgressService.java
📚 Learning: 2025-09-20T16:43:32.823Z
Learnt from: MoritzSpengler
Repo: ls1intum/Artemis PR: 11382
File: src/main/java/de/tum/cit/aet/artemis/quiz/service/QuizQuestionProgressService.java:130-137
Timestamp: 2025-09-20T16:43:32.823Z
Learning: The findAllDueQuestions method exists in QuizQuestionRepository and accepts Set<Long> ids, Long courseId, and Pageable parameters, returning Page<QuizQuestion>. This method is properly implemented and available for use in QuizQuestionProgressService.

Applied to files:

  • src/main/java/de/tum/cit/aet/artemis/atlas/api/CompetencyRepositoryApi.java
📚 Learning: 2025-09-20T16:43:32.823Z
Learnt from: MoritzSpengler
Repo: ls1intum/Artemis PR: 11382
File: src/main/java/de/tum/cit/aet/artemis/quiz/service/QuizQuestionProgressService.java:130-137
Timestamp: 2025-09-20T16:43:32.823Z
Learning: The findAllDueQuestions method exists in QuizQuestionRepository and accepts Set<Long> ids, Long courseId, and Pageable parameters, returning Page<QuizQuestion>. The method has a Query annotation that filters by courseId, isOpenForPractice = TRUE, and excludes questions with IDs in the provided set using NOT IN.

Applied to files:

  • src/main/java/de/tum/cit/aet/artemis/atlas/api/CompetencyRepositoryApi.java
📚 Learning: 2025-09-25T20:28:36.905Z
Learnt from: SamuelRoettgermann
Repo: ls1intum/Artemis PR: 11419
File: src/main/java/de/tum/cit/aet/artemis/exam/domain/ExamUser.java:16-17
Timestamp: 2025-09-25T20:28:36.905Z
Learning: In the Artemis codebase, ExamUser entity uses ExamSeatDTO as a transient field for performance reasons. SamuelRoettgermann tested domain value objects but they caused 60x slower performance. This architectural exception is approved by maintainers due to significant performance benefits and Artemis naming convention requirements.

Applied to files:

  • src/main/java/de/tum/cit/aet/artemis/lecture/service/LectureUnitService.java
📚 Learning: 2025-10-10T17:12:35.471Z
Learnt from: marlonnienaber
Repo: ls1intum/Artemis PR: 11436
File: src/main/webapp/app/lecture/manage/lecture-series-create/lecture-series-create.component.ts:356-361
Timestamp: 2025-10-10T17:12:35.471Z
Learning: In the LectureSeriesCreateComponent (src/main/webapp/app/lecture/manage/lecture-series-create/lecture-series-create.component.ts), the rawExistingLectures input contains only persisted Lecture entities from the database, so their id field is guaranteed to be non-null despite the Lecture type having id?: number. The cast to ExistingLecture at line 359 is therefore safe.

Applied to files:

  • src/main/java/de/tum/cit/aet/artemis/lecture/service/LectureUnitService.java
  • src/main/java/de/tum/cit/aet/artemis/lecture/web/TextUnitResource.java
📚 Learning: 2025-10-10T13:22:16.754Z
Learnt from: marlonnienaber
Repo: ls1intum/Artemis PR: 11436
File: src/main/java/de/tum/cit/aet/artemis/iris/web/IrisLectureChatSessionResource.java:79-81
Timestamp: 2025-10-10T13:22:16.754Z
Learning: The `visibleDate` property of the `Lecture` entity in the Artemis codebase is deprecated and no longer supported. Visibility checks based on `lecture.isVisibleToStudents()` or `lecture.getVisibleDate()` should not be flagged as missing security controls, as this functionality has been intentionally removed across the codebase (tracked in issue #11479).

Applied to files:

  • src/main/java/de/tum/cit/aet/artemis/lecture/dto/LectureUnitDTO.java
📚 Learning: 2024-06-18T07:02:44.462Z
Learnt from: JohannesWt
Repo: ls1intum/Artemis PR: 8791
File: src/main/java/de/tum/in/www1/artemis/service/learningpath/LearningPathRecommendationService.java:402-410
Timestamp: 2024-06-18T07:02:44.462Z
Learning: JohannesWt agrees with using the `orElse` method from the Optional API for simplifying the handling of `optionalCompetencyProgress` in `LearningPathRecommendationService.java` to enhance code readability and conciseness.

Applied to files:

  • src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CompetencyProgressService.java
📚 Learning: 2024-06-10T19:44:09.116Z
Learnt from: valentin-boehm
Repo: ls1intum/Artemis PR: 7384
File: src/main/java/de/tum/in/www1/artemis/web/rest/StudentExamResource.java:892-892
Timestamp: 2024-06-10T19:44:09.116Z
Learning: Valentin-boehm has indicated that including detailed error messages for self-explanatory exceptions such as a BadRequestException when a student exam is already submitted or abandoned is not necessary in the context of `StudentExamResource.java`.

Applied to files:

  • src/main/java/de/tum/cit/aet/artemis/lecture/web/TextUnitResource.java
📚 Learning: 2024-10-08T15:35:48.768Z
Learnt from: florian-glombik
Repo: ls1intum/Artemis PR: 8858
File: src/test/javascript/spec/component/shared/exercise-filter/exercise-filter-modal.component.spec.ts:201-225
Timestamp: 2024-10-08T15:35:48.768Z
Learning: Non-null assertions should not be flagged or suggested for removal in the context of PR ls1intum/Artemis#8858.

Applied to files:

  • src/main/java/de/tum/cit/aet/artemis/lecture/web/TextUnitResource.java
📚 Learning: 2024-07-09T19:10:33.905Z
Learnt from: florian-glombik
Repo: ls1intum/Artemis PR: 8858
File: src/main/webapp/app/shared/exercise-filter/exercise-filter-modal.component.ts:97-107
Timestamp: 2024-07-09T19:10:33.905Z
Learning: In the context of PR ls1intum/Artemis#8858, avoid suggesting the removal of non-null assertions.

Applied to files:

  • src/main/java/de/tum/cit/aet/artemis/lecture/web/TextUnitResource.java
🧬 Code graph analysis (2)
src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CompetencyProgressService.java (1)
src/main/webapp/app/atlas/shared/entities/competency.model.ts (1)
  • CompetencyLearningObjectLink (93-101)
src/main/java/de/tum/cit/aet/artemis/lecture/web/TextUnitResource.java (4)
src/main/webapp/app/atlas/shared/entities/competency.model.ts (1)
  • CompetencyLearningObjectLink (93-101)
src/main/webapp/app/lecture/shared/entities/lecture.model.ts (1)
  • Lecture (9-26)
src/main/webapp/app/lecture/manage/lecture-units/edit-text-unit/edit-text-unit.component.ts (1)
  • updateTextUnit (58-78)
src/main/webapp/app/lecture/manage/lecture-units/create-text-unit/create-text-unit.component.ts (1)
  • createTextUnit (39-66)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: Build and Push Docker Image / Build linux/amd64 Docker Image for ls1intum/artemis
  • GitHub Check: Analyse
  • GitHub Check: Build .war artifact
  • GitHub Check: Build and Push Docker Image / Build linux/arm64 Docker Image for ls1intum/artemis
  • GitHub Check: bean-instantiation-check
  • GitHub Check: client-tests
🔇 Additional comments (17)
src/main/java/de/tum/cit/aet/artemis/atlas/api/CompetencyRepositoryApi.java (1)

28-30: LGTM!

The new API method follows the established delegation pattern and provides a clean interface for retrieving competencies by ID with exception handling.

src/main/java/de/tum/cit/aet/artemis/atlas/api/CompetencyProgressApi.java (1)

48-50: LGTM!

The new API overload provides a clean alternative when competency IDs are available instead of the full original learning object, properly delegating to the service layer.

src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CompetencyProgressService.java (2)

158-173: LGTM!

The implementation correctly compares original and updated competency IDs to determine which progress records need updating. The use of updatedLearningObject for added competencies (line 171) is appropriate for this overload.


195-196: Good optimization reminder.

The TODO comment appropriately flags a performance concern. Loading competencies once outside the loop would indeed improve efficiency when many users and competencies are involved.

src/main/java/de/tum/cit/aet/artemis/lecture/dto/LectureUnitDTO.java (1)

6-13: LGTM!

The interface provides a clean contract for lecture unit DTOs with essential fields for identification, release scheduling, and competency linking.

src/main/java/de/tum/cit/aet/artemis/lecture/dto/CompetencyLinkDTO.java (1)

6-10: LGTM!

The record correctly models the link between a competency and its weight, with appropriate Jackson annotations for JSON serialization.

src/main/java/de/tum/cit/aet/artemis/lecture/dto/CompetencyDTO.java (1)

6-10: LGTM!

The record appropriately uses a primitive long for the ID since competency references must always have a valid identifier. This differs intentionally from unit DTOs that use boxed Long to support creation scenarios where ID is not yet assigned.

src/main/java/de/tum/cit/aet/artemis/lecture/dto/TextUnitDTO.java (1)

9-12: LGTM!

The record correctly implements LectureUnitDTO and includes all necessary fields for text unit representation. The use of boxed Long for ID appropriately supports creation scenarios where the ID is not yet assigned.

src/main/java/de/tum/cit/aet/artemis/lecture/dto/OnlineUnitDTO.java (1)

9-12: LGTM!

The record correctly implements LectureUnitDTO with online-specific fields (description, source). The structure aligns well with TextUnitDTO while accommodating the distinct properties of online units.

src/main/java/de/tum/cit/aet/artemis/lecture/service/LectureUnitService.java (4)

76-90: LGTM!

The dependency injection of CompetencyRepositoryApi follows best practices: constructor injection with Optional wrapper for the optional Atlas module dependency.


129-154: LGTM!

The refactoring to use a generic type parameter and create a modifiable copy of the input list is good defensive programming. This prevents unintended side effects on the caller's collection while maintaining type safety.


182-182: LGTM!

Delegating the removal to lecture.removeLectureUnitById() improves encapsulation and aligns with the PR's refactoring to properly manage lecture unit ordering.


250-258: LGTM!

The added guard to check if competency links are initialized prevents lazy loading exceptions and ensures only managed collections are processed. The null-safe clearing of bidirectional references is proper defensive programming.

src/main/java/de/tum/cit/aet/artemis/lecture/web/OnlineUnitResource.java (2)

71-80: LGTM!

The addition of LectureUnitRepository follows proper dependency injection patterns with constructor injection.


106-106: Verify authorization scope change is intentional.

The authorization was changed from @EnforceAtLeastEditorInLectureUnit to @EnforceAtLeastEditorInLecture. This broadens the authorization scope—any editor of the lecture can now update any unit in that lecture, rather than requiring unit-specific authorization.

Ensure this aligns with the intended security model changes in the PR.

src/main/java/de/tum/cit/aet/artemis/lecture/web/TextUnitResource.java (2)

51-68: LGTM!

The dependency injection and field reordering follow proper patterns.


100-100: Verify authorization scope change is intentional.

The authorization was changed from @EnforceAtLeastEditorInLectureUnit to @EnforceAtLeastEditorInLecture, broadening the scope. Ensure this aligns with the intended security model changes.

@github-actions
Copy link

End-to-End (E2E) Test Results Summary

TestsPassed ✅SkippedFailedTime ⏱
End-to-End (E2E) Test Report1 ran1 passed0 skipped0 failed1s 701ms
TestResultTime ⏱
No test annotations available

@github-actions
Copy link

End-to-End (E2E) Test Results Summary

TestsPassed ☑️Skipped ⚠️Failed ❌️Time ⏱
End-to-End (E2E) Test Report205 ran200 passed3 skipped2 failed1h 12m 45s 240ms
TestResultTime ⏱
End-to-End (E2E) Test Report
e2e/exercise/quiz-exercise/QuizExerciseDropLocation.spec.ts
ts.Quiz Exercise Drop Location Spec › DnD Quiz drop locations › Checks drop locations❌ failure2m 3s 825ms
e2e/exam/ExamParticipation.spec.ts
ts.Exam participation › Early hand-in with continue and reload page › Participates in the exam, hand-in early, but instead continues❌ failure1m 2s 659ms

Copy link
Contributor

@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

♻️ Duplicate comments (4)
src/main/java/de/tum/cit/aet/artemis/lecture/web/TextUnitResource.java (2)

107-107: Return 404 instead of 500 when text unit is missing.

The orElseThrow() without an argument throws NoSuchElementException, resulting in a 500 error instead of a 404 when the text unit does not exist. This is inconsistent with getTextUnit (line 83), which throws EntityNotFoundException("TextUnit").

Apply this diff to align the behavior:

-        var existingTextUnit = textUnitRepository.findByIdWithCompetencies(textUnitDto.id()).orElseThrow();
+        var existingTextUnit = textUnitRepository.findByIdWithCompetencies(textUnitDto.id())
+                .orElseThrow(() -> new EntityNotFoundException("TextUnit"));

163-171: Unsafe cast and fragile retrieval logic.

Line 164 unsafely casts getLast() to TextUnit without an instanceof check and assumes the last element is the newly added unit. This is fragile and could throw ClassCastException if the collection order differs or contains other unit types.

The safer approach is to use the textUnit reference directly after saveAndFlush, as Hibernate will have populated its ID and merged it into the persistence context:

 lecture.addLectureUnit(textUnit);
-Lecture updatedLecture = lectureRepository.saveAndFlush(lecture);
-TextUnit persistedUnit = (TextUnit) updatedLecture.getLectureUnits().getLast();
+lectureRepository.saveAndFlush(lecture);
 // From now on, only use persistedUnit
-lectureUnitService.saveWithCompetencyLinks(persistedUnit, textUnitRepository::saveAndFlush);
-competencyProgressApi.ifPresent(api -> api.updateProgressByLearningObjectAsync(persistedUnit));
+lectureUnitService.saveWithCompetencyLinks(textUnit, textUnitRepository::saveAndFlush);
+competencyProgressApi.ifPresent(api -> api.updateProgressByLearningObjectAsync(textUnit));

 // TODO: return a DTO instead to avoid manipulation of the entity before sending it to the client
-lectureUnitService.disconnectCompetencyLectureUnitLinks(persistedUnit);
-return ResponseEntity.created(new URI("/api/text-units/" + persistedUnit.getId())).body(persistedUnit);
+lectureUnitService.disconnectCompetencyLectureUnitLinks(textUnit);
+return ResponseEntity.created(new URI("/api/text-units/" + textUnit.getId())).body(textUnit);
src/main/java/de/tum/cit/aet/artemis/lecture/web/OnlineUnitResource.java (1)

174-183: Unsafe cast and fragile retrieval logic.

Line 176 unsafely casts getLast() to OnlineUnit without an instanceof check and assumes the last element is the newly added unit. This is fragile and could throw ClassCastException if the collection order differs or contains other unit types.

The safer approach is to use the onlineUnit reference directly after saveAndFlush, as it will be managed and have its ID populated:

 lecture.addLectureUnit(onlineUnit);
-Lecture updatedLecture = lectureRepository.saveAndFlush(lecture);
-
-OnlineUnit persistedUnit = (OnlineUnit) updatedLecture.getLectureUnits().getLast();
+lectureRepository.saveAndFlush(lecture);
 // From now on, only use persistedUnit
-lectureUnitService.saveWithCompetencyLinks(persistedUnit, onlineUnitRepository::saveAndFlush);
-competencyProgressApi.ifPresent(api -> api.updateProgressByLearningObjectAsync(persistedUnit));
+lectureUnitService.saveWithCompetencyLinks(onlineUnit, onlineUnitRepository::saveAndFlush);
+competencyProgressApi.ifPresent(api -> api.updateProgressByLearningObjectAsync(onlineUnit));

 // TODO: return a DTO instead to avoid manipulation of the entity before sending it to the client
-lectureUnitService.disconnectCompetencyLectureUnitLinks(persistedUnit);
-return ResponseEntity.created(new URI("/api/online-units/" + persistedUnit.getId())).body(persistedUnit);
+lectureUnitService.disconnectCompetencyLectureUnitLinks(onlineUnit);
+return ResponseEntity.created(new URI("/api/online-units/" + onlineUnit.getId())).body(onlineUnit);
src/main/java/de/tum/cit/aet/artemis/lecture/service/LectureUnitService.java (1)

319-321: Add null check for competency to prevent NPE.

Line 320 calls dtoLink.competency().id() without checking if competency is null. If a client sends a DTO entry with competency: null, this will throw NullPointerException, resulting in a 500 error instead of a 400 validation error.

Apply this diff to add validation:

         for (var dtoLink : lectureUnitDto.competencyLinks()) {
+            if (dtoLink.competency() == null || dtoLink.competency().id() == null) {
+                throw new BadRequestAlertException("Competency and competency ID must not be null", "lectureUnit", "competencyNull");
+            }
             long competencyId = dtoLink.competency().id();
             double weight = dtoLink.weight();
🧹 Nitpick comments (2)
src/test/java/de/tum/cit/aet/artemis/lecture/OnlineUnitIntegrationTest.java (1)

159-174: Order-preservation test is correct but could snapshot expected ordering more defensively

Using lectureUtilService.createOnlineUnit(lecture1) and updating via PUT with OnlineUnitDTO (Lines 163-164, 170) fits the new API surface and correctly targets the second lecture unit for the ordering check. However, the assertion

List<LectureUnit> orderedUnits = lecture1.getLectureUnits();
// ...
List<LectureUnit> updatedOrderedUnits = lectureRepository.findByIdWithLectureUnitsAndAttachments(lecture1.getId()).orElseThrow().getLectureUnits();
assertThat(updatedOrderedUnits).containsExactlyElementsOf(orderedUnits);

could become fragile if getLectureUnits() ever returns the mutable backing collection instead of a copy—orderedUnits might then reflect any reordering performed during the update, leading to a false positive.

To decouple expected vs. actual, consider snapshotting the list explicitly when capturing the expected order, e.g.:

-List<LectureUnit> orderedUnits = lecture1.getLectureUnits();
+List<LectureUnit> orderedUnits = List.copyOf(lecture1.getLectureUnits());

(or new ArrayList<>(...)), so the test will fail if the backend accidentally reorders units.

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

109-111: Use descriptive exception for consistency.

Line 109 throws a generic BadRequestException() when the ID is null, while TextUnitResource.updateTextUnit (line 104) throws a more descriptive BadRequestAlertException with entity name and error code. Consider aligning the exception handling for consistency.

Apply this diff:

         if (onlineUnitDto.id() == null) {
-            throw new BadRequestException();
+            throw new BadRequestAlertException("An online unit must have an ID to be updated", ENTITY_NAME, "idNull");
         }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 89dc376 and b6a4b62.

📒 Files selected for processing (7)
  • src/main/java/de/tum/cit/aet/artemis/atlas/api/CompetencyProgressApi.java (1 hunks)
  • src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CompetencyProgressService.java (3 hunks)
  • src/main/java/de/tum/cit/aet/artemis/lecture/service/LectureUnitService.java (9 hunks)
  • src/main/java/de/tum/cit/aet/artemis/lecture/web/OnlineUnitResource.java (5 hunks)
  • src/main/java/de/tum/cit/aet/artemis/lecture/web/TextUnitResource.java (5 hunks)
  • src/test/java/de/tum/cit/aet/artemis/lecture/OnlineUnitIntegrationTest.java (4 hunks)
  • src/test/java/de/tum/cit/aet/artemis/lecture/TextUnitIntegrationTest.java (6 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/main/java/de/tum/cit/aet/artemis/atlas/api/CompetencyProgressApi.java
  • src/test/java/de/tum/cit/aet/artemis/lecture/TextUnitIntegrationTest.java
  • src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CompetencyProgressService.java
🧰 Additional context used
📓 Path-based instructions (2)
src/main/java/**/*.java

⚙️ CodeRabbit configuration file

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

Files:

  • src/main/java/de/tum/cit/aet/artemis/lecture/web/TextUnitResource.java
  • src/main/java/de/tum/cit/aet/artemis/lecture/web/OnlineUnitResource.java
  • src/main/java/de/tum/cit/aet/artemis/lecture/service/LectureUnitService.java
src/test/java/**/*.java

⚙️ CodeRabbit configuration file

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

Files:

  • src/test/java/de/tum/cit/aet/artemis/lecture/OnlineUnitIntegrationTest.java
🧠 Learnings (9)
📓 Common learnings
Learnt from: marlonnienaber
Repo: ls1intum/Artemis PR: 11436
File: src/main/webapp/app/lecture/manage/lecture-series-create/lecture-series-create.component.ts:356-361
Timestamp: 2025-10-10T17:12:35.471Z
Learning: In the LectureSeriesCreateComponent (src/main/webapp/app/lecture/manage/lecture-series-create/lecture-series-create.component.ts), the rawExistingLectures input contains only persisted Lecture entities from the database, so their id field is guaranteed to be non-null despite the Lecture type having id?: number. The cast to ExistingLecture at line 359 is therefore safe.
📚 Learning: 2025-10-10T17:12:35.471Z
Learnt from: marlonnienaber
Repo: ls1intum/Artemis PR: 11436
File: src/main/webapp/app/lecture/manage/lecture-series-create/lecture-series-create.component.ts:356-361
Timestamp: 2025-10-10T17:12:35.471Z
Learning: In the LectureSeriesCreateComponent (src/main/webapp/app/lecture/manage/lecture-series-create/lecture-series-create.component.ts), the rawExistingLectures input contains only persisted Lecture entities from the database, so their id field is guaranteed to be non-null despite the Lecture type having id?: number. The cast to ExistingLecture at line 359 is therefore safe.

Applied to files:

  • src/main/java/de/tum/cit/aet/artemis/lecture/web/TextUnitResource.java
  • src/main/java/de/tum/cit/aet/artemis/lecture/web/OnlineUnitResource.java
  • src/main/java/de/tum/cit/aet/artemis/lecture/service/LectureUnitService.java
📚 Learning: 2024-06-10T19:44:09.116Z
Learnt from: valentin-boehm
Repo: ls1intum/Artemis PR: 7384
File: src/main/java/de/tum/in/www1/artemis/web/rest/StudentExamResource.java:892-892
Timestamp: 2024-06-10T19:44:09.116Z
Learning: Valentin-boehm has indicated that including detailed error messages for self-explanatory exceptions such as a BadRequestException when a student exam is already submitted or abandoned is not necessary in the context of `StudentExamResource.java`.

Applied to files:

  • src/main/java/de/tum/cit/aet/artemis/lecture/web/TextUnitResource.java
📚 Learning: 2024-06-10T19:44:09.116Z
Learnt from: julian-christl
Repo: ls1intum/Artemis PR: 8052
File: src/test/java/de/tum/in/www1/artemis/lecture/CompetencyIntegrationTest.java:310-310
Timestamp: 2024-06-10T19:44:09.116Z
Learning: Modifications to parameters in `competencyProgressUtilService.createCompetencyProgress` for debugging purposes are considered irrelevant to the test outcomes but helpful for clarity during debugging.

Applied to files:

  • src/main/java/de/tum/cit/aet/artemis/lecture/web/TextUnitResource.java
  • src/test/java/de/tum/cit/aet/artemis/lecture/OnlineUnitIntegrationTest.java
  • src/main/java/de/tum/cit/aet/artemis/lecture/service/LectureUnitService.java
📚 Learning: 2024-10-08T15:35:48.768Z
Learnt from: florian-glombik
Repo: ls1intum/Artemis PR: 8858
File: src/test/javascript/spec/component/shared/exercise-filter/exercise-filter-modal.component.spec.ts:201-225
Timestamp: 2024-10-08T15:35:48.768Z
Learning: Non-null assertions should not be flagged or suggested for removal in the context of PR ls1intum/Artemis#8858.

Applied to files:

  • src/main/java/de/tum/cit/aet/artemis/lecture/web/TextUnitResource.java
📚 Learning: 2024-10-15T11:33:17.915Z
Learnt from: alexjoham
Repo: ls1intum/Artemis PR: 9455
File: src/test/java/de/tum/cit/aet/artemis/iris/IrisTextExerciseChatMessageIntegrationTest.java:401-401
Timestamp: 2024-10-15T11:33:17.915Z
Learning: In the Artemis project, when new fields are added to classes like `PyrisChatStatusUpdateDTO`, corresponding tests may be implemented in separate integration test classes such as `IrisChatTokenTrackingIntegrationTest`.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/lecture/OnlineUnitIntegrationTest.java
📚 Learning: 2025-09-25T20:28:36.905Z
Learnt from: SamuelRoettgermann
Repo: ls1intum/Artemis PR: 11419
File: src/main/java/de/tum/cit/aet/artemis/exam/domain/ExamUser.java:16-17
Timestamp: 2025-09-25T20:28:36.905Z
Learning: In the Artemis codebase, ExamUser entity uses ExamSeatDTO as a transient field for performance reasons. SamuelRoettgermann tested domain value objects but they caused 60x slower performance. This architectural exception is approved by maintainers due to significant performance benefits and Artemis naming convention requirements.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/lecture/OnlineUnitIntegrationTest.java
  • src/main/java/de/tum/cit/aet/artemis/lecture/service/LectureUnitService.java
📚 Learning: 2024-10-08T15:35:42.972Z
Learnt from: jakubriegel
Repo: ls1intum/Artemis PR: 8050
File: src/test/java/de/tum/in/www1/artemis/plagiarism/PlagiarismUtilService.java:62-66
Timestamp: 2024-10-08T15:35:42.972Z
Learning: The `createCourseWithUsers` method in `PlagiarismUtilService.java` uses fixed inputs as it is designed to be a test helper method for simplifying the setup of courses and users in tests.

Applied to files:

  • src/test/java/de/tum/cit/aet/artemis/lecture/OnlineUnitIntegrationTest.java
📚 Learning: 2025-05-24T16:06:41.454Z
Learnt from: tobias-lippert
Repo: ls1intum/Artemis PR: 10812
File: src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyRepository.java:70-88
Timestamp: 2025-05-24T16:06:41.454Z
Learning: In CompetencyRepository JPQL queries that join from LearningPath to course competencies, DISTINCT is not necessary because a learning path belongs to exactly one course, preventing duplicate competencies in the result set.

Applied to files:

  • src/main/java/de/tum/cit/aet/artemis/lecture/service/LectureUnitService.java
🧬 Code graph analysis (2)
src/main/java/de/tum/cit/aet/artemis/lecture/web/TextUnitResource.java (3)
src/main/webapp/app/atlas/shared/entities/competency.model.ts (1)
  • CompetencyLearningObjectLink (93-101)
src/main/webapp/app/lecture/manage/lecture-units/edit-text-unit/edit-text-unit.component.ts (1)
  • updateTextUnit (58-78)
src/main/webapp/app/lecture/manage/lecture-units/create-text-unit/create-text-unit.component.ts (1)
  • createTextUnit (39-66)
src/main/java/de/tum/cit/aet/artemis/lecture/web/OnlineUnitResource.java (4)
src/main/webapp/app/atlas/shared/entities/competency.model.ts (1)
  • CompetencyLearningObjectLink (93-101)
src/main/webapp/app/lecture/shared/entities/lecture.model.ts (1)
  • Lecture (9-26)
src/main/webapp/app/lecture/manage/lecture-units/edit-online-unit/edit-online-unit.component.ts (1)
  • updateOnlineUnit (61-81)
src/main/webapp/app/lecture/manage/lecture-units/create-online-unit/create-online-unit.component.ts (1)
  • createOnlineUnit (39-67)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: server-tests
  • GitHub Check: client-tests
  • GitHub Check: Build and Push Docker Image / Build linux/arm64 Docker Image for ls1intum/artemis
  • GitHub Check: Build and Push Docker Image / Build linux/amd64 Docker Image for ls1intum/artemis
  • GitHub Check: Build .war artifact
  • GitHub Check: bean-instantiation-check
  • GitHub Check: Analyse
🔇 Additional comments (2)
src/test/java/de/tum/cit/aet/artemis/lecture/OnlineUnitIntegrationTest.java (2)

38-38: OnlineUnitDTO import and usage in update test look consistent

The new import on Line 38 and the switch to OnlineUnitDTO as the response type in updateOnlineUnit_asInstructor_shouldUpdateOnlineUnit (Lines 145-155) align with a DTO-based update API. The assertion on updatedOnlineUnit.description() and the verification of updateProgressForUpdatedLearningObjectAsyncWithOriginalCompetencyIds(Set.of(competency.getId()), onlineUnit) correctly exercise the new competency-progress contract for updates.

Also applies to: 145-155


69-86: Explicitly setting lecture on test OnlineUnit matches non-null DB constraint

Assigning the lecture in initTestCase via this.onlineUnit.setLecture(this.lecture1); (Line 76) ensures the test OnlineUnit is always associated with a Lecture, which is consistent with a non-null lectureId constraint on lecture units and avoids persistence edge cases during test setup.

@github-actions
Copy link

End-to-End (E2E) Test Results Summary

TestsPassed ☑️Skipped ⚠️Failed ❌️Time ⏱
End-to-End (E2E) Test Report205 ran199 passed3 skipped3 failed1h 15m 51s 269ms
TestResultTime ⏱
End-to-End (E2E) Test Report
e2e/exam/test-exam/TestExamParticipation.spec.ts
ts.Test exam participation › Early Hand-in › Using exercise sidebar to navigate within exam❌ failure3m 22s 641ms
ts.Test exam participation › Early Hand-in › Using exercise overview to navigate within exam❌ failure3m 43s 165ms
e2e/exercise/programming/ProgrammingExerciseStaticCodeAnalysis.spec.ts
ts.Static code analysis tests › Configures SCA grading and makes a successful submission with SCA errors❌ failure2m 38s 122ms

@helios-aet helios-aet bot temporarily deployed to artemis-test9.artemis.cit.tum.de November 19, 2025 07:32 Inactive
@hanna20022005
Copy link

tested on test server 9
when importing lectures one cannot see if any units were imported as well at a glance but once one opens it through the student view one can see the units and it seems like quizzes with missed deadlines do not appear to get imported :/

course with quiz were deadline is in the past to be imported:
image
same course when imported:
image

also not sure if this is intended but if you edit a lecture unit the checkbox for "automatic unit processing" is unchecked and one cannot view the previously uploaded pdf in the page anymore
image
and clicking on "view isolated" on a lecture unit opens a blank page
image

Copy link

@NicoNoell NicoNoell 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 TS9, worked as expected

Copy link

@lana-ati lana-ati left a comment

Choose a reason for hiding this comment

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

Tested during working session on TS9. All described features working as expected from both student and instructor view.

Copy link

@maxgutke maxgutke left a comment

Choose a reason for hiding this comment

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

I tested this PR on TS9. I followed the steps for testing and encountered no issues

Copy link

@kristi-balla kristi-balla 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 t9, created a text and exercise unit: deleting, importing units and linking competencies works. The automatic unit processing also works

@oTaczkowski
Copy link

Tested everything as perscribed. I created a lecture, added units automatically, added more units with several competencies, deleted the units and edited them. Everything worked without issues.

Copy link
Contributor

@MoritzSpengler MoritzSpengler left a comment

Choose a reason for hiding this comment

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

Re-tested. The previous errors did not occur anymore. Creating, editing and deleting lectures with lecture units works without issues.
I can confirm that exercises that are past the due date are not getting imported when the lecture gets importet (maybe this is expected behavior).

@MaximilianAnzinger
Copy link
Collaborator

@hanna20022005 the "view isolated" on a lecture unit opens a blank page bug is also happening on production. Could you create an issue and inform @bassner since he's the current maintainer of the lectures module.
Thx in advance :)

@krusche krusche merged commit 9db970f into develop Nov 19, 2025
38 of 43 checks passed
@krusche krusche deleted the bugfix/avoid-orphaned-lecture-units branch November 19, 2025 10:53
@github-project-automation github-project-automation bot moved this from Ready For Review to Merged in Artemis Development Nov 19, 2025
m4rl0ne pushed a commit to m4rl0ne/Artemis that referenced this pull request Dec 1, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

assessment Pull requests that affect the corresponding module atlas Pull requests that affect the corresponding module client Pull requests that update TypeScript code. (Added Automatically!) communication Pull requests that affect the corresponding module config-change Pull requests that change the config in a way that they require a deployment via Ansible. core Pull requests that affect the corresponding module database Pull requests that update the database. (Added Automatically!). Require a CRITICAL deployment. documentation exam Pull requests that affect the corresponding module iris Pull requests that affect the corresponding module lecture Pull requests that affect the corresponding module programming Pull requests that affect the corresponding module ready for review server Pull requests that update Java code. (Added Automatically!) tests

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.