diff --git a/src/main/java/de/tum/in/www1/artemis/domain/LearningObject.java b/src/main/java/de/tum/in/www1/artemis/domain/LearningObject.java index e3288ede3bae..85962cd88821 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/LearningObject.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/LearningObject.java @@ -19,4 +19,6 @@ public interface LearningObject { Long getId(); Set getCompetencies(); + + boolean isVisibleToStudents(); } diff --git a/src/main/java/de/tum/in/www1/artemis/domain/lecture/LectureUnit.java b/src/main/java/de/tum/in/www1/artemis/domain/lecture/LectureUnit.java index 0fd6d3ae3b0f..25ca1a98b87a 100644 --- a/src/main/java/de/tum/in/www1/artemis/domain/lecture/LectureUnit.java +++ b/src/main/java/de/tum/in/www1/artemis/domain/lecture/LectureUnit.java @@ -135,8 +135,18 @@ public void setCompletedUsers(Set completedUsers) { this.completedUsers = completedUsers; } + /** + * Checks if the lecture unit is visible to the students. + * A lecture unit is visible to the students if the lecture is visible to the students and the release date is null or in the past. + * + * @return true if the lecture unit is visible to the students, false otherwise + */ @JsonProperty("visibleToStudents") public boolean isVisibleToStudents() { + if (lecture == null || !lecture.isVisibleToStudents()) { + return false; + } + if (releaseDate == null) { return true; } diff --git a/src/main/java/de/tum/in/www1/artemis/service/learningpath/LearningPathService.java b/src/main/java/de/tum/in/www1/artemis/service/learningpath/LearningPathService.java index 426d3fc70ab9..14640c73b79b 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/learningpath/LearningPathService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/learningpath/LearningPathService.java @@ -347,7 +347,7 @@ public NgxLearningPathDTO generateNgxPathRepresentation(@NotNull LearningPath le * @return the navigation overview */ public LearningPathNavigationOverviewDTO getLearningPathNavigationOverview(long learningPathId) { - var learningPath = findWithCompetenciesAndLearningObjectsAndCompletedUsersById(learningPathId); + var learningPath = findWithCompetenciesAndReleasedLearningObjectsAndCompletedUsersById(learningPathId); if (!userRepository.getUser().equals(learningPath.getUser())) { throw new AccessForbiddenException("You are not allowed to access this learning path"); } @@ -355,19 +355,24 @@ public LearningPathNavigationOverviewDTO getLearningPathNavigationOverview(long } /** - * Finds a learning path by its id and eagerly fetches the competencies, linked lecture units and exercises, and the corresponding domain objects storing the progress of the - * connected user. + * Finds a learning path by its id and eagerly fetches the competencies, linked and released lecture units and exercises, and the corresponding domain objects storing the + * progress of the connected user. *

* As Spring Boot 3 doesn't support conditional JOIN FETCH statements, we have to retrieve the data manually. * * @param learningPathId the id of the learning path to fetch * @return the learning path with fetched data */ - public LearningPath findWithCompetenciesAndLearningObjectsAndCompletedUsersById(long learningPathId) { + public LearningPath findWithCompetenciesAndReleasedLearningObjectsAndCompletedUsersById(long learningPathId) { LearningPath learningPath = learningPathRepository.findWithCompetenciesAndLectureUnitsAndExercisesByIdElseThrow(learningPathId); - // Remove exercise units, since they are already retrieved as exercises - learningPath.getCompetencies().stream().forEach(competency -> competency - .setLectureUnits(competency.getLectureUnits().stream().filter(lectureUnit -> !(lectureUnit instanceof ExerciseUnit)).collect(Collectors.toSet()))); + + // Remove exercises that are not visible to students + learningPath.getCompetencies() + .forEach(competency -> competency.setExercises(competency.getExercises().stream().filter(Exercise::isVisibleToStudents).collect(Collectors.toSet()))); + // Remove unreleased lecture units as well as exercise units, since they are already retrieved as exercises + learningPath.getCompetencies().forEach(competency -> competency.setLectureUnits(competency.getLectureUnits().stream() + .filter(lectureUnit -> !(lectureUnit instanceof ExerciseUnit) && lectureUnit.isVisibleToStudents()).collect(Collectors.toSet()))); + if (learningPath.getUser() == null) { learningPath.getCompetencies().forEach(competency -> { competency.setUserProgress(Collections.emptySet()); diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/LearningPathResource.java b/src/main/java/de/tum/in/www1/artemis/web/rest/LearningPathResource.java index 06db174e4942..5bdb10c41c9e 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/LearningPathResource.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/LearningPathResource.java @@ -250,7 +250,7 @@ public ResponseEntity getRelativeLearningPathNavigati @RequestParam LearningObjectType learningObjectType, @RequestParam long competencyId) { log.debug("REST request to get navigation for learning path with id: {} relative to learning object with id: {} and type: {} in competency with id: {}", learningPathId, learningObjectId, learningObjectType, competencyId); - var learningPath = learningPathService.findWithCompetenciesAndLearningObjectsAndCompletedUsersById(learningPathId); + var learningPath = learningPathService.findWithCompetenciesAndReleasedLearningObjectsAndCompletedUsersById(learningPathId); checkLearningPathAccessElseThrow(Optional.empty(), learningPath, Optional.empty()); return ResponseEntity.ok(learningPathNavigationService.getNavigationRelativeToLearningObject(learningPath, learningObjectId, learningObjectType, competencyId)); } @@ -267,7 +267,7 @@ public ResponseEntity getRelativeLearningPathNavigati @EnforceAtLeastStudent public ResponseEntity getLearningPathNavigation(@PathVariable long learningPathId) { log.debug("REST request to get navigation for learning path with id: {}", learningPathId); - var learningPath = learningPathService.findWithCompetenciesAndLearningObjectsAndCompletedUsersById(learningPathId); + var learningPath = learningPathService.findWithCompetenciesAndReleasedLearningObjectsAndCompletedUsersById(learningPathId); checkLearningPathAccessElseThrow(Optional.empty(), learningPath, Optional.empty()); return ResponseEntity.ok(learningPathNavigationService.getNavigation(learningPath)); } @@ -287,7 +287,7 @@ public ResponseEntity getLearningPathNavigati } private ResponseEntity getLearningPathNgx(@PathVariable long learningPathId, NgxRequestType type) { - LearningPath learningPath = learningPathService.findWithCompetenciesAndLearningObjectsAndCompletedUsersById(learningPathId); + LearningPath learningPath = learningPathService.findWithCompetenciesAndReleasedLearningObjectsAndCompletedUsersById(learningPathId); Course course = courseRepository.findByIdElseThrow(learningPath.getCourse().getId()); courseService.checkLearningPathsEnabledElseThrow(course); @@ -372,7 +372,7 @@ public ResponseEntity> getCompetencyPr @EnforceAtLeastStudent public ResponseEntity> getCompetencyOrderForLearningPath(@PathVariable long learningPathId) { log.debug("REST request to get competency order for learning path: {}", learningPathId); - final var learningPath = learningPathService.findWithCompetenciesAndLearningObjectsAndCompletedUsersById(learningPathId); + final var learningPath = learningPathService.findWithCompetenciesAndReleasedLearningObjectsAndCompletedUsersById(learningPathId); checkLearningPathAccessElseThrow(Optional.of(learningPath.getCourse()), learningPath, Optional.empty()); diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/dto/competency/LearningPathNavigationObjectDTO.java b/src/main/java/de/tum/in/www1/artemis/web/rest/dto/competency/LearningPathNavigationObjectDTO.java index ae6662e1a7ac..6a7b7a84d7f2 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/dto/competency/LearningPathNavigationObjectDTO.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/dto/competency/LearningPathNavigationObjectDTO.java @@ -15,7 +15,7 @@ * @param type the type of the learning object */ @JsonInclude(JsonInclude.Include.NON_EMPTY) -public record LearningPathNavigationObjectDTO(long id, boolean completed, String name, long competencyId, LearningObjectType type) { +public record LearningPathNavigationObjectDTO(long id, boolean completed, String name, long competencyId, LearningObjectType type, boolean unreleased) { /** * Create a navigation object DTO from a learning object. @@ -25,11 +25,26 @@ public record LearningPathNavigationObjectDTO(long id, boolean completed, String * @return the navigation object DTO */ public static LearningPathNavigationObjectDTO of(LearningObject learningObject, boolean completed, long competencyId) { - return switch (learningObject) { - case LectureUnit lectureUnit -> new LearningPathNavigationObjectDTO(lectureUnit.getId(), completed, lectureUnit.getName(), competencyId, LearningObjectType.LECTURE); - case Exercise exercise -> new LearningPathNavigationObjectDTO(learningObject.getId(), completed, exercise.getTitle(), competencyId, LearningObjectType.EXERCISE); + long id = learningObject.getId(); + String name; + LearningObjectType type; + boolean unreleased = !learningObject.isVisibleToStudents(); + + switch (learningObject) { + case LectureUnit lectureUnit -> { + name = lectureUnit.getName(); + type = LearningObjectType.LECTURE; + } + case Exercise exercise -> { + name = exercise.getTitle(); + type = LearningObjectType.EXERCISE; + } default -> throw new IllegalArgumentException("Learning object must be either LectureUnit or Exercise"); - }; + } + + name = unreleased ? "" : name; + + return new LearningPathNavigationObjectDTO(id, completed, name, competencyId, type, unreleased); } public enum LearningObjectType { diff --git a/src/main/webapp/app/course/learning-paths/components/learning-path-nav-overview-learning-objects/learning-path-nav-overview-learning-objects.component.html b/src/main/webapp/app/course/learning-paths/components/learning-path-nav-overview-learning-objects/learning-path-nav-overview-learning-objects.component.html index abbe56dc2884..73356f9f1645 100644 --- a/src/main/webapp/app/course/learning-paths/components/learning-path-nav-overview-learning-objects/learning-path-nav-overview-learning-objects.component.html +++ b/src/main/webapp/app/course/learning-paths/components/learning-path-nav-overview-learning-objects/learning-path-nav-overview-learning-objects.component.html @@ -3,10 +3,18 @@ @for (learningObject of learningObjects(); let last = $last; track learningObject) {

- {{ learningObject.name }} + @if (learningObject.unreleased) { + + + } @else { + {{ learningObject.name }} + } @if (learningObject.completed) { } @else if (nextLearningObjectOnPath()?.id === learningObject.id) { diff --git a/src/main/webapp/app/course/learning-paths/components/learning-path-nav-overview-learning-objects/learning-path-nav-overview-learning-objects.component.ts b/src/main/webapp/app/course/learning-paths/components/learning-path-nav-overview-learning-objects/learning-path-nav-overview-learning-objects.component.ts index 236b4353b9d3..15589bdb24bc 100644 --- a/src/main/webapp/app/course/learning-paths/components/learning-path-nav-overview-learning-objects/learning-path-nav-overview-learning-objects.component.ts +++ b/src/main/webapp/app/course/learning-paths/components/learning-path-nav-overview-learning-objects/learning-path-nav-overview-learning-objects.component.ts @@ -4,7 +4,7 @@ import { AlertService } from 'app/core/util/alert.service'; import { LearningPathApiService } from 'app/course/learning-paths/services/learning-path-api.service'; import { LearningPathNavigationService } from 'app/course/learning-paths/services/learning-path-navigation.service'; import { LearningPathNavigationObjectDTO } from 'app/entities/competency/learning-path.model'; -import { IconDefinition, faCheckCircle } from '@fortawesome/free-solid-svg-icons'; +import { IconDefinition, faCheckCircle, faLock } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap'; @@ -17,6 +17,7 @@ import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap'; }) export class LearningPathNavOverviewLearningObjectsComponent implements OnInit { protected readonly faCheckCircle: IconDefinition = faCheckCircle; + protected readonly faLock: IconDefinition = faLock; private readonly alertService: AlertService = inject(AlertService); private readonly learningPathApiService: LearningPathApiService = inject(LearningPathApiService); @@ -57,7 +58,9 @@ export class LearningPathNavOverviewLearningObjectsComponent implements OnInit { } selectLearningObject(learningObject: LearningPathNavigationObjectDTO): void { - this.learningPathNavigationService.loadRelativeLearningPathNavigation(this.learningPathId(), learningObject); - this.onLearningObjectSelected.emit(); + if (!learningObject.unreleased) { + this.learningPathNavigationService.loadRelativeLearningPathNavigation(this.learningPathId(), learningObject); + this.onLearningObjectSelected.emit(); + } } } diff --git a/src/main/webapp/app/entities/competency/learning-path.model.ts b/src/main/webapp/app/entities/competency/learning-path.model.ts index e325c34a49bc..3f5c0e7775de 100644 --- a/src/main/webapp/app/entities/competency/learning-path.model.ts +++ b/src/main/webapp/app/entities/competency/learning-path.model.ts @@ -35,9 +35,10 @@ export interface LearningPathCompetencyDTO { export interface LearningPathNavigationObjectDTO { id: number; completed: boolean; - name: string; + name?: string; competencyId: number; type: LearningObjectType; + unreleased: boolean; } export interface LearningPathNavigationDTO { diff --git a/src/main/webapp/i18n/de/learningPath.json b/src/main/webapp/i18n/de/learningPath.json index 0438bb34c785..f8ee7e720963 100644 --- a/src/main/webapp/i18n/de/learningPath.json +++ b/src/main/webapp/i18n/de/learningPath.json @@ -8,8 +8,9 @@ "overview": { "title": "Lernpfad Kompetenzen", "showCompetenciesGraphButton": "Kompetenzgraph", - "emptyLearningObjectsLabel": "Diese Kompetenz enthält bisher noch keine Lerneinheiten!", - "nextLearningObjectOnPathLabel": "Nächste" + "emptyLearningObjectsLabel": "Diese Kompetenz enthält bisher noch keine Lernobjekte!", + "nextLearningObjectOnPathLabel": "Nächste", + "unreleasedLearningObjectLabel": "Unveröffentlichte Lerneinheit" }, "recapLabel": "Lerneinheiten wiederholen" }, diff --git a/src/main/webapp/i18n/en/learningPath.json b/src/main/webapp/i18n/en/learningPath.json index e35bcdc8f31b..abe8f850c53f 100644 --- a/src/main/webapp/i18n/en/learningPath.json +++ b/src/main/webapp/i18n/en/learningPath.json @@ -8,8 +8,9 @@ "overview": { "title": "Learning path competencies", "showCompetenciesGraphButton": "Competency graph", - "emptyLearningObjectsLabel": "This competency doesn't contain any learning units yet!", - "nextLearningObjectOnPathLabel": "Next" + "emptyLearningObjectsLabel": "This competency doesn't contain any learning objects yet!", + "nextLearningObjectOnPathLabel": "Next", + "unreleasedLearningObjectLabel": "Unreleased learning unit" }, "recapLabel": "Recap learning units" }, diff --git a/src/test/java/de/tum/in/www1/artemis/competency/CompetencyUtilService.java b/src/test/java/de/tum/in/www1/artemis/competency/CompetencyUtilService.java index 312523a3e7b5..b11952ee1aa1 100644 --- a/src/test/java/de/tum/in/www1/artemis/competency/CompetencyUtilService.java +++ b/src/test/java/de/tum/in/www1/artemis/competency/CompetencyUtilService.java @@ -131,9 +131,9 @@ public Competency[] createCompetencies(Course course, int numberOfCompetencies) * @param competency The Competency to add to the LectureUnit * @param lectureUnit The LectureUnit to update */ - public void linkLectureUnitToCompetency(Competency competency, LectureUnit lectureUnit) { + public LectureUnit linkLectureUnitToCompetency(Competency competency, LectureUnit lectureUnit) { lectureUnit.getCompetencies().add(competency); - lectureUnitRepository.save(lectureUnit); + return lectureUnitRepository.save(lectureUnit); } /** diff --git a/src/test/java/de/tum/in/www1/artemis/competency/LearningPathIntegrationTest.java b/src/test/java/de/tum/in/www1/artemis/competency/LearningPathIntegrationTest.java index 481d5687ab39..5076a50148fb 100644 --- a/src/test/java/de/tum/in/www1/artemis/competency/LearningPathIntegrationTest.java +++ b/src/test/java/de/tum/in/www1/artemis/competency/LearningPathIntegrationTest.java @@ -45,6 +45,7 @@ import de.tum.in.www1.artemis.repository.GradingCriterionRepository; import de.tum.in.www1.artemis.repository.LearningPathRepository; import de.tum.in.www1.artemis.repository.LectureRepository; +import de.tum.in.www1.artemis.repository.LectureUnitRepository; import de.tum.in.www1.artemis.service.LectureUnitService; import de.tum.in.www1.artemis.service.competency.CompetencyProgressService; import de.tum.in.www1.artemis.util.PageableSearchUtilService; @@ -103,6 +104,9 @@ class LearningPathIntegrationTest extends AbstractSpringIntegrationIndependentTe @Autowired private StudentScoreUtilService studentScoreUtilService; + @Autowired + private LectureUnitRepository lectureUnitRepository; + private Course course; private Competency[] competencies; @@ -681,6 +685,29 @@ void testGetLearningPathNavigationEmptyCompetencies() throws Exception { verifyNavigationResult(result, thirdTextUnit, null, null); } + @Test + @WithMockUser(username = STUDENT_OF_COURSE, roles = "USER") + void testGetLearningPathNavigationDoesNotLeakUnreleasedLearningObjects() throws Exception { + course = learningPathUtilService.enableAndGenerateLearningPathsForCourse(course); + final var student = userRepository.findOneByLogin(STUDENT_OF_COURSE).orElseThrow(); + final var learningPath = learningPathRepository.findByCourseIdAndUserIdElseThrow(course.getId(), student.getId()); + + textExercise.setCompetencies(Set.of()); + textExercise = exerciseRepository.save(textExercise); + + TextUnit secondTextUnit = createAndLinkTextUnit(student, competencies[1], false); + secondTextUnit.setReleaseDate(ZonedDateTime.now().plusDays(1)); + lectureUnitRepository.save(secondTextUnit); + TextUnit thirdTextUnit = createAndLinkTextUnit(student, competencies[2], false); + TextUnit fourthTextUnit = createAndLinkTextUnit(student, competencies[3], false); + fourthTextUnit.setReleaseDate(ZonedDateTime.now().plusDays(1)); + lectureUnitRepository.save(fourthTextUnit); + TextUnit fifthTextUnit = createAndLinkTextUnit(student, competencies[4], false); + + var result = request.get("/api/learning-path/" + learningPath.getId() + "/navigation", HttpStatus.OK, LearningPathNavigationDTO.class); + verifyNavigationResult(result, textUnit, thirdTextUnit, fifthTextUnit); + } + private LearningPathNavigationObjectDTO.LearningObjectType getLearningObjectType(LearningObject learningObject) { return switch (learningObject) { case LectureUnit ignored -> LearningPathNavigationObjectDTO.LearningObjectType.LECTURE; @@ -836,7 +863,7 @@ private TextExercise createAndLinkTextExercise(Competency competency, boolean wi private TextUnit createAndLinkTextUnit(User student, Competency competency, boolean completed) { TextUnit textUnit = lectureUtilService.createTextUnit(); lectureUtilService.addLectureUnitsToLecture(lecture, List.of(textUnit)); - competencyUtilService.linkLectureUnitToCompetency(competency, textUnit); + textUnit = (TextUnit) competencyUtilService.linkLectureUnitToCompetency(competency, textUnit); if (completed) { lectureUnitService.setLectureUnitCompletion(textUnit, student, true); diff --git a/src/test/java/de/tum/in/www1/artemis/service/LearningObjectServiceTest.java b/src/test/java/de/tum/in/www1/artemis/service/LearningObjectServiceTest.java index d13ca4fe10dd..22fe0ffc6542 100644 --- a/src/test/java/de/tum/in/www1/artemis/service/LearningObjectServiceTest.java +++ b/src/test/java/de/tum/in/www1/artemis/service/LearningObjectServiceTest.java @@ -112,6 +112,11 @@ public Long getId() { public Set getCompetencies() { return Set.of(); } + + @Override + public boolean isVisibleToStudents() { + return false; + } }; assertThatThrownBy(() -> learningObjectService.isCompletedByUser(unexpectedSubclass, student)).isInstanceOf(IllegalArgumentException.class); } diff --git a/src/test/java/de/tum/in/www1/artemis/service/LearningPathServiceTest.java b/src/test/java/de/tum/in/www1/artemis/service/LearningPathServiceTest.java index 063a924dac16..243708bf4868 100644 --- a/src/test/java/de/tum/in/www1/artemis/service/LearningPathServiceTest.java +++ b/src/test/java/de/tum/in/www1/artemis/service/LearningPathServiceTest.java @@ -39,6 +39,7 @@ import de.tum.in.www1.artemis.lecture.LectureUtilService; import de.tum.in.www1.artemis.repository.CompetencyRepository; import de.tum.in.www1.artemis.repository.ExerciseRepository; +import de.tum.in.www1.artemis.repository.LectureUnitRepository; import de.tum.in.www1.artemis.security.SecurityUtils; import de.tum.in.www1.artemis.service.learningpath.LearningPathNgxService; import de.tum.in.www1.artemis.service.learningpath.LearningPathRecommendationService; @@ -85,6 +86,9 @@ class LearningPathServiceTest extends AbstractSpringIntegrationIndependentTest { @Autowired private StudentScoreUtilService studentScoreUtilService; + @Autowired + private LectureUnitRepository lectureUnitRepository; + private Course course; private User user; @@ -187,6 +191,7 @@ void testCompetencyWithLectureUnitAndExercise() { lectureUtilService.addLectureUnitsToLecture(lecture, List.of(lectureUnit)); competencyUtilService.linkLectureUnitToCompetency(competency, lectureUnit); final var exercise = programmingExerciseUtilService.addProgrammingExerciseToCourse(course); + exercise.setReleaseDate(null); competencyUtilService.linkExerciseToCompetency(competency, exercise); final var startNodeId = LearningPathNgxService.getCompetencyStartNodeId(competency.getId()); final var endNodeId = LearningPathNgxService.getCompetencyEndNodeId(competency.getId()); @@ -576,7 +581,29 @@ void testRecommendCorrectAmountOfLearningObjects() { exerciseRepository.saveAll(List.of(exercises)); LearningPath learningPath = learningPathUtilService.createLearningPathInCourseForUser(course, user); - learningPath = learningPathService.findWithCompetenciesAndLearningObjectsAndCompletedUsersById(learningPath.getId()); + learningPath = learningPathService.findWithCompetenciesAndReleasedLearningObjectsAndCompletedUsersById(learningPath.getId()); + NgxLearningPathDTO actual = learningPathService.generateNgxPathRepresentation(learningPath); + // competency start & end, lecture unit, and one exercise per difficulty level + assertThat(actual.nodes()).hasSize(6); + } + + @Test + void testDoesNotLeakUnreleasedLearningObjects() { + generateLectureUnits(3); + generateExercises(3); + + lectureUnits[0].setReleaseDate(ZonedDateTime.now().plusDays(1)); + lectureUnits[1].setReleaseDate(ZonedDateTime.now().minusDays(1)); + lectureUnits[2].setReleaseDate(null); + lectureUnitRepository.saveAll(List.of(lectureUnits)); + + exercises[0].setReleaseDate(ZonedDateTime.now().plusDays(1)); + exercises[1].setReleaseDate(ZonedDateTime.now().minusDays(1)); + exercises[2].setReleaseDate(null); + exerciseRepository.saveAll(List.of(exercises)); + + LearningPath learningPath = learningPathUtilService.createLearningPathInCourseForUser(course, user); + learningPath = learningPathService.findWithCompetenciesAndReleasedLearningObjectsAndCompletedUsersById(learningPath.getId()); NgxLearningPathDTO actual = learningPathService.generateNgxPathRepresentation(learningPath); // competency start & end, lecture unit, and one exercise per difficulty level assertThat(actual.nodes()).hasSize(6); @@ -587,7 +614,7 @@ private void generateLectureUnits(int numberOfLectureUnits) { for (int i = 0; i < lectureUnits.length; i++) { lectureUnits[i] = lectureUtilService.createTextUnit(); lectureUtilService.addLectureUnitsToLecture(lecture, List.of(lectureUnits[i])); - competencyUtilService.linkLectureUnitToCompetency(competency, lectureUnits[i]); + lectureUnits[i] = competencyUtilService.linkLectureUnitToCompetency(competency, lectureUnits[i]); } } @@ -595,6 +622,7 @@ private void generateExercises(int numberOfExercises) { exercises = new Exercise[numberOfExercises]; for (int i = 0; i < exercises.length; i++) { exercises[i] = programmingExerciseUtilService.addProgrammingExerciseToCourse(course); + exercises[i].setReleaseDate(null); exercises[i] = competencyUtilService.linkExerciseToCompetency(competency, exercises[i]); } } @@ -647,7 +675,7 @@ private void generatePathAndAssert(NgxLearningPathDTO expected) { private void generateAndAssert(NgxLearningPathDTO expected, LearningPathResource.NgxRequestType type) { LearningPath learningPath = learningPathUtilService.createLearningPathInCourseForUser(course, user); - learningPath = learningPathService.findWithCompetenciesAndLearningObjectsAndCompletedUsersById(learningPath.getId()); + learningPath = learningPathService.findWithCompetenciesAndReleasedLearningObjectsAndCompletedUsersById(learningPath.getId()); NgxLearningPathDTO actual = switch (type) { case GRAPH -> learningPathService.generateNgxGraphRepresentation(learningPath); case PATH -> learningPathService.generateNgxPathRepresentation(learningPath); diff --git a/src/test/javascript/spec/component/learning-paths/components/learning-path-nav.component.spec.ts b/src/test/javascript/spec/component/learning-paths/components/learning-path-nav.component.spec.ts index e0f814416d86..6706a6b833af 100644 --- a/src/test/javascript/spec/component/learning-paths/components/learning-path-nav.component.spec.ts +++ b/src/test/javascript/spec/component/learning-paths/components/learning-path-nav.component.spec.ts @@ -22,6 +22,7 @@ describe('LearningPathStudentNavComponent', () => { type: LearningObjectType.EXERCISE, completed: true, competencyId: 1, + unreleased: false, }, currentLearningObject: { id: 2, @@ -29,6 +30,7 @@ describe('LearningPathStudentNavComponent', () => { type: LearningObjectType.LECTURE, completed: false, competencyId: 2, + unreleased: false, }, successorLearningObject: { id: 3, @@ -36,6 +38,7 @@ describe('LearningPathStudentNavComponent', () => { type: LearningObjectType.EXERCISE, completed: false, competencyId: 2, + unreleased: false, }, progress: 50, };