From 97138577c2d9ad9a51bec8b442bb01661448362d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20St=C3=B6hr?= Date: Fri, 18 Oct 2024 20:02:43 +0200 Subject: [PATCH 01/25] First refactoring --- .../artemis/atlas/domain/LearningObject.java | 4 +- .../competency/CompetencyExerciseLink.java | 43 +++++++ .../CompetencyLearningObjectLink.java | 117 ++++++++++++++++++ .../competency/CompetencyLectureUnitLink.java | 43 +++++++ .../domain/competency/CourseCompetency.java | 55 +++----- .../CompetencyExerciseLinkRepository.java | 7 ++ .../CompetencyLectureUnitLinkRepository.java | 7 ++ .../CompetencyMetricsRepository.java | 8 +- .../repository/CompetencyRepository.java | 15 ++- .../CourseCompetencyRepository.java | 75 ++++++----- .../repository/PrerequisiteRepository.java | 15 ++- .../service/LearningObjectImportService.java | 41 +++--- .../competency/CompetencyProgressService.java | 21 ++-- .../service/competency/CompetencyService.java | 12 -- .../competency/CourseCompetencyService.java | 45 ++++--- .../LearningPathNavigationService.java | 2 +- .../learningpath/LearningPathNgxService.java | 6 +- .../LearningPathRecommendationService.java | 36 +++--- .../learningpath/LearningPathService.java | 27 ++-- .../artemis/atlas/web/CompetencyResource.java | 2 +- .../atlas/web/PrerequisiteResource.java | 2 +- .../aet/artemis/exercise/domain/Exercise.java | 19 ++- .../repository/ExerciseRepository.java | 3 +- .../service/ExerciseDeletionService.java | 6 +- .../service/ExerciseImportService.java | 2 +- .../exercise/service/ExerciseService.java | 18 ++- .../FileUploadExerciseRepository.java | 6 +- .../artemis/lecture/domain/ExerciseUnit.java | 12 +- .../artemis/lecture/domain/LectureUnit.java | 21 ++-- .../repository/AttachmentUnitRepository.java | 3 +- .../repository/ExerciseUnitRepository.java | 6 +- .../lecture/repository/LectureRepository.java | 16 ++- .../repository/LectureUnitRepository.java | 33 +++-- .../repository/OnlineUnitRepository.java | 3 +- .../repository/TextUnitRepository.java | 3 +- .../repository/VideoUnitRepository.java | 3 +- .../service/AttachmentUnitService.java | 8 +- .../lecture/service/LectureUnitService.java | 62 ++++------ .../artemis/lecture/web/LectureResource.java | 2 +- .../ModelingExerciseRepository.java | 9 +- .../ProgrammingExerciseRepository.java | 20 +-- .../repository/QuizExerciseRepository.java | 8 +- .../repository/TextExerciseRepository.java | 8 +- .../changelog/20241018120000_changelog.xml | 17 +++ .../resources/config/liquibase/master.xml | 1 + .../competencies-popover.component.html | 6 +- .../competencies-popover.component.ts | 4 +- .../course/competencies/competency.service.ts | 12 ++ .../competencies/course-competency.service.ts | 39 +++--- .../create/create-competency.component.html | 8 +- .../create/create-competency.component.ts | 4 +- .../create/create-prerequisite.component.html | 1 + .../create/create-prerequisite.component.ts | 6 +- .../edit/edit-competency.component.html | 1 + .../edit/edit-competency.component.ts | 10 +- .../edit/edit-prerequisite.component.html | 1 + .../edit/edit-prerequisite.component.ts | 10 +- ...mmon-course-competency-form.component.html | 23 ++-- ...common-course-competency-form.component.ts | 28 ++--- .../competency/competency-form.component.html | 2 +- .../competency/competency-form.component.ts | 10 +- .../forms/course-competency-form.component.ts | 13 +- .../prerequisite-form.component.html | 2 +- .../prerequisite-form.component.ts | 7 +- .../competencies/prerequisite.service.ts | 12 ++ .../webapp/app/entities/competency.model.ts | 32 ++++- .../webapp/app/entities/exercise.model.ts | 6 +- .../lecture-unit/lectureUnit.model.ts | 4 +- ...file-upload-exercise-update.component.html | 4 +- .../modeling-exercise-update.component.html | 4 +- ...rogramming-exercise-problem.component.html | 4 +- .../manage/quiz-exercise-detail.component.ts | 4 +- .../quiz-exercise-update.component.html | 4 +- src/main/webapp/app/exercises/shared/utils.ts | 4 +- .../text-exercise-update.component.html | 4 +- .../webapp/app/lecture/attachment.service.ts | 2 +- .../attachment-unit-form.component.html | 2 +- .../attachment-unit-form.component.ts | 6 +- .../create-attachment-unit.component.ts | 4 +- .../create-online-unit.component.ts | 4 +- .../create-text-unit.component.ts | 4 +- .../create-video-unit.component.ts | 4 +- .../edit-attachment-unit.component.ts | 6 +- .../edit-online-unit.component.ts | 6 +- .../edit-text-unit.component.ts | 7 +- .../edit-video-unit.component.ts | 6 +- .../lecture-unit-management.component.html | 6 +- .../online-unit-form.component.html | 2 +- .../online-unit-form.component.ts | 6 +- .../text-unit-form.component.html | 2 +- .../text-unit-form.component.ts | 6 +- .../video-unit-form.component.html | 2 +- .../video-unit-form.component.ts | 6 +- .../lecture-wizard-units.component.ts | 18 ++- ...course-competencies-details.component.html | 40 +++--- .../course-competencies-details.component.ts | 26 ++-- .../course-lecture-details.component.html | 4 +- .../competency-selection.component.html | 18 +-- .../competency-selection.component.ts | 60 ++++----- ...CompetencyPrerequisiteIntegrationTest.java | 45 ++++--- .../CourseCompetencyIntegrationTest.java | 6 +- .../util/CompetencyUtilService.java | 14 ++- .../LearningPathIntegrationTest.java | 7 +- .../service/LearningObjectServiceTest.java | 4 +- .../artemis/core/MetricsIntegrationTest.java | 6 +- .../FileUploadExerciseIntegrationTest.java | 8 +- .../AttachmentUnitIntegrationTest.java | 7 +- .../lecture/LectureIntegrationTest.java | 3 +- .../lecture/LectureUnitIntegrationTest.java | 5 +- .../lecture/OnlineUnitIntegrationTest.java | 7 +- .../lecture/TextUnitIntegrationTest.java | 7 +- .../lecture/VideoUnitIntegrationTest.java | 7 +- .../lecture/util/LectureUtilService.java | 6 +- .../ModelingExerciseIntegrationTest.java | 7 +- ...ExerciseLocalVCLocalCIIntegrationTest.java | 9 +- .../text/TextExerciseIntegrationTest.java | 7 +- .../competency-form.component.spec.ts | 8 +- .../competency-management.component.spec.ts | 11 +- .../competency-popover.component.spec.ts | 4 +- .../competencies/competency.service.spec.ts | 21 ++-- .../course-competencies.component.spec.ts | 4 +- .../create-competency.component.spec.ts | 6 +- .../create-prerequisite.component.spec.ts | 5 +- .../edit-competency.component.spec.ts | 10 +- .../edit-prerequisite.component.spec.ts | 10 +- .../prerequisite-form.component.spec.ts | 4 +- .../competencies/prerequisite.service.spec.ts | 19 ++- .../attachment-unit-form.component.spec.ts | 2 +- .../online-unit-form.component.spec.ts | 2 +- .../text-unit-form.component.spec.ts | 2 +- .../video-unit-form.component.spec.ts | 2 +- .../lecture-wizard-units.component.spec.ts | 6 +- ...rse-competencies-details.component.spec.ts | 12 +- .../competency-selection.component.spec.ts | 46 +++---- 134 files changed, 1064 insertions(+), 653 deletions(-) create mode 100644 src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyExerciseLink.java create mode 100644 src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLearningObjectLink.java create mode 100644 src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLectureUnitLink.java create mode 100644 src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyExerciseLinkRepository.java create mode 100644 src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyLectureUnitLinkRepository.java create mode 100644 src/main/resources/config/liquibase/changelog/20241018120000_changelog.xml diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/domain/LearningObject.java b/src/main/java/de/tum/cit/aet/artemis/atlas/domain/LearningObject.java index 765ee9eccf4e..d2e28b5bc585 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/domain/LearningObject.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/domain/LearningObject.java @@ -4,7 +4,7 @@ import java.util.Optional; import java.util.Set; -import de.tum.cit.aet.artemis.atlas.domain.competency.CourseCompetency; +import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyLearningObjectLink; import de.tum.cit.aet.artemis.core.domain.User; public interface LearningObject { @@ -19,7 +19,7 @@ public interface LearningObject { Long getId(); - Set getCompetencies(); + Set getCompetencyLinks(); boolean isVisibleToStudents(); } diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyExerciseLink.java b/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyExerciseLink.java new file mode 100644 index 000000000000..829c966b9149 --- /dev/null +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyExerciseLink.java @@ -0,0 +1,43 @@ +package de.tum.cit.aet.artemis.atlas.domain.competency; + +import jakarta.persistence.Entity; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.MapsId; +import jakarta.persistence.Table; + +import org.hibernate.annotations.Cache; +import org.hibernate.annotations.CacheConcurrencyStrategy; + +import de.tum.cit.aet.artemis.exercise.domain.Exercise; + +@Entity +@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) +@Table(name = "competency_exercise") +public class CompetencyExerciseLink extends CompetencyLearningObjectLink { + + @ManyToOne + @MapsId("learningObjectId") + private Exercise exercise; + + public Exercise getExercise() { + return exercise; + } + + public void setExercise(Exercise exercise) { + this.exercise = exercise; + } + + public CompetencyExerciseLink(CourseCompetency competency, Exercise exercise, double weight) { + super(competency, weight); + this.exercise = exercise; + } + + public CompetencyExerciseLink() { + // Empty constructor for Spring + } + + @Override + public String toString() { + return "CompetencyExerciseLink{" + "exercise=" + exercise + ", id=" + id + ", competency=" + competency + ", weight=" + weight + '}'; + } +} diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLearningObjectLink.java b/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLearningObjectLink.java new file mode 100644 index 000000000000..9dea9865b873 --- /dev/null +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLearningObjectLink.java @@ -0,0 +1,117 @@ +package de.tum.cit.aet.artemis.atlas.domain.competency; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Objects; + +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import jakarta.persistence.EmbeddedId; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.MappedSuperclass; +import jakarta.persistence.MapsId; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +@MappedSuperclass +public abstract class CompetencyLearningObjectLink implements Serializable { + + /** + * The primary key of the association, composited through {@link CompetencyLearningObjectId}. + */ + @EmbeddedId + @JsonIgnore + protected CompetencyLearningObjectId id = new CompetencyLearningObjectId(); + + @ManyToOne + @MapsId("competencyId") + protected CourseCompetency competency; + + @Column(name = "link_weight") + protected double weight; + + public CompetencyLearningObjectId getId() { + return id; + } + + public CourseCompetency getCompetency() { + return competency; + } + + public void setCompetency(CourseCompetency competency) { + this.competency = competency; + } + + public double getWeight() { + return weight; + } + + public void setWeight(double weight) { + this.weight = weight; + } + + public CompetencyLearningObjectLink(CourseCompetency competency, double weight) { + this.competency = competency; + this.weight = weight; + } + + public CompetencyLearningObjectLink() { + // Empty constructor for Spring + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof CompetencyLearningObjectLink that)) { + return false; + } + return Objects.equals(id, that.id); + } + + @Override + public int hashCode() { + return Objects.hashCode(id); + } + + /** + * This class is used to create a composite primary key (user_id, competency_id). + * See also ... + */ + @Embeddable + public static class CompetencyLearningObjectId implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + private long learningObjectId; + + private long competencyId; + + public CompetencyLearningObjectId() { + // Empty constructor for Spring + } + + public CompetencyLearningObjectId(long learningObjectId, long competencyId) { + this.learningObjectId = learningObjectId; + this.competencyId = competencyId; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof CompetencyLearningObjectId that)) { + return false; + } + return learningObjectId == that.learningObjectId && competencyId == that.competencyId; + } + + @Override + public int hashCode() { + return Objects.hash(learningObjectId, competencyId); + } + } +} diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLectureUnitLink.java b/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLectureUnitLink.java new file mode 100644 index 000000000000..8f3707a6c9d8 --- /dev/null +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLectureUnitLink.java @@ -0,0 +1,43 @@ +package de.tum.cit.aet.artemis.atlas.domain.competency; + +import jakarta.persistence.Entity; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.MapsId; +import jakarta.persistence.Table; + +import org.hibernate.annotations.Cache; +import org.hibernate.annotations.CacheConcurrencyStrategy; + +import de.tum.cit.aet.artemis.lecture.domain.LectureUnit; + +@Entity +@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) +@Table(name = "competency_lecture_unit") +public class CompetencyLectureUnitLink extends CompetencyLearningObjectLink { + + @ManyToOne + @MapsId("learningObjectId") + private LectureUnit lectureUnit; + + public LectureUnit getLectureUnit() { + return lectureUnit; + } + + public void setLectureUnit(LectureUnit lectureUnit) { + this.lectureUnit = lectureUnit; + } + + public CompetencyLectureUnitLink(CourseCompetency competency, LectureUnit lectureUnit, double weight) { + super(competency, weight); + this.lectureUnit = lectureUnit; + } + + public CompetencyLectureUnitLink() { + // Empty constructor for Spring + } + + @Override + public String toString() { + return "CompetencyLectureUnitLink{" + "lectureUnit=" + lectureUnit + ", id=" + id + ", competency=" + competency + ", weight=" + weight + '}'; + } +} diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CourseCompetency.java b/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CourseCompetency.java index fd81132c2bbb..44a439589183 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CourseCompetency.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CourseCompetency.java @@ -29,7 +29,6 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; import de.tum.cit.aet.artemis.core.domain.Course; -import de.tum.cit.aet.artemis.exercise.domain.Exercise; import de.tum.cit.aet.artemis.lecture.domain.ExerciseUnit; import de.tum.cit.aet.artemis.lecture.domain.LectureUnit; @@ -71,13 +70,13 @@ public abstract class CourseCompetency extends BaseCompetency { @JsonIgnoreProperties({ "competencies" }) private StandardizedCompetency linkedStandardizedCompetency; - @ManyToMany(mappedBy = "competencies") - @JsonIgnoreProperties({ "competencies", "course" }) - private Set exercises = new HashSet<>(); + @OneToMany(mappedBy = "competency", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true) + @JsonIgnoreProperties("competency") + private Set exerciseLinks = new HashSet<>(); - @ManyToMany(mappedBy = "competencies") - @JsonIgnoreProperties("competencies") - private Set lectureUnits = new HashSet<>(); + @OneToMany(mappedBy = "competency", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true) + @JsonIgnoreProperties("competency") + private Set lectureUnitLinks = new HashSet<>(); @OneToMany(mappedBy = "competency", fetch = FetchType.LAZY, cascade = CascadeType.REMOVE, orphanRemoval = true) @JsonIgnoreProperties({ "user", "competency" }) @@ -161,40 +160,20 @@ public void setLinkedStandardizedCompetency(StandardizedCompetency linkedStandar this.linkedStandardizedCompetency = linkedStandardizedCompetency; } - public Set getExercises() { - return exercises; - } - - public void setExercises(Set exercises) { - this.exercises = exercises; + public Set getExerciseLinks() { + return exerciseLinks; } - public void addExercise(Exercise exercise) { - this.exercises.add(exercise); - exercise.getCompetencies().add(this); + public void setExerciseLinks(Set exerciseLinks) { + this.exerciseLinks = exerciseLinks; } - public Set getLectureUnits() { - return lectureUnits; + public Set getLectureUnitLinks() { + return lectureUnitLinks; } - public void setLectureUnits(Set lectureUnits) { - this.lectureUnits = lectureUnits; - } - - /** - * Adds the lecture unit to the competency (bidirectional) - * Note: ExerciseUnits are not accepted, should be set via the connected exercise (see {@link #addExercise(Exercise)}) - * - * @param lectureUnit The lecture unit to add - */ - public void addLectureUnit(LectureUnit lectureUnit) { - if (lectureUnit instanceof ExerciseUnit) { - // The competencies of ExerciseUnits are taken from the corresponding exercise - throw new IllegalArgumentException("ExerciseUnits can not be connected to competencies"); - } - this.lectureUnits.add(lectureUnit); - lectureUnit.getCompetencies().add(this); + public void setLectureUnitLinks(Set lectureUnitLinks) { + this.lectureUnitLinks = lectureUnitLinks; } /** @@ -208,8 +187,8 @@ public void removeLectureUnit(LectureUnit lectureUnit) { // The competencies of ExerciseUnits are taken from the corresponding exercise throw new IllegalArgumentException("ExerciseUnits can not be disconnected from competencies"); } - this.lectureUnits.remove(lectureUnit); - lectureUnit.getCompetencies().remove(this); + this.lectureUnitLinks.remove(lectureUnit); + lectureUnit.getCompetencyLinks().remove(this); } public Set getUserProgress() { @@ -234,6 +213,6 @@ public void setLearningPaths(Set learningPaths) { @PrePersist @PreUpdate public void prePersistOrUpdate() { - this.lectureUnits.removeIf(lectureUnit -> lectureUnit instanceof ExerciseUnit); + this.lectureUnitLinks.removeIf(lectureUnit -> lectureUnit.getLectureUnit() instanceof ExerciseUnit); } } diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyExerciseLinkRepository.java b/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyExerciseLinkRepository.java new file mode 100644 index 000000000000..96d6f51ba9a8 --- /dev/null +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyExerciseLinkRepository.java @@ -0,0 +1,7 @@ +package de.tum.cit.aet.artemis.atlas.repository; + +import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyExerciseLink; +import de.tum.cit.aet.artemis.core.repository.base.ArtemisJpaRepository; + +public interface CompetencyExerciseLinkRepository extends ArtemisJpaRepository { +} diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyLectureUnitLinkRepository.java b/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyLectureUnitLinkRepository.java new file mode 100644 index 000000000000..34971f8b786e --- /dev/null +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyLectureUnitLinkRepository.java @@ -0,0 +1,7 @@ +package de.tum.cit.aet.artemis.atlas.repository; + +import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyLectureUnitLink; +import de.tum.cit.aet.artemis.core.repository.base.ArtemisJpaRepository; + +public interface CompetencyLectureUnitLinkRepository extends ArtemisJpaRepository { +} diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyMetricsRepository.java b/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyMetricsRepository.java index 362064d79ec5..c2888428ec3b 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyMetricsRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyMetricsRepository.java @@ -45,7 +45,7 @@ public interface CompetencyMetricsRepository extends ArtemisJpaRepository findAllExerciseIdsByCompetencyIds(@Param("competencyIds") Set competencyIds); @@ -57,10 +57,10 @@ public interface CompetencyMetricsRepository extends ArtemisJpaRepository findAllLectureUnitIdsByCompetencyIds(@Param("competencyIds") Set competencyIds); diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyRepository.java b/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyRepository.java index 91ae85978b42..38651ce86558 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyRepository.java @@ -26,8 +26,10 @@ public interface CompetencyRepository extends ArtemisJpaRepository findByIdWithLectureUnits(@Param("competencyId") long competencyId); @@ -45,8 +48,10 @@ public interface CompetencyRepository extends ArtemisJpaRepository findByIdWithLectureUnitsAndExercises(@Param("competencyId") long competencyId); diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CourseCompetencyRepository.java b/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CourseCompetencyRepository.java index d8b66519355c..f3f393a41657 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CourseCompetencyRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CourseCompetencyRepository.java @@ -33,7 +33,8 @@ public interface CourseCompetencyRepository extends ArtemisJpaRepository findByIdWithLectureUnits(@Param("competencyId") long competencyId); @@ -48,8 +49,10 @@ public interface CourseCompetencyRepository extends ArtemisJpaRepository findAllExerciseInfoByCompetencyId(@Param("competencyId") long competencyId, @Param("user") User user); @Query(""" SELECT c FROM CourseCompetency c - LEFT JOIN FETCH c.lectureUnits lu - LEFT JOIN FETCH c.exercises ex + LEFT JOIN FETCH c.lectureUnitLinks lul + LEFT JOIN FETCH lul.lectureUnit + LEFT JOIN FETCH c.exerciseLinks e + LEFT JOIN FETCH e.exercise WHERE c.id = :competencyId """) Optional findByIdWithExercisesAndLectureUnits(@Param("competencyId") long competencyId); @@ -125,10 +135,14 @@ GROUP BY ex.maxPoints, ex.difficulty, TYPE(ex), sS.lastScore, tS.lastScore, sS.l @Query(""" SELECT c FROM CourseCompetency c - LEFT JOIN FETCH c.exercises ex - LEFT JOIN FETCH ex.competencies - LEFT JOIN FETCH c.lectureUnits lu - LEFT JOIN FETCH lu.competencies + LEFT JOIN FETCH c.exerciseLinks el + LEFT JOIN FETCH el.exercise e + LEFT JOIN FETCH e.competencyLinks ecl + LEFT JOIN FETCH ecl.competency + LEFT JOIN FETCH c.lectureUnitLinks lul + LEFT JOIN FETCH lul.lectureUnit lu + LEFT JOIN FETCH lu.competencyLinks lucl + LEFT JOIN FETCH lucl.competency WHERE c.id = :competencyId """) Optional findByIdWithExercisesAndLectureUnitsBidirectional(@Param("competencyId") long competencyId); @@ -136,15 +150,17 @@ GROUP BY ex.maxPoints, ex.difficulty, TYPE(ex), sS.lastScore, tS.lastScore, sS.l @Query(""" SELECT c.id FROM CourseCompetency c - LEFT JOIN c.exercises ex - WHERE :exercise = ex + LEFT JOIN c.exerciseLinks el + LEFT JOIN el.exercise e + WHERE :exercise = e """) Set findAllIdsByExercise(@Param("exercise") Exercise exercise); @Query(""" SELECT c.id FROM CourseCompetency c - LEFT JOIN c.lectureUnits lu + LEFT JOIN c.lectureUnitLinks lul + LEFT JOIN lul.lectureUnit lu WHERE :lectureUnit = lu """) Set findAllIdsByLectureUnit(@Param("lectureUnit") LectureUnit lectureUnit); @@ -192,7 +208,8 @@ Page findForImportAndUserHasAccessToCourse(@Param("partialTitl @Query(""" SELECT c FROM CourseCompetency c - LEFT JOIN FETCH c.exercises ex + LEFT JOIN FETCH c.exerciseLinks el + LEFT JOIN FETCH el.exercise WHERE c.id = :competencyId """) Optional findByIdWithExercises(@Param("competencyId") long competencyId); @@ -200,8 +217,10 @@ Page findForImportAndUserHasAccessToCourse(@Param("partialTitl @Query(""" SELECT c FROM CourseCompetency c - LEFT JOIN FETCH c.lectureUnits lu - LEFT JOIN FETCH c.exercises + LEFT JOIN FETCH c.lectureUnitLinks lul + LEFT JOIN FETCH lul.lectureUnit + LEFT JOIN FETCH c.exerciseLinks el + LEFT JOIN FETCH el.exercise WHERE c.id = :competencyId """) Optional findByIdWithLectureUnitsAndExercises(@Param("competencyId") long competencyId); diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/repository/PrerequisiteRepository.java b/src/main/java/de/tum/cit/aet/artemis/atlas/repository/PrerequisiteRepository.java index 9616c2a5f34b..a6640f2ca10b 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/repository/PrerequisiteRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/repository/PrerequisiteRepository.java @@ -21,8 +21,10 @@ public interface PrerequisiteRepository extends ArtemisJpaRepository findByIdWithLectureUnitsAndExercises(@Param("competencyId") long competencyId); @@ -41,7 +45,8 @@ public interface PrerequisiteRepository extends ArtemisJpaRepository findByIdWithLectureUnits(@Param("competencyId") long competencyId); diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/service/LearningObjectImportService.java b/src/main/java/de/tum/cit/aet/artemis/atlas/service/LearningObjectImportService.java index 6b67d8f01b44..5db97a2c9c8f 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/service/LearningObjectImportService.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/service/LearningObjectImportService.java @@ -28,6 +28,8 @@ import de.tum.cit.aet.artemis.assessment.domain.GradingCriterion; import de.tum.cit.aet.artemis.assessment.repository.GradingCriterionRepository; +import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyExerciseLink; +import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyLectureUnitLink; import de.tum.cit.aet.artemis.atlas.domain.competency.CourseCompetency; import de.tum.cit.aet.artemis.atlas.dto.CompetencyImportOptionsDTO; import de.tum.cit.aet.artemis.atlas.dto.CompetencyWithTailRelationDTO; @@ -168,19 +170,22 @@ public void importRelatedLearningObjects(Collection private void importOrLoadExercises(Collection sourceCourseCompetencies, Map idToImportedCompetency, Course courseToImportInto, Set importedExercises) { for (CourseCompetency sourceCourseCompetency : sourceCourseCompetencies) { - for (Exercise sourceExercise : sourceCourseCompetency.getExercises()) { + sourceCourseCompetency.getExerciseLinks().forEach(sourceExerciseLink -> { try { - Exercise importedExercise = importOrLoadExercise(sourceExercise, courseToImportInto); + Exercise importedExercise = importOrLoadExercise(sourceExerciseLink.getExercise(), courseToImportInto); importedExercises.add(importedExercise); - importedExercise.getCompetencies().add(idToImportedCompetency.get(sourceCourseCompetency.getId()).competency()); - idToImportedCompetency.get(sourceCourseCompetency.getId()).competency().getExercises().add(importedExercise); + CourseCompetency importedCompetency = idToImportedCompetency.get(sourceCourseCompetency.getId()).competency(); + CompetencyExerciseLink link = new CompetencyExerciseLink(importedCompetency, importedExercise, sourceExerciseLink.getWeight()); + importedExercise.getCompetencyLinks().add(link); + importedCompetency.getExerciseLinks().add(link); } catch (Exception e) { - log.error("Failed to import exercise with title {} together with its competency with id {}", sourceExercise.getTitle(), sourceCourseCompetency.getId(), e); + log.error("Failed to import exercise with title {} together with its competency with id {}", sourceExerciseLink.getExercise().getTitle(), + sourceCourseCompetency.getId(), e); } - } + }); } } @@ -257,7 +262,7 @@ private void clearProgrammingExerciseAttributes(ProgrammingExercise programmingE programmingExercise.setAttachments(new HashSet<>()); programmingExercise.setPosts(new HashSet<>()); programmingExercise.setPlagiarismCases(new HashSet<>()); - programmingExercise.setCompetencies(new HashSet<>()); + programmingExercise.setCompetencyLinks(new HashSet<>()); } /** @@ -281,7 +286,7 @@ private Exercise importOrLoadExercise(E exercise, Course co exercise = loadForImport.apply(exercise.getId()); exercise.setCourse(course); exercise.setId(null); - exercise.setCompetencies(new HashSet<>()); + exercise.setCompetencyLinks(new HashSet<>()); return importFunction.apply(exercise, exercise); } @@ -299,19 +304,23 @@ private Exercise importOrLoadExercise(E exercise, Course co private void importOrLoadLectureUnits(Collection sourceCourseCompetencies, Map idToImportedCompetency, Course courseToImportInto, Map titleToImportedLectures, Set importedLectureUnits) { for (CourseCompetency sourceCourseCompetency : sourceCourseCompetencies) { - for (LectureUnit sourceLectureUnit : sourceCourseCompetency.getLectureUnits()) { + for (CompetencyLectureUnitLink sourceLectureUnitLink : sourceCourseCompetency.getLectureUnitLinks()) { try { - importOrLoadLectureUnit(sourceLectureUnit, sourceCourseCompetency, idToImportedCompetency, courseToImportInto, titleToImportedLectures, importedLectureUnits); + importOrLoadLectureUnit(sourceLectureUnitLink, sourceCourseCompetency, idToImportedCompetency, courseToImportInto, titleToImportedLectures, + importedLectureUnits); } catch (Exception e) { - log.error("Failed to import lecture unit with name {} together with its competency with id {}", sourceLectureUnit.getName(), sourceCourseCompetency.getId(), e); + log.error("Failed to import lecture unit with name {} together with its competency with id {}", sourceLectureUnitLink.getLectureUnit().getName(), + sourceCourseCompetency.getId(), e); } } } } - private void importOrLoadLectureUnit(LectureUnit sourceLectureUnit, CourseCompetency sourceCourseCompetency, Map idToImportedCompetency, - Course courseToImportInto, Map titleToImportedLectures, Set importedLectureUnits) throws NoUniqueQueryException { + private void importOrLoadLectureUnit(CompetencyLectureUnitLink sourceLectureUnitLink, CourseCompetency sourceCourseCompetency, + Map idToImportedCompetency, Course courseToImportInto, Map titleToImportedLectures, + Set importedLectureUnits) throws NoUniqueQueryException { + LectureUnit sourceLectureUnit = sourceLectureUnitLink.getLectureUnit(); Lecture sourceLecture = sourceLectureUnit.getLecture(); Lecture importedLecture = importOrLoadLecture(sourceLecture, courseToImportInto, titleToImportedLectures); @@ -330,8 +339,10 @@ private void importOrLoadLectureUnit(LectureUnit sourceLectureUnit, CourseCompet importedLectureUnits.add(importedLectureUnit); - importedLectureUnit.getCompetencies().add(idToImportedCompetency.get(sourceCourseCompetency.getId()).competency()); - idToImportedCompetency.get(sourceCourseCompetency.getId()).competency().getLectureUnits().add(importedLectureUnit); + CourseCompetency importedCompetency = idToImportedCompetency.get(sourceCourseCompetency.getId()).competency(); + CompetencyLectureUnitLink link = new CompetencyLectureUnitLink(importedCompetency, importedLectureUnit, sourceLectureUnitLink.getWeight()); + importedLectureUnit.getCompetencyLinks().add(link); + importedCompetency.getLectureUnitLinks().add(link); } private Lecture importOrLoadLecture(Lecture sourceLecture, Course courseToImportInto, Map titleToImportedLectures) throws NoUniqueQueryException { diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CompetencyProgressService.java b/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CompetencyProgressService.java index bd6ff4b3148d..7fbfd6618134 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CompetencyProgressService.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CompetencyProgressService.java @@ -22,6 +22,9 @@ import de.tum.cit.aet.artemis.assessment.service.ParticipantScoreService; import de.tum.cit.aet.artemis.atlas.domain.CompetencyProgressConfidenceReason; import de.tum.cit.aet.artemis.atlas.domain.LearningObject; +import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyExerciseLink; +import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyLearningObjectLink; +import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyLectureUnitLink; import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyProgress; import de.tum.cit.aet.artemis.atlas.domain.competency.CourseCompetency; import de.tum.cit.aet.artemis.atlas.dto.metrics.CompetencyExerciseMasteryCalculationDTO; @@ -150,8 +153,10 @@ public void updateProgressByCompetencyAndUsersInCourseAsync(CourseCompetency com public void updateProgressForUpdatedLearningObjectAsync(LearningObject originalLearningObject, Optional updatedLearningObject) { SecurityUtils.setAuthorizationObject(); // Required for async - Set originalCompetencyIds = originalLearningObject.getCompetencies().stream().map(CourseCompetency::getId).collect(Collectors.toSet()); - Set updatedCompetencies = updatedLearningObject.map(LearningObject::getCompetencies).orElse(Set.of()); + Set originalCompetencyIds = originalLearningObject.getCompetencyLinks().stream().map(CompetencyLearningObjectLink::getCompetency).map(CourseCompetency::getId) + .collect(Collectors.toSet()); + Set updatedCompetencies = updatedLearningObject + .map(learningObject -> learningObject.getCompetencyLinks().stream().map(CompetencyLearningObjectLink::getCompetency).collect(Collectors.toSet())).orElse(Set.of()); Set updatedCompetencyIds = updatedCompetencies.stream().map(CourseCompetency::getId).collect(Collectors.toSet()); Set removedCompetencyIds = originalCompetencyIds.stream().filter(id -> !updatedCompetencyIds.contains(id)).collect(Collectors.toSet()); @@ -218,10 +223,11 @@ public CompetencyProgress updateCompetencyProgress(Long competencyId, User user) } CourseCompetency competency = optionalCompetency.get(); - Set lectureUnits = competency.getLectureUnits().stream().filter(lectureUnit -> !(lectureUnit instanceof ExerciseUnit)).collect(Collectors.toSet()); + Set lectureUnits = competency.getLectureUnitLinks().stream().map(CompetencyLectureUnitLink::getLectureUnit) + .filter(lectureUnit -> !(lectureUnit instanceof ExerciseUnit)).collect(Collectors.toSet()); Set exerciseInfos = courseCompetencyRepository.findAllExerciseInfoByCompetencyId(competencyId, user); int numberOfCompletedLectureUnits = lectureUnitCompletionRepository - .countByLectureUnitIdsAndUserId(competency.getLectureUnits().stream().map(LectureUnit::getId).collect(Collectors.toSet()), user.getId()); + .countByLectureUnitIdsAndUserId(lectureUnits.stream().map(LectureUnit::getId).collect(Collectors.toSet()), user.getId()); var competencyProgress = competencyProgressRepository.findEagerByCompetencyIdAndUserId(competencyId, user.getId()); @@ -491,8 +497,8 @@ public static boolean isMastered(@NotNull CompetencyProgress competencyProgress) * @return true if the competency can be mastered without completing any exercises, false otherwise */ public static boolean canBeMasteredWithoutExercises(@NotNull CourseCompetency competency) { - double numberOfLectureUnits = competency.getLectureUnits().size(); - double numberOfLearningObjects = numberOfLectureUnits + competency.getExercises().size(); + double numberOfLectureUnits = competency.getLectureUnitLinks().size(); + double numberOfLearningObjects = numberOfLectureUnits + competency.getExerciseLinks().size(); if (numberOfLearningObjects == 0) { return true; } @@ -521,7 +527,8 @@ public void deleteProgressForCompetency(long competencyId) { public CourseCompetencyProgressDTO getCompetencyCourseProgress(@NotNull CourseCompetency competency, @NotNull Course course) { var numberOfStudents = competencyProgressRepository.countByCompetency(competency.getId()); var numberOfMasteredStudents = competencyProgressRepository.countByCompetencyAndMastered(competency.getId(), competency.getMasteryThreshold()); - var averageStudentScore = RoundingUtil.roundScoreSpecifiedByCourseSettings(participantScoreService.getAverageOfAverageScores(competency.getExercises()), course); + Set exercises = competency.getExerciseLinks().stream().map(CompetencyExerciseLink::getExercise).collect(Collectors.toSet()); + var averageStudentScore = RoundingUtil.roundScoreSpecifiedByCourseSettings(participantScoreService.getAverageOfAverageScores(exercises), course); return new CourseCompetencyProgressDTO(competency.getId(), numberOfStudents, numberOfMasteredStudents, averageStudentScore); } } diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CompetencyService.java b/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CompetencyService.java index 9ec942846bf8..e9ef9be7a960 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CompetencyService.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CompetencyService.java @@ -80,18 +80,6 @@ public List importStandardizedCompetencies(List competen return super.importStandardizedCompetencies(competencyIdsToImport, course, Competency::new); } - /** - * Creates a new competency and links it to a course and lecture units. - * - * @param competency the competency to create - * @param course the course to link the competency to - * @return the persisted competency - */ - public Competency createCompetency(CourseCompetency competency, Course course) { - Competency competencyToCreate = new Competency(competency); - return createCourseCompetency(competencyToCreate, course); - } - /** * Creates a list of new competencies and links them to a course and lecture units. * diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CourseCompetencyService.java b/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CourseCompetencyService.java index 01eb37cf8271..a0dc48a85d3a 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CourseCompetencyService.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CourseCompetencyService.java @@ -9,6 +9,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.function.Function; import java.util.function.Supplier; @@ -20,6 +21,8 @@ import org.springframework.stereotype.Service; import de.tum.cit.aet.artemis.atlas.domain.competency.Competency; +import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyExerciseLink; +import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyLectureUnitLink; import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyRelation; import de.tum.cit.aet.artemis.atlas.domain.competency.CourseCompetency; import de.tum.cit.aet.artemis.atlas.domain.competency.Prerequisite; @@ -43,6 +46,7 @@ import de.tum.cit.aet.artemis.core.util.PageUtil; import de.tum.cit.aet.artemis.exercise.domain.Exercise; import de.tum.cit.aet.artemis.exercise.service.ExerciseService; +import de.tum.cit.aet.artemis.lecture.domain.LectureUnit; import de.tum.cit.aet.artemis.lecture.repository.LectureUnitCompletionRepository; import de.tum.cit.aet.artemis.lecture.service.LectureUnitService; @@ -150,13 +154,23 @@ public SearchResultPageDTO getOnPageWithSizeForImport(final Co * @param currentUser The user for whom to filter the learning objects */ public void filterOutLearningObjectsThatUserShouldNotSee(CourseCompetency competency, User currentUser) { - competency.setLectureUnits(competency.getLectureUnits().stream().filter(lectureUnit -> authCheckService.isAllowedToSeeLectureUnit(lectureUnit, currentUser)) - .peek(lectureUnit -> lectureUnit.setCompleted(lectureUnit.isCompletedFor(currentUser))).collect(Collectors.toSet())); - - Set exercisesUserIsAllowedToSee = exerciseService.filterOutExercisesThatUserShouldNotSee(competency.getExercises(), currentUser); - Set exercisesWithAllInformationNeeded = exerciseService - .loadExercisesWithInformationForDashboard(exercisesUserIsAllowedToSee.stream().map(Exercise::getId).collect(Collectors.toSet()), currentUser); - competency.setExercises(exercisesWithAllInformationNeeded); + competency.setLectureUnitLinks(competency.getLectureUnitLinks().stream() + .filter(lectureUnitLink -> authCheckService.isAllowedToSeeLectureUnit(lectureUnitLink.getLectureUnit(), currentUser)) + .peek(lectureUnitLink -> lectureUnitLink.getLectureUnit().setCompleted(lectureUnitLink.getLectureUnit().isCompletedFor(currentUser))).collect(Collectors.toSet())); + + Set exercises = competency.getExerciseLinks().stream().map(CompetencyExerciseLink::getExercise).collect(Collectors.toSet()); + Set exerciseIdsUserIsAllowedToSee = exerciseService.filterOutExercisesThatUserShouldNotSee(exercises, currentUser).stream().map(Exercise::getId) + .collect(Collectors.toSet()); + Set exercisesWithAllInformationNeeded = exerciseService.loadExercisesWithInformationForDashboard(exerciseIdsUserIsAllowedToSee, currentUser); + + Set exerciseLinksWithAllInformation = competency.getExerciseLinks().stream() + .filter(exerciseLink -> exerciseIdsUserIsAllowedToSee.contains(exerciseLink.getExercise().getId())).peek(exerciseLink -> { + Optional exerciseWithAllInformationNeeded = exercisesWithAllInformationNeeded.stream() + .filter(exercise -> exercise.getId().equals(exerciseLink.getExercise().getId())).findFirst(); + exerciseWithAllInformationNeeded.ifPresent(exerciseLink::setExercise); + }).collect(Collectors.toSet()); + + competency.setExerciseLinks(exerciseLinksWithAllInformation); } /** @@ -273,6 +287,7 @@ public List importStandardizedCompetencies(List competen */ public C createCourseCompetency(C competencyToCreate, Course course) { competencyToCreate.setCourse(course); + competencyToCreate.getLectureUnitLinks().forEach(link -> link.setCompetency(competencyToCreate)); var persistedCompetency = courseCompetencyRepository.save(competencyToCreate); @@ -332,7 +347,8 @@ public C updateCourseCompetency(C competencyToUpdat final var persistedCompetency = courseCompetencyRepository.save(competencyToUpdate); // update competency progress if necessary - if (competency.getLectureUnits().size() != competencyToUpdate.getLectureUnits().size() || !competencyToUpdate.getLectureUnits().containsAll(competency.getLectureUnits())) { + if (competency.getLectureUnitLinks().size() != competencyToUpdate.getLectureUnitLinks().size() + || !competencyToUpdate.getLectureUnitLinks().containsAll(competency.getLectureUnitLinks())) { competencyProgressService.updateProgressByCompetencyAndUsersInCourseAsync(persistedCompetency); } @@ -349,10 +365,11 @@ public C updateCourseCompetency(C competencyToUpdat */ public C findProgressAndLectureUnitCompletionsForUser(C competency, Long userId) { competencyProgressRepository.findByCompetencyIdAndUserId(competency.getId(), userId).ifPresent(progress -> competency.setUserProgress(Set.of(progress))); + Set lectureUnits = competency.getLectureUnitLinks().stream().map(CompetencyLectureUnitLink::getLectureUnit).collect(Collectors.toSet()); // collect to map lecture unit id -> this - var completions = lectureUnitCompletionRepository.findByLectureUnitsAndUserId(competency.getLectureUnits(), userId).stream() + var completions = lectureUnitCompletionRepository.findByLectureUnitsAndUserId(lectureUnits, userId).stream() .collect(Collectors.toMap(completion -> completion.getLectureUnit().getId(), completion -> completion)); - competency.getLectureUnits().forEach(lectureUnit -> { + lectureUnits.forEach(lectureUnit -> { if (completions.containsKey(lectureUnit.getId())) { lectureUnit.setCompletedUsers(Set.of(completions.get(lectureUnit.getId()))); } @@ -398,8 +415,8 @@ public void deleteCourseCompetency(CourseCompetency courseCompetency, Course cou competencyRelationRepository.deleteAllByCompetencyId(courseCompetency.getId()); competencyProgressService.deleteProgressForCompetency(courseCompetency.getId()); - exerciseService.removeCompetency(courseCompetency.getExercises(), courseCompetency); - lectureUnitService.removeCompetency(courseCompetency.getLectureUnits(), courseCompetency); + exerciseService.removeCompetency(courseCompetency.getExerciseLinks(), courseCompetency); + lectureUnitService.removeCompetency(courseCompetency.getLectureUnitLinks(), courseCompetency); if (course.getLearningPathsEnabled()) { learningPathService.removeLinkedCompetencyFromLearningPathsOfCourse(courseCompetency, course.getId()); @@ -422,8 +439,8 @@ public void checkIfCompetencyBelongsToCourse(long competencyId, long courseId) { } private void updateLectureUnits(CourseCompetency competency, CourseCompetency createdCompetency) { - if (!competency.getLectureUnits().isEmpty()) { - lectureUnitService.linkLectureUnitsToCompetency(createdCompetency, competency.getLectureUnits(), Set.of()); + if (!competency.getLectureUnitLinks().isEmpty()) { + lectureUnitService.linkLectureUnitsToCompetency(createdCompetency, competency.getLectureUnitLinks(), Set.of()); competencyProgressService.updateProgressByCompetencyAndUsersInCourseAsync(createdCompetency); } } diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/service/learningpath/LearningPathNavigationService.java b/src/main/java/de/tum/cit/aet/artemis/atlas/service/learningpath/LearningPathNavigationService.java index f4d9bc336544..586caf54b905 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/service/learningpath/LearningPathNavigationService.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/service/learningpath/LearningPathNavigationService.java @@ -82,7 +82,7 @@ public LearningPathNavigationDTO getNavigation(LearningPath learningPath) { private CourseCompetency findCorrespondingCompetencyForLearningObject(RecommendationState recommendationState, LearningObject learningObject, boolean firstCompetency) { Stream potentialCompetencies = recommendationState.recommendedOrderOfCompetencies().stream() .map(competencyId -> recommendationState.competencyIdMap().get(competencyId)) - .filter(competency -> competency.getLectureUnits().contains(learningObject) || competency.getExercises().contains(learningObject)); + .filter(competency -> competency.getLectureUnitLinks().contains(learningObject) || competency.getExerciseLinks().contains(learningObject)); // There will always be at least one competency that contains the learning object, otherwise the learning object would not be in the learning path Comparator comparator = Comparator.comparingInt(competency -> recommendationState.recommendedOrderOfCompetencies().indexOf(competency.getId())); diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/service/learningpath/LearningPathNgxService.java b/src/main/java/de/tum/cit/aet/artemis/atlas/service/learningpath/LearningPathNgxService.java index 8a3b228a27e5..f0351804c341 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/service/learningpath/LearningPathNgxService.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/service/learningpath/LearningPathNgxService.java @@ -17,6 +17,8 @@ import org.springframework.stereotype.Service; import de.tum.cit.aet.artemis.atlas.domain.LearningObject; +import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyExerciseLink; +import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyLectureUnitLink; import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyRelation; import de.tum.cit.aet.artemis.atlas.domain.competency.CourseCompetency; import de.tum.cit.aet.artemis.atlas.domain.competency.LearningPath; @@ -86,7 +88,7 @@ private void generateNgxGraphRepresentationForCompetency(LearningPath learningPa currentCluster.add(NgxLearningPathDTO.Node.of(endNodeId, NgxLearningPathDTO.NodeType.COMPETENCY_END, competency.getId())); // generate nodes and edges for lecture units - competency.getLectureUnits().forEach(lectureUnit -> { + competency.getLectureUnitLinks().stream().map(CompetencyLectureUnitLink::getLectureUnit).forEach(lectureUnit -> { currentCluster.add(NgxLearningPathDTO.Node.of(getLectureUnitNodeId(competency.getId(), lectureUnit.getId()), NgxLearningPathDTO.NodeType.LECTURE_UNIT, lectureUnit.getId(), lectureUnit.getLecture().getId(), lectureUnit.isCompletedFor(learningPath.getUser()), lectureUnit.getName())); edges.add(new NgxLearningPathDTO.Edge(getLectureUnitInEdgeId(competency.getId(), lectureUnit.getId()), startNodeId, @@ -95,7 +97,7 @@ private void generateNgxGraphRepresentationForCompetency(LearningPath learningPa endNodeId)); }); // generate nodes and edges for exercises - competency.getExercises().forEach(exercise -> { + competency.getExerciseLinks().stream().map(CompetencyExerciseLink::getExercise).forEach(exercise -> { currentCluster.add(NgxLearningPathDTO.Node.of(getExerciseNodeId(competency.getId(), exercise.getId()), NgxLearningPathDTO.NodeType.EXERCISE, exercise.getId(), false, exercise.getTitle())); edges.add(new NgxLearningPathDTO.Edge(getExerciseInEdgeId(competency.getId(), exercise.getId()), startNodeId, getExerciseNodeId(competency.getId(), exercise.getId()))); diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/service/learningpath/LearningPathRecommendationService.java b/src/main/java/de/tum/cit/aet/artemis/atlas/service/learningpath/LearningPathRecommendationService.java index 5fe455ec8ce7..eede413d71ee 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/service/learningpath/LearningPathRecommendationService.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/service/learningpath/LearningPathRecommendationService.java @@ -24,6 +24,8 @@ import de.tum.cit.aet.artemis.assessment.service.ParticipantScoreService; import de.tum.cit.aet.artemis.atlas.domain.LearningObject; +import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyExerciseLink; +import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyLectureUnitLink; import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyProgress; import de.tum.cit.aet.artemis.atlas.domain.competency.CourseCompetency; import de.tum.cit.aet.artemis.atlas.domain.competency.LearningPath; @@ -34,9 +36,9 @@ import de.tum.cit.aet.artemis.atlas.repository.CourseCompetencyRepository; import de.tum.cit.aet.artemis.atlas.service.competency.CompetencyProgressService; import de.tum.cit.aet.artemis.core.domain.User; +import de.tum.cit.aet.artemis.exercise.domain.BaseExercise; import de.tum.cit.aet.artemis.exercise.domain.DifficultyLevel; import de.tum.cit.aet.artemis.exercise.domain.Exercise; -import de.tum.cit.aet.artemis.lecture.domain.Lecture; import de.tum.cit.aet.artemis.lecture.domain.LectureUnit; import de.tum.cit.aet.artemis.lecture.service.LearningObjectService; @@ -390,8 +392,8 @@ else if (timeDelta > 0) { * @return earliest due date of the competency */ private static Optional getEarliestDueDate(CourseCompetency competency) { - final var lectureDueDates = competency.getLectureUnits().stream().map(LectureUnit::getLecture).map(Lecture::getEndDate); - final var exerciseDueDates = competency.getExercises().stream().map(Exercise::getDueDate); + final var lectureDueDates = competency.getLectureUnitLinks().stream().map(lectureUnitLink -> lectureUnitLink.getLectureUnit().getLecture().getEndDate()); + final var exerciseDueDates = competency.getExerciseLinks().stream().map(exerciseLink -> exerciseLink.getExercise().getDueDate()); return Stream.concat(Stream.concat(Stream.of(competency.getSoftDueDate()), lectureDueDates), exerciseDueDates).filter(Objects::nonNull).min(Comparator.naturalOrder()); } @@ -473,7 +475,8 @@ public List getRecommendedOrderOfLearningObjects(User user, Cour * @return the recommended ordering of learning objects */ public List getRecommendedOrderOfLearningObjects(User user, CourseCompetency competency, double combinedPriorConfidence) { - var pendingLectureUnits = competency.getLectureUnits().stream().filter(lectureUnit -> !lectureUnit.isCompletedFor(user)).toList(); + var pendingLectureUnits = competency.getLectureUnitLinks().stream().map(CompetencyLectureUnitLink::getLectureUnit).filter(lectureUnit -> !lectureUnit.isCompletedFor(user)) + .toList(); List recommendedOrder = new ArrayList<>(pendingLectureUnits); // early return if competency can be trivially mastered @@ -486,8 +489,9 @@ public List getRecommendedOrderOfLearningObjects(User user, Cour final var numberOfRequiredExercisePointsToMaster = calculateNumberOfExercisePointsRequiredToMaster(user, competency, weightedConfidence); - final var pendingExercises = competency.getExercises().stream().filter(exercise -> !learningObjectService.isCompletedByUser(exercise, user)).collect(Collectors.toSet()); - final var pendingExercisePoints = pendingExercises.stream().mapToDouble(Exercise::getMaxPoints).sum(); + final var pendingExercises = competency.getExerciseLinks().stream().map(CompetencyExerciseLink::getExercise) + .filter(exercise -> !learningObjectService.isCompletedByUser(exercise, user)).collect(Collectors.toSet()); + final var pendingExercisePoints = pendingExercises.stream().mapToDouble(BaseExercise::getMaxPoints).sum(); Map> difficultyLevelMap = generateDifficultyLevelMap(pendingExercises); if (numberOfRequiredExercisePointsToMaster >= pendingExercisePoints) { @@ -605,16 +609,16 @@ private static double computeCombinedPriorConfidence(CourseCompetency competency private double calculateNumberOfExercisePointsRequiredToMaster(User user, CourseCompetency competency, double weightedConfidence) { // we assume that the student may perform slightly worse than previously and dampen the confidence for the prediction process weightedConfidence *= 0.9; - double currentPoints = participantScoreService.getStudentAndTeamParticipationPointsAsDoubleStream(user, competency.getExercises()).sum(); - double maxPoints = competency.getExercises().stream().mapToDouble(Exercise::getMaxPoints).sum(); - double lectureUnits = competency.getLectureUnits().size(); - double exercises = competency.getExercises().size(); - double learningObjects = lectureUnits + exercises; + Set exercises = competency.getExerciseLinks().stream().map(CompetencyExerciseLink::getExercise).collect(Collectors.toSet()); + double currentPoints = participantScoreService.getStudentAndTeamParticipationPointsAsDoubleStream(user, exercises).sum(); + double maxPoints = exercises.stream().mapToDouble(Exercise::getMaxPoints).sum(); + double lectureUnits = competency.getLectureUnitLinks().size(); + double learningObjects = lectureUnits + exercises.size(); double masteryThreshold = competency.getMasteryThreshold(); double neededProgress = masteryThreshold / weightedConfidence; double maxLectureUnitProgress = lectureUnits / learningObjects * 100; - double exerciseWeight = exercises / learningObjects; + double exerciseWeight = exercises.size() / learningObjects; double neededTotalExercisePoints = (neededProgress - maxLectureUnitProgress) / exerciseWeight * (maxPoints / 100); double neededExercisePoints = neededTotalExercisePoints - currentPoints; @@ -702,13 +706,15 @@ public List getOrderOfLearningObjectsForCompetency(long competen public List getOrderOfLearningObjectsForCompetency(CourseCompetency competency, User user) { Optional optionalCompetencyProgress = competencyProgressRepository.findByCompetencyIdAndUserId(competency.getId(), user.getId()); competency.setUserProgress(optionalCompetencyProgress.map(Set::of).orElse(Set.of())); - learningObjectService.setLectureUnitCompletions(competency.getLectureUnits(), user); + Set lectureUnits = competency.getLectureUnitLinks().stream().map(CompetencyLectureUnitLink::getLectureUnit).collect(Collectors.toSet()); + learningObjectService.setLectureUnitCompletions(lectureUnits, user); Set priorCompetencyProgresses = competencyProgressRepository.findAllPriorByCompetencyId(competency, user); double combinedPriorConfidence = priorCompetencyProgresses.stream().mapToDouble(CompetencyProgress::getConfidence).average().orElse(0); double weightedConfidence = computeWeightedConfidence(combinedPriorConfidence, optionalCompetencyProgress); - Stream completedLectureUnits = competency.getLectureUnits().stream().filter(lectureUnit -> lectureUnit.isCompletedFor(user)); - Stream completedExercises = competency.getExercises().stream().filter(exercise -> learningObjectService.isCompletedByUser(exercise, user)); + Stream completedLectureUnits = lectureUnits.stream().filter(lectureUnit -> lectureUnit.isCompletedFor(user)); + Stream completedExercises = competency.getExerciseLinks().stream().map(CompetencyExerciseLink::getExercise) + .filter(exercise -> learningObjectService.isCompletedByUser(exercise, user)); Stream pendingLearningObjects = getRecommendedOrderOfLearningObjects(user, competency, weightedConfidence).stream(); return Stream.concat(completedLectureUnits, Stream.concat(completedExercises, pendingLearningObjects)).toList(); diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/service/learningpath/LearningPathService.java b/src/main/java/de/tum/cit/aet/artemis/atlas/service/learningpath/LearningPathService.java index ea2a4bd9ec37..9a1c87429ecf 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/service/learningpath/LearningPathService.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/service/learningpath/LearningPathService.java @@ -18,6 +18,8 @@ import org.springframework.data.domain.Page; import org.springframework.stereotype.Service; +import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyExerciseLink; +import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyLectureUnitLink; import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyProgress; import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyRelation; import de.tum.cit.aet.artemis.atlas.domain.competency.CourseCompetency; @@ -45,7 +47,6 @@ import de.tum.cit.aet.artemis.core.repository.CourseRepository; import de.tum.cit.aet.artemis.core.repository.UserRepository; import de.tum.cit.aet.artemis.core.util.PageUtil; -import de.tum.cit.aet.artemis.exercise.domain.Exercise; import de.tum.cit.aet.artemis.exercise.domain.participation.StudentParticipation; import de.tum.cit.aet.artemis.exercise.repository.StudentParticipationRepository; import de.tum.cit.aet.artemis.lecture.domain.ExerciseUnit; @@ -430,17 +431,19 @@ public LearningPath findWithCompetenciesAndReleasedLearningObjectsAndCompletedUs LearningPath learningPath = learningPathRepository.findWithCompetenciesAndLectureUnitsAndExercisesByIdElseThrow(learningPathId); // Remove exercises that are not visible to students - learningPath.getCompetencies() - .forEach(competency -> competency.setExercises(competency.getExercises().stream().filter(Exercise::isVisibleToStudents).collect(Collectors.toSet()))); + learningPath.getCompetencies().forEach(competency -> competency + .setExerciseLinks(competency.getExerciseLinks().stream().filter(exerciseLink -> exerciseLink.getExercise().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()))); + learningPath.getCompetencies() + .forEach(competency -> competency.setLectureUnitLinks(competency.getLectureUnitLinks().stream() + .filter(lectureUnitLink -> !(lectureUnitLink.getLectureUnit() instanceof ExerciseUnit) && lectureUnitLink.getLectureUnit().isVisibleToStudents()) + .collect(Collectors.toSet()))); if (learningPath.getUser() == null) { learningPath.getCompetencies().forEach(competency -> { competency.setUserProgress(Collections.emptySet()); - competency.getLectureUnits().forEach(lectureUnit -> lectureUnit.setCompletedUsers(Collections.emptySet())); - competency.getExercises().forEach(exercise -> exercise.setStudentParticipations(Collections.emptySet())); + competency.getLectureUnitLinks().forEach(lectureUnitLink -> lectureUnitLink.getLectureUnit().setCompletedUsers(Collections.emptySet())); + competency.getExerciseLinks().forEach(exerciseLink -> exerciseLink.getExercise().setStudentParticipations(Collections.emptySet())); }); return learningPath; } @@ -448,10 +451,12 @@ public LearningPath findWithCompetenciesAndReleasedLearningObjectsAndCompletedUs Set competencyIds = learningPath.getCompetencies().stream().map(CourseCompetency::getId).collect(Collectors.toSet()); Map competencyProgresses = competencyProgressRepository.findAllByCompetencyIdsAndUserId(competencyIds, userId).stream() .collect(Collectors.toMap(progress -> progress.getCompetency().getId(), cp -> cp)); - Set lectureUnits = learningPath.getCompetencies().stream().flatMap(competency -> competency.getLectureUnits().stream()).collect(Collectors.toSet()); + Set lectureUnits = learningPath.getCompetencies().stream() + .flatMap(competency -> competency.getLectureUnitLinks().stream().map(CompetencyLectureUnitLink::getLectureUnit)).collect(Collectors.toSet()); Map completions = lectureUnitCompletionRepository.findByLectureUnitsAndUserId(lectureUnits, userId).stream() .collect(Collectors.toMap(completion -> completion.getLectureUnit().getId(), cp -> cp)); - Set exerciseIds = learningPath.getCompetencies().stream().flatMap(competency -> competency.getExercises().stream()).map(Exercise::getId).collect(Collectors.toSet()); + Set exerciseIds = learningPath.getCompetencies().stream().flatMap(competency -> competency.getExerciseLinks().stream()) + .map(exerciseLink -> exerciseLink.getExercise().getId()).collect(Collectors.toSet()); Map studentParticipations = studentParticipationRepository.findDistinctAllByExerciseIdInAndStudentId(exerciseIds, userId).stream() .collect(Collectors.toMap(participation -> participation.getExercise().getId(), sp -> sp)); learningPath.getCompetencies().forEach(competency -> { @@ -461,7 +466,7 @@ public LearningPath findWithCompetenciesAndReleasedLearningObjectsAndCompletedUs else { competency.setUserProgress(Collections.emptySet()); } - competency.getLectureUnits().forEach(lectureUnit -> { + competency.getLectureUnitLinks().stream().map(CompetencyLectureUnitLink::getLectureUnit).forEach(lectureUnit -> { if (completions.containsKey(lectureUnit.getId())) { lectureUnit.setCompletedUsers(Set.of(completions.get(lectureUnit.getId()))); } @@ -469,7 +474,7 @@ public LearningPath findWithCompetenciesAndReleasedLearningObjectsAndCompletedUs lectureUnit.setCompletedUsers(Collections.emptySet()); } }); - competency.getExercises().forEach(exercise -> { + competency.getExerciseLinks().stream().map(CompetencyExerciseLink::getExercise).forEach(exercise -> { if (studentParticipations.containsKey(exercise.getId())) { exercise.setStudentParticipations(Set.of(studentParticipations.get(exercise.getId()))); } diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/web/CompetencyResource.java b/src/main/java/de/tum/cit/aet/artemis/atlas/web/CompetencyResource.java index aa1a9f78dc0e..315a86d1fdbf 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/web/CompetencyResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/web/CompetencyResource.java @@ -301,7 +301,7 @@ public ResponseEntity updateCompetency(@PathVariable long courseId, checkCourseForCompetency(course, existingCompetency); var persistedCompetency = competencyService.updateCourseCompetency(existingCompetency, competency); - lectureUnitService.linkLectureUnitsToCompetency(persistedCompetency, competency.getLectureUnits(), existingCompetency.getLectureUnits()); + lectureUnitService.linkLectureUnitsToCompetency(persistedCompetency, competency.getLectureUnitLinks(), existingCompetency.getLectureUnitLinks()); return ResponseEntity.ok(persistedCompetency); } diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/web/PrerequisiteResource.java b/src/main/java/de/tum/cit/aet/artemis/atlas/web/PrerequisiteResource.java index a9be53c3c4a4..2723fdd7d6f5 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/web/PrerequisiteResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/web/PrerequisiteResource.java @@ -305,7 +305,7 @@ public ResponseEntity updatePrerequisite(@PathVariable long course checkCourseForPrerequisite(course, existingPrerequisite); var persistedPrerequisite = prerequisiteService.updateCourseCompetency(existingPrerequisite, prerequisite); - lectureUnitService.linkLectureUnitsToCompetency(persistedPrerequisite, prerequisite.getLectureUnits(), existingPrerequisite.getLectureUnits()); + lectureUnitService.linkLectureUnitsToCompetency(persistedPrerequisite, prerequisite.getLectureUnitLinks(), existingPrerequisite.getLectureUnitLinks()); return ResponseEntity.ok(persistedPrerequisite); } diff --git a/src/main/java/de/tum/cit/aet/artemis/exercise/domain/Exercise.java b/src/main/java/de/tum/cit/aet/artemis/exercise/domain/Exercise.java index b25eb7ab154d..9fa635e491eb 100644 --- a/src/main/java/de/tum/cit/aet/artemis/exercise/domain/Exercise.java +++ b/src/main/java/de/tum/cit/aet/artemis/exercise/domain/Exercise.java @@ -29,8 +29,6 @@ import jakarta.persistence.Inheritance; import jakarta.persistence.InheritanceType; import jakarta.persistence.JoinColumn; -import jakarta.persistence.JoinTable; -import jakarta.persistence.ManyToMany; import jakarta.persistence.ManyToOne; import jakarta.persistence.OneToMany; import jakarta.persistence.OneToOne; @@ -55,7 +53,7 @@ import de.tum.cit.aet.artemis.assessment.domain.Result; import de.tum.cit.aet.artemis.assessment.domain.TutorParticipation; import de.tum.cit.aet.artemis.atlas.domain.LearningObject; -import de.tum.cit.aet.artemis.atlas.domain.competency.CourseCompetency; +import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyExerciseLink; import de.tum.cit.aet.artemis.communication.domain.Post; import de.tum.cit.aet.artemis.core.domain.Course; import de.tum.cit.aet.artemis.core.domain.User; @@ -116,11 +114,10 @@ public abstract class Exercise extends BaseExercise implements LearningObject { @Column(name = "grading_instructions") private String gradingInstructions; - @ManyToMany - @JoinTable(name = "competency_exercise", joinColumns = @JoinColumn(name = "exercise_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "competency_id", referencedColumnName = "id")) - @JsonIgnoreProperties({ "exercises", "course" }) + @OneToMany(mappedBy = "exercise", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true) + @JsonIgnoreProperties("exercise") @JsonView(QuizView.Before.class) - private Set competencies = new HashSet<>(); + private Set competencyLinks = new HashSet<>(); @ElementCollection(fetch = FetchType.LAZY) @CollectionTable(name = "exercise_categories", joinColumns = @JoinColumn(name = "exercise_id")) @@ -461,12 +458,12 @@ public void setPlagiarismDetectionConfig(PlagiarismDetectionConfig plagiarismDet // jhipster-needle-entity-add-getters-setters - JHipster will add getters and setters here, do not remove @Override - public Set getCompetencies() { - return competencies; + public Set getCompetencyLinks() { + return competencyLinks; } - public void setCompetencies(Set competencies) { - this.competencies = competencies; + public void setCompetencyLinks(Set competencyLinks) { + this.competencyLinks = competencyLinks; } public Long getNumberOfParticipations() { diff --git a/src/main/java/de/tum/cit/aet/artemis/exercise/repository/ExerciseRepository.java b/src/main/java/de/tum/cit/aet/artemis/exercise/repository/ExerciseRepository.java index ff509559e132..bec0747e9b08 100644 --- a/src/main/java/de/tum/cit/aet/artemis/exercise/repository/ExerciseRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/exercise/repository/ExerciseRepository.java @@ -76,7 +76,8 @@ public interface ExerciseRepository extends ArtemisJpaRepository @Query(""" SELECT e FROM Exercise e - LEFT JOIN FETCH e.competencies + LEFT JOIN FETCH e.competencyLinks cl + LEFT JOIN FETCH cl.competency c WHERE e.id = :exerciseId """) Optional findWithCompetenciesById(@Param("exerciseId") long exerciseId); diff --git a/src/main/java/de/tum/cit/aet/artemis/exercise/service/ExerciseDeletionService.java b/src/main/java/de/tum/cit/aet/artemis/exercise/service/ExerciseDeletionService.java index 73f31c2b73a5..70ab783682fc 100644 --- a/src/main/java/de/tum/cit/aet/artemis/exercise/service/ExerciseDeletionService.java +++ b/src/main/java/de/tum/cit/aet/artemis/exercise/service/ExerciseDeletionService.java @@ -15,7 +15,7 @@ import de.tum.cit.aet.artemis.assessment.repository.TutorParticipationRepository; import de.tum.cit.aet.artemis.assessment.service.ExampleSubmissionService; -import de.tum.cit.aet.artemis.atlas.domain.competency.CourseCompetency; +import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyExerciseLink; import de.tum.cit.aet.artemis.atlas.service.competency.CompetencyProgressService; import de.tum.cit.aet.artemis.communication.domain.conversation.Channel; import de.tum.cit.aet.artemis.communication.repository.conversation.ChannelRepository; @@ -143,7 +143,7 @@ public void cleanup(Long exerciseId, boolean deleteRepositories) { */ public void delete(long exerciseId, boolean deleteStudentReposBuildPlans, boolean deleteBaseReposBuildPlans) { var exercise = exerciseRepository.findWithCompetenciesByIdElseThrow(exerciseId); - Set competencies = exercise.getCompetencies(); + Set competenciyLinks = exercise.getCompetencyLinks(); log.info("Request to delete {} with id {}", exercise.getClass().getSimpleName(), exerciseId); long start = System.nanoTime(); @@ -204,7 +204,7 @@ public void delete(long exerciseId, boolean deleteStudentReposBuildPlans, boolea exerciseRepository.delete(exercise); } - competencies.forEach(competencyProgressService::updateProgressByCompetencyAsync); + competenciyLinks.stream().map(CompetencyExerciseLink::getCompetency).forEach(competencyProgressService::updateProgressByCompetencyAsync); } /** diff --git a/src/main/java/de/tum/cit/aet/artemis/exercise/service/ExerciseImportService.java b/src/main/java/de/tum/cit/aet/artemis/exercise/service/ExerciseImportService.java index 21d0d05a9a8b..5ad1f4b87268 100644 --- a/src/main/java/de/tum/cit/aet/artemis/exercise/service/ExerciseImportService.java +++ b/src/main/java/de/tum/cit/aet/artemis/exercise/service/ExerciseImportService.java @@ -63,7 +63,7 @@ protected void copyExerciseBasis(final Exercise newExercise, final Exercise impo newExercise.setDifficulty(importedExercise.getDifficulty()); newExercise.setGradingInstructions(importedExercise.getGradingInstructions()); newExercise.setGradingCriteria(importedExercise.copyGradingCriteria(gradingInstructionCopyTracker)); - newExercise.setCompetencies(importedExercise.getCompetencies()); + newExercise.setCompetencyLinks(importedExercise.getCompetencyLinks()); if (importedExercise.getPlagiarismDetectionConfig() != null) { newExercise.setPlagiarismDetectionConfig(new PlagiarismDetectionConfig(importedExercise.getPlagiarismDetectionConfig())); diff --git a/src/main/java/de/tum/cit/aet/artemis/exercise/service/ExerciseService.java b/src/main/java/de/tum/cit/aet/artemis/exercise/service/ExerciseService.java index 816d2bfdb558..164fbd86a507 100644 --- a/src/main/java/de/tum/cit/aet/artemis/exercise/service/ExerciseService.java +++ b/src/main/java/de/tum/cit/aet/artemis/exercise/service/ExerciseService.java @@ -43,7 +43,9 @@ import de.tum.cit.aet.artemis.assessment.repository.ResultRepository; import de.tum.cit.aet.artemis.assessment.service.RatingService; import de.tum.cit.aet.artemis.assessment.service.TutorLeaderboardService; +import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyExerciseLink; import de.tum.cit.aet.artemis.atlas.domain.competency.CourseCompetency; +import de.tum.cit.aet.artemis.atlas.repository.CompetencyExerciseLinkRepository; import de.tum.cit.aet.artemis.communication.service.notifications.GroupNotificationScheduleService; import de.tum.cit.aet.artemis.core.config.Constants; import de.tum.cit.aet.artemis.core.domain.Course; @@ -126,13 +128,16 @@ public class ExerciseService { private final GroupNotificationScheduleService groupNotificationScheduleService; + private final CompetencyExerciseLinkRepository competencyExerciseLinkRepository; + public ExerciseService(ExerciseRepository exerciseRepository, AuthorizationCheckService authCheckService, AuditEventRepository auditEventRepository, TeamRepository teamRepository, ProgrammingExerciseRepository programmingExerciseRepository, Optional lti13ResourceLaunchRepository, StudentParticipationRepository studentParticipationRepository, ResultRepository resultRepository, SubmissionRepository submissionRepository, ParticipantScoreRepository participantScoreRepository, UserRepository userRepository, ComplaintRepository complaintRepository, TutorLeaderboardService tutorLeaderboardService, ComplaintResponseRepository complaintResponseRepository, GradingCriterionRepository gradingCriterionRepository, FeedbackRepository feedbackRepository, RatingService ratingService, ExerciseDateService exerciseDateService, ExampleSubmissionRepository exampleSubmissionRepository, - QuizBatchService quizBatchService, ExamLiveEventsService examLiveEventsService, GroupNotificationScheduleService groupNotificationScheduleService) { + QuizBatchService quizBatchService, ExamLiveEventsService examLiveEventsService, GroupNotificationScheduleService groupNotificationScheduleService, + CompetencyExerciseLinkRepository competencyExerciseLinkRepository) { this.exerciseRepository = exerciseRepository; this.resultRepository = resultRepository; this.authCheckService = authCheckService; @@ -155,6 +160,7 @@ public ExerciseService(ExerciseRepository exerciseRepository, AuthorizationCheck this.quizBatchService = quizBatchService; this.examLiveEventsService = examLiveEventsService; this.groupNotificationScheduleService = groupNotificationScheduleService; + this.competencyExerciseLinkRepository = competencyExerciseLinkRepository; } /** @@ -761,12 +767,12 @@ public List getFeedbackToBeDeletedAfterGradingInstructionUpdate(boolea /** * Removes competency from all exercises. * - * @param exercises set of exercises - * @param competency competency to remove + * @param exerciseLinks set of exercise links + * @param competency competency to remove */ - public void removeCompetency(@NotNull Set exercises, @NotNull CourseCompetency competency) { - exercises.forEach(exercise -> exercise.getCompetencies().remove(competency)); - exerciseRepository.saveAll(exercises); + public void removeCompetency(@NotNull Set exerciseLinks, @NotNull CourseCompetency competency) { + competencyExerciseLinkRepository.deleteAll(exerciseLinks); + competency.getExerciseLinks().removeAll(exerciseLinks); } /** diff --git a/src/main/java/de/tum/cit/aet/artemis/fileupload/repository/FileUploadExerciseRepository.java b/src/main/java/de/tum/cit/aet/artemis/fileupload/repository/FileUploadExerciseRepository.java index 376a70db4c7e..ae6c5ac3f436 100644 --- a/src/main/java/de/tum/cit/aet/artemis/fileupload/repository/FileUploadExerciseRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/fileupload/repository/FileUploadExerciseRepository.java @@ -35,16 +35,16 @@ public interface FileUploadExerciseRepository extends ArtemisJpaRepository findByCourseIdWithCategories(@Param("courseId") Long courseId); - @EntityGraph(type = LOAD, attributePaths = { "competencies" }) + @EntityGraph(type = LOAD, attributePaths = { "competencyLinks.competency" }) Optional findWithEagerCompetenciesById(Long exerciseId); - @EntityGraph(type = LOAD, attributePaths = { "teamAssignmentConfig", "categories", "competencies" }) + @EntityGraph(type = LOAD, attributePaths = { "teamAssignmentConfig", "categories", "competencyLinks.competency" }) Optional findWithEagerTeamAssignmentConfigAndCategoriesAndCompetenciesById(Long exerciseId); @Query(""" SELECT f FROM FileUploadExercise f - LEFT JOIN FETCH f.competencies + LEFT JOIN FETCH f.competencyLinks WHERE f.title = :title AND f.course.id = :courseId """) diff --git a/src/main/java/de/tum/cit/aet/artemis/lecture/domain/ExerciseUnit.java b/src/main/java/de/tum/cit/aet/artemis/lecture/domain/ExerciseUnit.java index cfed1218740b..0883baf19e1b 100644 --- a/src/main/java/de/tum/cit/aet/artemis/lecture/domain/ExerciseUnit.java +++ b/src/main/java/de/tum/cit/aet/artemis/lecture/domain/ExerciseUnit.java @@ -12,13 +12,12 @@ import jakarta.persistence.PrePersist; import jakarta.persistence.PreUpdate; -import org.hibernate.Hibernate; import org.hibernate.annotations.Cache; import org.hibernate.annotations.CacheConcurrencyStrategy; import com.fasterxml.jackson.annotation.JsonInclude; -import de.tum.cit.aet.artemis.atlas.domain.competency.CourseCompetency; +import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyLectureUnitLink; import de.tum.cit.aet.artemis.exercise.domain.Exercise; @Entity @@ -66,12 +65,13 @@ public void setReleaseDate(ZonedDateTime releaseDate) { } @Override - public Set getCompetencies() { - return exercise == null || !Hibernate.isPropertyInitialized(exercise, "competencies") ? new HashSet<>() : exercise.getCompetencies(); + public Set getCompetencyLinks() { + // Should be accessed via associated exercise + return null; } @Override - public void setCompetencies(Set competencies) { + public void setCompetencyLinks(Set competencies) { // Should be set in associated exercise } @@ -83,7 +83,7 @@ public void setCompetencies(Set competencies) { public void prePersistOrUpdate() { this.name = null; this.releaseDate = null; - this.competencies = new HashSet<>(); + this.competencyLinks = new HashSet<>(); } // IMPORTANT NOTICE: The following string has to be consistent with the one defined in LectureUnit.java diff --git a/src/main/java/de/tum/cit/aet/artemis/lecture/domain/LectureUnit.java b/src/main/java/de/tum/cit/aet/artemis/lecture/domain/LectureUnit.java index 5a46db2a15a5..3d05dabb1b72 100644 --- a/src/main/java/de/tum/cit/aet/artemis/lecture/domain/LectureUnit.java +++ b/src/main/java/de/tum/cit/aet/artemis/lecture/domain/LectureUnit.java @@ -15,11 +15,8 @@ import jakarta.persistence.Inheritance; import jakarta.persistence.InheritanceType; import jakarta.persistence.JoinColumn; -import jakarta.persistence.JoinTable; -import jakarta.persistence.ManyToMany; import jakarta.persistence.ManyToOne; import jakarta.persistence.OneToMany; -import jakarta.persistence.OrderBy; import jakarta.persistence.Table; import jakarta.persistence.Transient; @@ -34,7 +31,7 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; import de.tum.cit.aet.artemis.atlas.domain.LearningObject; -import de.tum.cit.aet.artemis.atlas.domain.competency.CourseCompetency; +import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyLectureUnitLink; import de.tum.cit.aet.artemis.core.domain.DomainObject; import de.tum.cit.aet.artemis.core.domain.User; @@ -72,12 +69,10 @@ public abstract class LectureUnit extends DomainObject implements LearningObject @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) private Lecture lecture; - @ManyToMany - @JoinTable(name = "competency_lecture_unit", joinColumns = @JoinColumn(name = "lecture_unit_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "competency_id", referencedColumnName = "id")) - @OrderBy("title") - @JsonIgnoreProperties({ "lectureUnits", "course" }) + @OneToMany(mappedBy = "lectureUnit", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true) + @JsonIgnoreProperties("lectureUnit") @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) - protected Set competencies = new HashSet<>(); + protected Set competencyLinks = new HashSet<>(); @OneToMany(mappedBy = "lectureUnit", fetch = FetchType.LAZY, cascade = CascadeType.REMOVE, orphanRemoval = true) @JsonIgnore // important, so that the completion status of other users do not leak to anyone @@ -108,12 +103,12 @@ public void setReleaseDate(ZonedDateTime releaseDate) { } @Override - public Set getCompetencies() { - return competencies; + public Set getCompetencyLinks() { + return competencyLinks; } - public void setCompetencies(Set competencies) { - this.competencies = competencies; + public void setCompetencyLinks(Set competencyLinks) { + this.competencyLinks = competencyLinks; } @JsonIgnore(false) diff --git a/src/main/java/de/tum/cit/aet/artemis/lecture/repository/AttachmentUnitRepository.java b/src/main/java/de/tum/cit/aet/artemis/lecture/repository/AttachmentUnitRepository.java index 0fef3fef3512..61f268d75b95 100644 --- a/src/main/java/de/tum/cit/aet/artemis/lecture/repository/AttachmentUnitRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/lecture/repository/AttachmentUnitRepository.java @@ -58,7 +58,8 @@ default List findAllByLectureIdAndAttachmentTypeElseThrow(Long l SELECT attachmentUnit FROM AttachmentUnit attachmentUnit LEFT JOIN FETCH attachmentUnit.slides slides - LEFT JOIN FETCH attachmentUnit.competencies + LEFT JOIN FETCH attachmentUnit.competencyLinks cl + LEFT JOIN FETCH cl.competency WHERE attachmentUnit.id = :attachmentUnitId """) AttachmentUnit findOneWithSlidesAndCompetencies(@Param("attachmentUnitId") long attachmentUnitId); diff --git a/src/main/java/de/tum/cit/aet/artemis/lecture/repository/ExerciseUnitRepository.java b/src/main/java/de/tum/cit/aet/artemis/lecture/repository/ExerciseUnitRepository.java index 7fed45abdec0..b4858f3dd095 100644 --- a/src/main/java/de/tum/cit/aet/artemis/lecture/repository/ExerciseUnitRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/lecture/repository/ExerciseUnitRepository.java @@ -29,8 +29,10 @@ public interface ExerciseUnitRepository extends ArtemisJpaRepository findByIdWithCompetenciesBidirectional(@Param("exerciseId") Long exerciseId); diff --git a/src/main/java/de/tum/cit/aet/artemis/lecture/repository/LectureRepository.java b/src/main/java/de/tum/cit/aet/artemis/lecture/repository/LectureRepository.java index 621216563727..d0fd1afbf8a6 100644 --- a/src/main/java/de/tum/cit/aet/artemis/lecture/repository/LectureRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/lecture/repository/LectureRepository.java @@ -63,9 +63,11 @@ public interface LectureRepository extends ArtemisJpaRepository { LEFT JOIN FETCH lecture.posts LEFT JOIN FETCH lecture.lectureUnits lu LEFT JOIN FETCH lu.completedUsers cu - LEFT JOIN FETCH lu.competencies - LEFT JOIN FETCH lu.exercise exercise - LEFT JOIN FETCH exercise.competencies + LEFT JOIN FETCH lu.competencyLinks cl + LEFT JOIN FETCH cl.competency + LEFT JOIN FETCH lu.exercise e + LEFT JOIN FETCH e.competencyLinks ecl + LEFT JOIN FETCH ecl.competency WHERE lecture.id = :lectureId """) Optional findByIdWithAttachmentsAndPostsAndLectureUnitsAndCompetenciesAndCompletions(@Param("lectureId") Long lectureId); @@ -74,9 +76,11 @@ public interface LectureRepository extends ArtemisJpaRepository { SELECT lecture FROM Lecture lecture LEFT JOIN FETCH lecture.lectureUnits lu - LEFT JOIN FETCH lu.competencies - LEFT JOIN FETCH lu.exercise exercise - LEFT JOIN FETCH exercise.competencies + LEFT JOIN FETCH lu.competencyLinks cl + LEFT JOIN FETCH cl.competency + LEFT JOIN FETCH lu.exercise e + LEFT JOIN FETCH e.competencyLinks ecl + LEFT JOIN FETCH ecl.competency WHERE lecture.id = :lectureId """) Optional findByIdWithLectureUnitsAndCompetencies(@Param("lectureId") Long lectureId); diff --git a/src/main/java/de/tum/cit/aet/artemis/lecture/repository/LectureUnitRepository.java b/src/main/java/de/tum/cit/aet/artemis/lecture/repository/LectureUnitRepository.java index ee29df83380a..031fdc300e9d 100644 --- a/src/main/java/de/tum/cit/aet/artemis/lecture/repository/LectureUnitRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/lecture/repository/LectureUnitRepository.java @@ -24,9 +24,11 @@ public interface LectureUnitRepository extends ArtemisJpaRepository findByIdWithCompetenciesBidirectional(@Param("lectureUnitId") long lectureUnitId); @@ -46,10 +51,13 @@ public interface LectureUnitRepository extends ArtemisJpaRepository findAllByIdWithCompetenciesBidirectional(@Param("lectureUnitIds") Iterable longs); @@ -75,7 +83,8 @@ public interface LectureUnitRepository extends ArtemisJpaRepository findByIdWithCompetencies(@Param("onlineUnitId") long onlineUnitId); diff --git a/src/main/java/de/tum/cit/aet/artemis/lecture/repository/TextUnitRepository.java b/src/main/java/de/tum/cit/aet/artemis/lecture/repository/TextUnitRepository.java index 38fc336c4642..40ebe9f68523 100644 --- a/src/main/java/de/tum/cit/aet/artemis/lecture/repository/TextUnitRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/lecture/repository/TextUnitRepository.java @@ -22,7 +22,8 @@ public interface TextUnitRepository extends ArtemisJpaRepository @Query(""" SELECT tu FROM TextUnit tu - LEFT JOIN FETCH tu.competencies + LEFT JOIN FETCH tu.competencyLinks cl + LEFT JOIN FETCH cl.competency c WHERE tu.id = :textUnitId """) Optional findByIdWithCompetencies(@Param("textUnitId") Long textUnitId); diff --git a/src/main/java/de/tum/cit/aet/artemis/lecture/repository/VideoUnitRepository.java b/src/main/java/de/tum/cit/aet/artemis/lecture/repository/VideoUnitRepository.java index 419e607a1ecc..ad8b74cc2933 100644 --- a/src/main/java/de/tum/cit/aet/artemis/lecture/repository/VideoUnitRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/lecture/repository/VideoUnitRepository.java @@ -24,7 +24,8 @@ public interface VideoUnitRepository extends ArtemisJpaRepository findByIdWithCompetencies(@Param("videoUnitId") long videoUnitId); diff --git a/src/main/java/de/tum/cit/aet/artemis/lecture/service/AttachmentUnitService.java b/src/main/java/de/tum/cit/aet/artemis/lecture/service/AttachmentUnitService.java index 44a5811c6020..01ccdcd7616d 100644 --- a/src/main/java/de/tum/cit/aet/artemis/lecture/service/AttachmentUnitService.java +++ b/src/main/java/de/tum/cit/aet/artemis/lecture/service/AttachmentUnitService.java @@ -15,7 +15,7 @@ import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; -import de.tum.cit.aet.artemis.atlas.domain.competency.CourseCompetency; +import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyLectureUnitLink; import de.tum.cit.aet.artemis.atlas.service.competency.CompetencyProgressService; import de.tum.cit.aet.artemis.core.exception.BadRequestAlertException; import de.tum.cit.aet.artemis.core.service.FilePathService; @@ -106,12 +106,12 @@ public AttachmentUnit createAttachmentUnit(AttachmentUnit attachmentUnit, Attach */ public AttachmentUnit updateAttachmentUnit(AttachmentUnit existingAttachmentUnit, AttachmentUnit updateUnit, Attachment updateAttachment, MultipartFile updateFile, boolean keepFilename) { - Set existingCompetencies = existingAttachmentUnit.getCompetencies(); + Set existingCompetencyLinks = existingAttachmentUnit.getCompetencyLinks(); existingAttachmentUnit.setDescription(updateUnit.getDescription()); existingAttachmentUnit.setName(updateUnit.getName()); existingAttachmentUnit.setReleaseDate(updateUnit.getReleaseDate()); - existingAttachmentUnit.setCompetencies(updateUnit.getCompetencies()); + existingAttachmentUnit.setCompetencyLinks(updateUnit.getCompetencyLinks()); AttachmentUnit savedAttachmentUnit = attachmentUnitRepository.saveAndFlush(existingAttachmentUnit); @@ -147,7 +147,7 @@ public AttachmentUnit updateAttachmentUnit(AttachmentUnit existingAttachmentUnit } // Set the original competencies back to the attachment unit so that the competencyProgressService can determine which competencies changed - existingAttachmentUnit.setCompetencies(existingCompetencies); + existingAttachmentUnit.setCompetencyLinks(existingCompetencyLinks); competencyProgressService.updateProgressForUpdatedLearningObjectAsync(existingAttachmentUnit, Optional.of(updateUnit)); return savedAttachmentUnit; diff --git a/src/main/java/de/tum/cit/aet/artemis/lecture/service/LectureUnitService.java b/src/main/java/de/tum/cit/aet/artemis/lecture/service/LectureUnitService.java index fd371f07c9e4..ac06d5e14cb7 100644 --- a/src/main/java/de/tum/cit/aet/artemis/lecture/service/LectureUnitService.java +++ b/src/main/java/de/tum/cit/aet/artemis/lecture/service/LectureUnitService.java @@ -11,7 +11,6 @@ import java.util.List; import java.util.Optional; import java.util.Set; -import java.util.function.Predicate; import java.util.stream.Collectors; import jakarta.validation.constraints.NotNull; @@ -21,14 +20,15 @@ import org.springframework.dao.DataIntegrityViolationException; import org.springframework.stereotype.Service; +import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyLearningObjectLink; +import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyLectureUnitLink; import de.tum.cit.aet.artemis.atlas.domain.competency.CourseCompetency; +import de.tum.cit.aet.artemis.atlas.repository.CompetencyLectureUnitLinkRepository; import de.tum.cit.aet.artemis.atlas.repository.CourseCompetencyRepository; import de.tum.cit.aet.artemis.atlas.service.competency.CompetencyProgressService; import de.tum.cit.aet.artemis.core.domain.User; import de.tum.cit.aet.artemis.core.service.FilePathService; import de.tum.cit.aet.artemis.core.service.FileService; -import de.tum.cit.aet.artemis.exercise.domain.Exercise; -import de.tum.cit.aet.artemis.exercise.repository.ExerciseRepository; import de.tum.cit.aet.artemis.iris.service.pyris.PyrisWebhookService; import de.tum.cit.aet.artemis.lecture.domain.AttachmentUnit; import de.tum.cit.aet.artemis.lecture.domain.ExerciseUnit; @@ -55,26 +55,26 @@ public class LectureUnitService { private final SlideRepository slideRepository; - private final ExerciseRepository exerciseRepository; - private final Optional pyrisWebhookService; private final CompetencyProgressService competencyProgressService; private final CourseCompetencyRepository courseCompetencyRepository; + private final CompetencyLectureUnitLinkRepository competencyLectureUnitLinkRepository; + public LectureUnitService(LectureUnitRepository lectureUnitRepository, LectureRepository lectureRepository, LectureUnitCompletionRepository lectureUnitCompletionRepository, - FileService fileService, SlideRepository slideRepository, ExerciseRepository exerciseRepository, Optional pyrisWebhookService, - CompetencyProgressService competencyProgressService, CourseCompetencyRepository courseCompetencyRepository) { + FileService fileService, SlideRepository slideRepository, Optional pyrisWebhookService, CompetencyProgressService competencyProgressService, + CourseCompetencyRepository courseCompetencyRepository, CompetencyLectureUnitLinkRepository competencyLectureUnitLinkRepository) { this.lectureUnitRepository = lectureUnitRepository; this.lectureRepository = lectureRepository; this.lectureUnitCompletionRepository = lectureUnitCompletionRepository; this.fileService = fileService; this.slideRepository = slideRepository; - this.exerciseRepository = exerciseRepository; this.pyrisWebhookService = pyrisWebhookService; this.courseCompetencyRepository = courseCompetencyRepository; this.competencyProgressService = competencyProgressService; + this.competencyLectureUnitLinkRepository = competencyLectureUnitLinkRepository; } /** @@ -158,10 +158,10 @@ public void removeLectureUnit(@NotNull LectureUnit lectureUnit) { if (!(lectureUnitToDelete instanceof ExerciseUnit)) { // update associated competencies - Set competencies = lectureUnitToDelete.getCompetencies(); + Set competencies = lectureUnitToDelete.getCompetencyLinks().stream().map(CompetencyLearningObjectLink::getCompetency).collect(Collectors.toSet()); courseCompetencyRepository.saveAll(competencies.stream().map(competency -> { competency = courseCompetencyRepository.findByIdWithLectureUnitsElseThrow(competency.getId()); - competency.getLectureUnits().remove(lectureUnitToDelete); + competency.getLectureUnitLinks().remove(lectureUnitToDelete); return competency; }).toList()); } @@ -192,40 +192,28 @@ public void removeLectureUnit(@NotNull LectureUnit lectureUnit) { /** * Link the competency to a set of lecture units (and exercises if it includes exercise units) * - * @param competency The competency to be linked - * @param lectureUnitsToAdd A set of lecture units to link to the specified competency - * @param lectureUnitsToRemove A set of lecture units to unlink from the specified competency + * @param competency The competency to be linked + * @param lectureUnitLinksToAdd A set of lecture unit links to add to the specified competency + * @param lectureUnitLinksToRemove A set of lecture unit link to remove from the specified competency */ - public void linkLectureUnitsToCompetency(CourseCompetency competency, Set lectureUnitsToAdd, Set lectureUnitsToRemove) { - final Predicate isExerciseUnit = lectureUnit -> lectureUnit instanceof ExerciseUnit; - - // Remove the competency from the old lecture units - var lectureUnitsToRemoveFromDb = lectureUnitRepository.findAllByIdWithCompetenciesBidirectional(lectureUnitsToRemove.stream().map(LectureUnit::getId).toList()); - lectureUnitRepository.saveAll(lectureUnitsToRemoveFromDb.stream().filter(isExerciseUnit.negate()).peek(lectureUnit -> lectureUnit.getCompetencies().remove(competency)) - .collect(Collectors.toSet())); - exerciseRepository.saveAll(lectureUnitsToRemoveFromDb.stream().filter(isExerciseUnit).map(lectureUnit -> ((ExerciseUnit) lectureUnit).getExercise()) - .peek(exercise -> exercise.getCompetencies().remove(competency)).collect(Collectors.toSet())); - - // Add the competency to the new lecture units - var lectureUnitsFromDb = lectureUnitRepository.findAllByIdWithCompetenciesBidirectional(lectureUnitsToAdd.stream().map(LectureUnit::getId).toList()); - var lectureUnitsWithoutExercises = lectureUnitsFromDb.stream().filter(isExerciseUnit.negate()).collect(Collectors.toSet()); - var exercises = lectureUnitsFromDb.stream().filter(isExerciseUnit).map(lectureUnit -> ((ExerciseUnit) lectureUnit).getExercise()).collect(Collectors.toSet()); - lectureUnitsWithoutExercises.stream().map(LectureUnit::getCompetencies).forEach(competencies -> competencies.add(competency)); - exercises.stream().map(Exercise::getCompetencies).forEach(competencies -> competencies.add(competency)); - lectureUnitRepository.saveAll(lectureUnitsWithoutExercises); - exerciseRepository.saveAll(exercises); - competency.setLectureUnits(lectureUnitsToAdd); + public void linkLectureUnitsToCompetency(CourseCompetency competency, Set lectureUnitLinksToAdd, + Set lectureUnitLinksToRemove) { + lectureUnitLinksToAdd.forEach(link -> link.setCompetency(competency)); + List persistedLinks = competencyLectureUnitLinkRepository.saveAll(lectureUnitLinksToAdd); + competencyLectureUnitLinkRepository.deleteAll(lectureUnitLinksToRemove); + + competency.getLectureUnitLinks().addAll(persistedLinks); } /** * Removes competency from all lecture units. * - * @param lectureUnits set of lecture units - * @param competency competency to remove + * @param lectureUnitLinks set of lecture unit links + * @param competency competency to remove */ - public void removeCompetency(Set lectureUnits, CourseCompetency competency) { - lectureUnits.forEach(lectureUnit -> lectureUnit.getCompetencies().remove(competency)); - lectureUnitRepository.saveAll(lectureUnits); + public void removeCompetency(Set lectureUnitLinks, CourseCompetency competency) { + competencyLectureUnitLinkRepository.deleteAll(lectureUnitLinks); + competency.getLectureUnitLinks().removeAll(lectureUnitLinks); } /** diff --git a/src/main/java/de/tum/cit/aet/artemis/lecture/web/LectureResource.java b/src/main/java/de/tum/cit/aet/artemis/lecture/web/LectureResource.java index 54ae76bf3bad..fd31ef09759d 100644 --- a/src/main/java/de/tum/cit/aet/artemis/lecture/web/LectureResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/lecture/web/LectureResource.java @@ -377,7 +377,7 @@ else if (lectureUnit instanceof AttachmentUnit) { // we replace the exercise with one that contains all the information needed for correct display exercisesWithAllInformationNeeded.stream().filter(exercise::equals).findAny().ifPresent(((ExerciseUnit) lectureUnit)::setExercise); // re-add the competencies already loaded with the exercise unit - ((ExerciseUnit) lectureUnit).getExercise().setCompetencies(exercise.getCompetencies()); + ((ExerciseUnit) lectureUnit).getExercise().setCompetencyLinks(exercise.getCompetencyLinks()); } }).toList(); diff --git a/src/main/java/de/tum/cit/aet/artemis/modeling/repository/ModelingExerciseRepository.java b/src/main/java/de/tum/cit/aet/artemis/modeling/repository/ModelingExerciseRepository.java index 626bb3c86694..e2861d984506 100644 --- a/src/main/java/de/tum/cit/aet/artemis/modeling/repository/ModelingExerciseRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/modeling/repository/ModelingExerciseRepository.java @@ -36,14 +36,15 @@ public interface ModelingExerciseRepository extends ArtemisJpaRepository findByCourseIdWithCategories(@Param("courseId") Long courseId); - @EntityGraph(type = LOAD, attributePaths = { "exampleSubmissions", "teamAssignmentConfig", "categories", "competencies", "exampleSubmissions.submission.results" }) + @EntityGraph(type = LOAD, attributePaths = { "exampleSubmissions", "teamAssignmentConfig", "categories", "competencyLinks.competency", + "exampleSubmissions.submission.results" }) Optional findWithEagerExampleSubmissionsAndCompetenciesById(Long exerciseId); - @EntityGraph(type = LOAD, attributePaths = { "exampleSubmissions", "teamAssignmentConfig", "categories", "competencies", "exampleSubmissions.submission.results", + @EntityGraph(type = LOAD, attributePaths = { "exampleSubmissions", "teamAssignmentConfig", "categories", "competencyLinks.competency", "exampleSubmissions.submission.results", "plagiarismDetectionConfig" }) Optional findWithEagerExampleSubmissionsAndCompetenciesAndPlagiarismDetectionConfigById(Long exerciseId); - @EntityGraph(type = LOAD, attributePaths = { "competencies" }) + @EntityGraph(type = LOAD, attributePaths = { "competencyLinks.competency" }) Optional findWithEagerCompetenciesById(Long exerciseId); @Query(""" @@ -100,7 +101,7 @@ public interface ModelingExerciseRepository extends ArtemisJpaRepository findWithTemplateAndSolutionParticipationTeamAssignmentConfigCategoriesAndBuildConfigById(long exerciseId); - @EntityGraph(type = LOAD, attributePaths = { "templateParticipation", "solutionParticipation", "teamAssignmentConfig", "categories", "competencies", "auxiliaryRepositories", - "submissionPolicy" }) + @EntityGraph(type = LOAD, attributePaths = { "templateParticipation", "solutionParticipation", "teamAssignmentConfig", "categories", "competencyLinks.competency", + "auxiliaryRepositories", "submissionPolicy" }) Optional findWithTemplateAndSolutionParticipationTeamAssignmentConfigCategoriesAndCompetenciesById(long exerciseId); - @EntityGraph(type = LOAD, attributePaths = { "templateParticipation", "solutionParticipation", "teamAssignmentConfig", "categories", "competencies", "auxiliaryRepositories", - "submissionPolicy", "buildConfig" }) + @EntityGraph(type = LOAD, attributePaths = { "templateParticipation", "solutionParticipation", "teamAssignmentConfig", "categories", "competencyLinks.competency", + "auxiliaryRepositories", "submissionPolicy", "buildConfig" }) Optional findWithTemplateAndSolutionParticipationTeamAssignmentConfigCategoriesCompetenciesAndBuildConfigById(long exerciseId); - @EntityGraph(type = LOAD, attributePaths = { "templateParticipation", "solutionParticipation", "teamAssignmentConfig", "categories", "competencies", "auxiliaryRepositories", - "submissionPolicy", "plagiarismDetectionConfig", "buildConfig" }) + @EntityGraph(type = LOAD, attributePaths = { "templateParticipation", "solutionParticipation", "teamAssignmentConfig", "categories", "competencyLinks.competency", + "auxiliaryRepositories", "submissionPolicy", "plagiarismDetectionConfig", "buildConfig" }) Optional findWithTemplateAndSolutionParticipationTeamAssignmentConfigCategoriesAndCompetenciesAndPlagiarismDetectionConfigAndBuildConfigById( long exerciseId); @@ -108,7 +108,7 @@ Optional findWithTemplateAndSolutionParticipationTeamAssign @EntityGraph(type = LOAD, attributePaths = "auxiliaryRepositories") Optional findWithAuxiliaryRepositoriesById(long exerciseId); - @EntityGraph(type = LOAD, attributePaths = { "auxiliaryRepositories", "competencies", "buildConfig" }) + @EntityGraph(type = LOAD, attributePaths = { "auxiliaryRepositories", "competencyLinks.competency", "buildConfig" }) Optional findWithAuxiliaryRepositoriesCompetenciesAndBuildConfigById(long exerciseId); @EntityGraph(type = LOAD, attributePaths = "submissionPolicy") @@ -547,7 +547,7 @@ SELECT COUNT (DISTINCT p) @Query(""" SELECT e FROM ProgrammingExercise e - LEFT JOIN FETCH e.competencies + LEFT JOIN FETCH e.competencyLinks WHERE e.title = :title AND e.course.id = :courseId """) @@ -556,7 +556,7 @@ SELECT COUNT (DISTINCT p) @Query(""" SELECT e FROM ProgrammingExercise e - LEFT JOIN FETCH e.competencies + LEFT JOIN FETCH e.competencyLinks WHERE e.shortName = :shortName AND e.course.id = :courseId """) @@ -958,7 +958,7 @@ enum ProgrammingExerciseFetchOptions implements FetchOptions { StaticCodeAnalysisCategories(ProgrammingExercise_.STATIC_CODE_ANALYSIS_CATEGORIES), SubmissionPolicy(ProgrammingExercise_.SUBMISSION_POLICY), ExerciseHints(ProgrammingExercise_.EXERCISE_HINTS), - Competencies(ProgrammingExercise_.COMPETENCIES), + CompetencyLinks(ProgrammingExercise_.COMPETENCY_LINKS), Teams(ProgrammingExercise_.TEAMS), TutorParticipations(ProgrammingExercise_.TUTOR_PARTICIPATIONS), ExampleSubmissions(ProgrammingExercise_.EXAMPLE_SUBMISSIONS), diff --git a/src/main/java/de/tum/cit/aet/artemis/quiz/repository/QuizExerciseRepository.java b/src/main/java/de/tum/cit/aet/artemis/quiz/repository/QuizExerciseRepository.java index 6f31cc005d1d..fc0537611937 100644 --- a/src/main/java/de/tum/cit/aet/artemis/quiz/repository/QuizExerciseRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/quiz/repository/QuizExerciseRepository.java @@ -62,14 +62,14 @@ public interface QuizExerciseRepository extends ArtemisJpaRepository findWithEagerQuestionsAndStatisticsById(Long quizExerciseId); - @EntityGraph(type = LOAD, attributePaths = { "quizQuestions", "quizPointStatistic", "quizQuestions.quizQuestionStatistic", "categories", "competencies", "quizBatches", - "gradingCriteria" }) + @EntityGraph(type = LOAD, attributePaths = { "quizQuestions", "quizPointStatistic", "quizQuestions.quizQuestionStatistic", "categories", "competencyLinks.competency", + "quizBatches", "gradingCriteria" }) Optional findWithEagerQuestionsAndStatisticsAndCompetenciesAndBatchesAndGradingCriteriaById(Long quizExerciseId); @EntityGraph(type = LOAD, attributePaths = { "quizQuestions" }) Optional findWithEagerQuestionsById(Long quizExerciseId); - @EntityGraph(type = LOAD, attributePaths = { "quizQuestions", "competencies" }) + @EntityGraph(type = LOAD, attributePaths = { "quizQuestions", "competencyLinks.competency" }) Optional findWithEagerQuestionsAndCompetenciesById(Long quizExerciseId); @EntityGraph(type = LOAD, attributePaths = { "quizBatches" }) @@ -78,7 +78,7 @@ public interface QuizExerciseRepository extends ArtemisJpaRepository findByCourseIdWithCategories(@Param("courseId") long courseId); - @EntityGraph(type = LOAD, attributePaths = { "competencies" }) + @EntityGraph(type = LOAD, attributePaths = { "competencyLinks.competency" }) Optional findWithEagerCompetenciesById(long exerciseId); - @EntityGraph(type = LOAD, attributePaths = { "teamAssignmentConfig", "categories", "competencies" }) + @EntityGraph(type = LOAD, attributePaths = { "teamAssignmentConfig", "categories", "competencyLinks.competency" }) Optional findWithEagerTeamAssignmentConfigAndCategoriesAndCompetenciesById(long exerciseId); - @EntityGraph(type = LOAD, attributePaths = { "teamAssignmentConfig", "categories", "competencies", "plagiarismDetectionConfig" }) + @EntityGraph(type = LOAD, attributePaths = { "teamAssignmentConfig", "categories", "competencyLinks.competency", "plagiarismDetectionConfig" }) Optional findWithEagerTeamAssignmentConfigAndCategoriesAndCompetenciesAndPlagiarismDetectionConfigById(long exerciseId); @Query(""" @@ -82,7 +82,7 @@ public interface TextExerciseRepository extends ArtemisJpaRepository + + + + + + + + + + + + + + diff --git a/src/main/resources/config/liquibase/master.xml b/src/main/resources/config/liquibase/master.xml index d496528a13ec..d3fbba28de55 100644 --- a/src/main/resources/config/liquibase/master.xml +++ b/src/main/resources/config/liquibase/master.xml @@ -28,6 +28,7 @@ + diff --git a/src/main/webapp/app/course/competencies/competencies-popover/competencies-popover.component.html b/src/main/webapp/app/course/competencies/competencies-popover/competencies-popover.component.html index 86b706cec05b..4d1c26d8b07a 100644 --- a/src/main/webapp/app/course/competencies/competencies-popover/competencies-popover.component.html +++ b/src/main/webapp/app/course/competencies/competencies-popover/competencies-popover.component.html @@ -1,11 +1,11 @@ - @if (competencies && competencies.length > 0) { + @if (competencyLinks?.length) {
diff --git a/src/main/webapp/app/course/competencies/competencies-popover/competencies-popover.component.ts b/src/main/webapp/app/course/competencies/competencies-popover/competencies-popover.component.ts index 8eee4ffa5a23..bdc5747b4ed3 100644 --- a/src/main/webapp/app/course/competencies/competencies-popover/competencies-popover.component.ts +++ b/src/main/webapp/app/course/competencies/competencies-popover/competencies-popover.component.ts @@ -1,6 +1,6 @@ import { Component, Input, OnInit, ViewEncapsulation } from '@angular/core'; import { faFlag } from '@fortawesome/free-solid-svg-icons'; -import { Competency } from 'app/entities/competency.model'; +import { CompetencyLectureUnitLink } from 'app/entities/competency.model'; @Component({ selector: 'jhi-competencies-popover', @@ -12,7 +12,7 @@ export class CompetenciesPopoverComponent implements OnInit { @Input() courseId: number; @Input() - competencies: Competency[] = []; + competencyLinks: CompetencyLectureUnitLink[] = []; @Input() navigateTo: 'competencyManagement' | 'courseCompetencies' = 'courseCompetencies'; diff --git a/src/main/webapp/app/course/competencies/competency.service.ts b/src/main/webapp/app/course/competencies/competency.service.ts index 1cba4d5410c4..ccac5b6318f3 100644 --- a/src/main/webapp/app/course/competencies/competency.service.ts +++ b/src/main/webapp/app/course/competencies/competency.service.ts @@ -37,6 +37,12 @@ export class CompetencyService extends CourseCompetencyService { } create(competency: Competency, courseId: number): Observable { + competency.lectureUnitLinks?.forEach((link) => { + link.lectureUnit = undefined; + if (link.competency?.lectureUnitLinks) { + link.competency.lectureUnitLinks = undefined; + } + }); const copy = this.convertCompetencyFromClient(competency); return this.httpClient.post(`${this.resourceURL}/courses/${courseId}/competencies`, copy, { observe: 'response' }); } @@ -91,6 +97,12 @@ export class CompetencyService extends CourseCompetencyService { } update(competency: Competency, courseId: number): Observable { + competency.lectureUnitLinks?.forEach((link) => { + link.lectureUnit = undefined; + if (link.competency?.lectureUnitLinks) { + link.competency.lectureUnitLinks = undefined; + } + }); const copy = this.convertCompetencyFromClient(competency); return this.httpClient.put(`${this.resourceURL}/courses/${courseId}/competencies`, copy, { observe: 'response' }); } diff --git a/src/main/webapp/app/course/competencies/course-competency.service.ts b/src/main/webapp/app/course/competencies/course-competency.service.ts index 04403d48db93..d5d4c07bd458 100644 --- a/src/main/webapp/app/course/competencies/course-competency.service.ts +++ b/src/main/webapp/app/course/competencies/course-competency.service.ts @@ -192,19 +192,21 @@ export class CourseCompetencyService { if (res.body?.softDueDate) { res.body.softDueDate = convertDateFromServer(res.body.softDueDate); } - if (res.body?.lectureUnits) { - res.body.lectureUnits = this.lectureUnitService.convertLectureUnitArrayDatesFromServer(res.body.lectureUnits); - } + res.body?.lectureUnitLinks?.forEach((lectureUnitLink) => { + if (lectureUnitLink.lectureUnit) { + lectureUnitLink.lectureUnit = this.lectureUnitService.convertLectureUnitDateFromServer(lectureUnitLink.lectureUnit); + } + }); if (res.body?.course) { this.accountService.setAccessRightsForCourse(res.body.course); } - if (res.body?.exercises) { - res.body.exercises = ExerciseService.convertExercisesDateFromServer(res.body.exercises); - res.body.exercises.forEach((exercise) => { - ExerciseService.parseExerciseCategories(exercise); - this.accountService.setAccessRightsForExercise(exercise); - }); - } + res.body?.exerciseLinks?.forEach((exerciseLink) => { + exerciseLink.exercise = ExerciseService.convertExerciseDatesFromServer(exerciseLink.exercise); + ExerciseService.parseExerciseCategories(exerciseLink.exercise); + if (exerciseLink.exercise) { + this.accountService.setAccessRightsForExercise(exerciseLink.exercise); + } + }); return res; } @@ -213,12 +215,17 @@ export class CourseCompetencyService { const copy = Object.assign({}, prerequisite, { softDueDate: convertDateFromClient(prerequisite.softDueDate), }); - if (copy.lectureUnits) { - copy.lectureUnits = this.lectureUnitService.convertLectureUnitArrayDatesFromClient(copy.lectureUnits); - } - if (copy.exercises) { - copy.exercises = copy.exercises.map((exercise) => ExerciseService.convertExerciseFromClient(exercise)); - } + copy.lectureUnitLinks?.forEach((lectureUnitLink) => { + if (lectureUnitLink.lectureUnit) { + lectureUnitLink.lectureUnit = this.lectureUnitService.convertLectureUnitDatesFromClient(lectureUnitLink.lectureUnit); + } + }); + copy.exerciseLinks?.forEach((exerciseLink) => { + if (exerciseLink.exercise) { + exerciseLink.exercise = ExerciseService.convertExerciseFromClient(exerciseLink.exercise); + } + }); + return copy; } diff --git a/src/main/webapp/app/course/competencies/create/create-competency.component.html b/src/main/webapp/app/course/competencies/create/create-competency.component.html index 1aadd38f47a2..3d9c3790d40c 100644 --- a/src/main/webapp/app/course/competencies/create/create-competency.component.html +++ b/src/main/webapp/app/course/competencies/create/create-competency.component.html @@ -10,6 +10,12 @@

- + } diff --git a/src/main/webapp/app/course/competencies/create/create-competency.component.ts b/src/main/webapp/app/course/competencies/create/create-competency.component.ts index c9faea9de7fe..aa30635759ef 100644 --- a/src/main/webapp/app/course/competencies/create/create-competency.component.ts +++ b/src/main/webapp/app/course/competencies/create/create-competency.component.ts @@ -38,7 +38,7 @@ export class CreateCompetencyComponent extends CreateCourseCompetencyComponent { return; } - const { title, description, softDueDate, taxonomy, masteryThreshold, optional, connectedLectureUnits } = formData; + const { title, description, softDueDate, taxonomy, masteryThreshold, optional, lectureUnitLinks } = formData; this.competencyToCreate.title = title; this.competencyToCreate.description = description; @@ -46,7 +46,7 @@ export class CreateCompetencyComponent extends CreateCourseCompetencyComponent { this.competencyToCreate.taxonomy = taxonomy; this.competencyToCreate.masteryThreshold = masteryThreshold; this.competencyToCreate.optional = optional; - this.competencyToCreate.lectureUnits = connectedLectureUnits; + this.competencyToCreate.lectureUnitLinks = lectureUnitLinks; this.isLoading = true; diff --git a/src/main/webapp/app/course/competencies/create/create-prerequisite.component.html b/src/main/webapp/app/course/competencies/create/create-prerequisite.component.html index 59e1065435c9..db7bbbec9b00 100644 --- a/src/main/webapp/app/course/competencies/create/create-prerequisite.component.html +++ b/src/main/webapp/app/course/competencies/create/create-prerequisite.component.html @@ -15,6 +15,7 @@

(formSubmitted)="createPrerequisite($event)" [courseId]="courseId" [lecturesOfCourseWithLectureUnits]="lecturesWithLectureUnits" + [prerequisite]="prerequisiteToCreate" /> } diff --git a/src/main/webapp/app/course/competencies/create/create-prerequisite.component.ts b/src/main/webapp/app/course/competencies/create/create-prerequisite.component.ts index 75c6413f84e7..c5ecdf011ac4 100644 --- a/src/main/webapp/app/course/competencies/create/create-prerequisite.component.ts +++ b/src/main/webapp/app/course/competencies/create/create-prerequisite.component.ts @@ -37,7 +37,7 @@ export class CreatePrerequisiteComponent extends CreateCourseCompetencyComponent return; } - const { title, description, softDueDate, taxonomy, masteryThreshold, optional, connectedLectureUnits } = formData; + const { title, description, softDueDate, taxonomy, masteryThreshold, optional, lectureUnitLinks } = formData; this.prerequisiteToCreate.title = title; this.prerequisiteToCreate.description = description; @@ -45,12 +45,12 @@ export class CreatePrerequisiteComponent extends CreateCourseCompetencyComponent this.prerequisiteToCreate.taxonomy = taxonomy; this.prerequisiteToCreate.masteryThreshold = masteryThreshold; this.prerequisiteToCreate.optional = optional; - this.prerequisiteToCreate.lectureUnits = connectedLectureUnits; + this.prerequisiteToCreate.lectureUnitLinks = lectureUnitLinks; this.isLoading = true; this.prerequisiteService - .create(this.prerequisiteToCreate!, this.courseId) + .create(this.prerequisiteToCreate, this.courseId) .pipe( finalize(() => { this.isLoading = false; diff --git a/src/main/webapp/app/course/competencies/edit/edit-competency.component.html b/src/main/webapp/app/course/competencies/edit/edit-competency.component.html index 7e166c011d7a..31423fd5b73c 100644 --- a/src/main/webapp/app/course/competencies/edit/edit-competency.component.html +++ b/src/main/webapp/app/course/competencies/edit/edit-competency.component.html @@ -14,6 +14,7 @@

[courseId]="courseId" [lecturesOfCourseWithLectureUnits]="lecturesWithLectureUnits" [averageStudentScore]="competency?.courseProgress?.averageStudentScore ?? 0" + [competency]="competency" /> } diff --git a/src/main/webapp/app/course/competencies/edit/edit-competency.component.ts b/src/main/webapp/app/course/competencies/edit/edit-competency.component.ts index 66687b025022..b5e24d7941c1 100644 --- a/src/main/webapp/app/course/competencies/edit/edit-competency.component.ts +++ b/src/main/webapp/app/course/competencies/edit/edit-competency.component.ts @@ -57,8 +57,8 @@ export class EditCompetencyComponent extends EditCourseCompetencyComponent imple this.competency.courseProgress = courseProgressResult.body; } // server will send undefined instead of empty array, therefore we set it here as it is easier to handle - if (!this.competency.lectureUnits) { - this.competency.lectureUnits = []; + if (!this.competency.lectureUnitLinks) { + this.competency.lectureUnitLinks = []; } } @@ -67,7 +67,7 @@ export class EditCompetencyComponent extends EditCourseCompetencyComponent imple title: this.competency.title, description: this.competency.description, softDueDate: this.competency.softDueDate, - connectedLectureUnits: this.competency.lectureUnits, + lectureUnitLinks: this.competency.lectureUnitLinks, taxonomy: this.competency.taxonomy, masteryThreshold: this.competency.masteryThreshold, optional: this.competency.optional, @@ -78,7 +78,7 @@ export class EditCompetencyComponent extends EditCourseCompetencyComponent imple } updateCompetency(formData: CourseCompetencyFormData) { - const { title, description, softDueDate, taxonomy, masteryThreshold, optional, connectedLectureUnits } = formData; + const { title, description, softDueDate, taxonomy, masteryThreshold, optional, lectureUnitLinks } = formData; this.competency.title = title; this.competency.description = description; @@ -86,7 +86,7 @@ export class EditCompetencyComponent extends EditCourseCompetencyComponent imple this.competency.taxonomy = taxonomy; this.competency.masteryThreshold = masteryThreshold; this.competency.optional = optional; - this.competency.lectureUnits = connectedLectureUnits; + this.competency.lectureUnitLinks = lectureUnitLinks; this.isLoading = true; diff --git a/src/main/webapp/app/course/competencies/edit/edit-prerequisite.component.html b/src/main/webapp/app/course/competencies/edit/edit-prerequisite.component.html index 8969dc814117..9d3f5968be1c 100644 --- a/src/main/webapp/app/course/competencies/edit/edit-prerequisite.component.html +++ b/src/main/webapp/app/course/competencies/edit/edit-prerequisite.component.html @@ -14,6 +14,7 @@

[courseId]="courseId" [lecturesOfCourseWithLectureUnits]="lecturesWithLectureUnits" [averageStudentScore]="prerequisite?.courseProgress?.averageStudentScore ?? 0" + [prerequisite]="prerequisite" /> } diff --git a/src/main/webapp/app/course/competencies/edit/edit-prerequisite.component.ts b/src/main/webapp/app/course/competencies/edit/edit-prerequisite.component.ts index 1ef4bbb39c9c..9dcf0b1490d1 100644 --- a/src/main/webapp/app/course/competencies/edit/edit-prerequisite.component.ts +++ b/src/main/webapp/app/course/competencies/edit/edit-prerequisite.component.ts @@ -58,8 +58,8 @@ export class EditPrerequisiteComponent extends EditCourseCompetencyComponent imp this.prerequisite.courseProgress = courseProgressResult.body; } // server will send undefined instead of empty array, therefore we set it here as it is easier to handle - if (!this.prerequisite.lectureUnits) { - this.prerequisite.lectureUnits = []; + if (!this.prerequisite.lectureUnitLinks) { + this.prerequisite.lectureUnitLinks = []; } } @@ -68,7 +68,7 @@ export class EditPrerequisiteComponent extends EditCourseCompetencyComponent imp title: this.prerequisite.title, description: this.prerequisite.description, softDueDate: this.prerequisite.softDueDate, - connectedLectureUnits: this.prerequisite.lectureUnits, + lectureUnitLinks: this.prerequisite.lectureUnitLinks, taxonomy: this.prerequisite.taxonomy, masteryThreshold: this.prerequisite.masteryThreshold, optional: this.prerequisite.optional, @@ -79,7 +79,7 @@ export class EditPrerequisiteComponent extends EditCourseCompetencyComponent imp } updateCompetency(formData: CourseCompetencyFormData) { - const { title, description, softDueDate, taxonomy, masteryThreshold, optional, connectedLectureUnits } = formData; + const { title, description, softDueDate, taxonomy, masteryThreshold, optional, lectureUnitLinks } = formData; this.prerequisite.title = title; this.prerequisite.description = description; @@ -87,7 +87,7 @@ export class EditPrerequisiteComponent extends EditCourseCompetencyComponent imp this.prerequisite.taxonomy = taxonomy; this.prerequisite.masteryThreshold = masteryThreshold; this.prerequisite.optional = optional; - this.prerequisite.lectureUnits = connectedLectureUnits; + this.prerequisite.lectureUnitLinks = lectureUnitLinks; this.isLoading = true; diff --git a/src/main/webapp/app/course/competencies/forms/common-course-competency-form.component.html b/src/main/webapp/app/course/competencies/forms/common-course-competency-form.component.html index 77a39956b91c..6eda4fbaad0d 100644 --- a/src/main/webapp/app/course/competencies/forms/common-course-competency-form.component.html +++ b/src/main/webapp/app/course/competencies/forms/common-course-competency-form.component.html @@ -1,21 +1,24 @@
@if (!isInConnectMode) {
- + @if (titleControl?.invalid && (titleControl?.dirty || titleControl?.touched)) {
@if (titleControl?.errors?.required) { -
+
} @if (titleControl?.errors?.maxlength) { -
+
} @if (titleControl?.errors?.titleUnique) {
@@ -38,7 +41,7 @@
@if (descriptionControl?.errors?.maxlength) {
} @@ -49,8 +52,8 @@
@@ -78,11 +81,11 @@ }
- - + +
- + @if (lecturesOfCourseWithLectureUnits && lecturesOfCourseWithLectureUnits.length > 0) {
} diff --git a/src/main/webapp/app/exercises/quiz/manage/quiz-exercise-detail.component.ts b/src/main/webapp/app/exercises/quiz/manage/quiz-exercise-detail.component.ts index 207d394ce4a1..77166e7bfb2b 100644 --- a/src/main/webapp/app/exercises/quiz/manage/quiz-exercise-detail.component.ts +++ b/src/main/webapp/app/exercises/quiz/manage/quiz-exercise-detail.component.ts @@ -78,11 +78,11 @@ export class QuizExerciseDetailComponent implements OnInit { const modeSection = getExerciseModeDetailSection(exercise); const defaultGradingDetails = getExerciseGradingDefaultDetails(exercise); - if (exercise.competencies?.length) { + if (exercise.competencyLinks?.length) { modeSection.details.push({ title: 'artemisApp.competency.link.title', type: DetailType.Text, - data: { text: exercise.competencies?.map((competency) => competency.title).join(', ') }, + data: { text: exercise.competencyLinks?.map((competencyLink) => competencyLink.competency?.title).join(', ') }, }); } return [ diff --git a/src/main/webapp/app/exercises/quiz/manage/quiz-exercise-update.component.html b/src/main/webapp/app/exercises/quiz/manage/quiz-exercise-update.component.html index 5548d546d1b7..1946a13812ef 100644 --- a/src/main/webapp/app/exercises/quiz/manage/quiz-exercise-update.component.html +++ b/src/main/webapp/app/exercises/quiz/manage/quiz-exercise-update.component.html @@ -219,9 +219,9 @@

id="competencies" [labelName]="'artemisApp.competency.link.title' | artemisTranslate" [labelTooltip]="'artemisApp.competency.link.exercise' | artemisTranslate" - [(ngModel)]="quizExercise.competencies" + [(ngModel)]="quizExercise.competencyLinks" (ngModelChange)="cacheValidation()" - name="competencies" + name="competencyLinks" />

} diff --git a/src/main/webapp/app/exercises/shared/utils.ts b/src/main/webapp/app/exercises/shared/utils.ts index 9e3955478289..de47432a0cb5 100644 --- a/src/main/webapp/app/exercises/shared/utils.ts +++ b/src/main/webapp/app/exercises/shared/utils.ts @@ -63,7 +63,7 @@ export function getExerciseModeDetailSection(exercise: Exercise): DetailOverview } export function getExerciseProblemDetailSection(formattedProblemStatement: SafeHtml | null, exercise: Exercise): DetailOverviewSection { - const hasCompetencies = !!exercise.competencies?.length; + const hasCompetencies = !!exercise.competencyLinks?.length; const details: Detail[] = [ { title: hasCompetencies ? 'artemisApp.exercise.problemStatement' : undefined, @@ -76,7 +76,7 @@ export function getExerciseProblemDetailSection(formattedProblemStatement: SafeH details.push({ title: 'artemisApp.competency.link.title', type: DetailType.Text, - data: { text: exercise.competencies?.map((competency) => competency.title).join(', ') }, + data: { text: exercise.competencyLinks?.map((competencyLink) => competencyLink.competency?.title).join(', ') }, }); } return { diff --git a/src/main/webapp/app/exercises/text/manage/text-exercise/text-exercise-update.component.html b/src/main/webapp/app/exercises/text/manage/text-exercise/text-exercise-update.component.html index 9200b885485c..3a1c9182658a 100644 --- a/src/main/webapp/app/exercises/text/manage/text-exercise/text-exercise-update.component.html +++ b/src/main/webapp/app/exercises/text/manage/text-exercise/text-exercise-update.component.html @@ -47,8 +47,8 @@

} diff --git a/src/main/webapp/app/lecture/attachment.service.ts b/src/main/webapp/app/lecture/attachment.service.ts index 49f6739bfd8d..0ba18b39a248 100644 --- a/src/main/webapp/app/lecture/attachment.service.ts +++ b/src/main/webapp/app/lecture/attachment.service.ts @@ -26,7 +26,7 @@ export class AttachmentService { // avoid potential issues when sending the attachment to the server if (copy.attachmentUnit) { copy.attachmentUnit.lecture = undefined; - copy.attachmentUnit.competencies = undefined; + copy.attachmentUnit.competencyLinks = undefined; } if (copy.lecture) { copy.lecture.lectureUnits = undefined; diff --git a/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/attachment-unit-form/attachment-unit-form.component.html b/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/attachment-unit-form/attachment-unit-form.component.html index 414beb7b9666..c7d9568fb7c2 100644 --- a/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/attachment-unit-form/attachment-unit-form.component.html +++ b/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/attachment-unit-form/attachment-unit-form.component.html @@ -78,7 +78,7 @@ id="competencies" [labelName]="'artemisApp.competency.link.title' | artemisTranslate" [labelTooltip]="'artemisApp.competency.link.lectureUnit' | artemisTranslate" - formControlName="competencies" + formControlName="competencyLinks" />
diff --git a/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/attachment-unit-form/attachment-unit-form.component.ts b/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/attachment-unit-form/attachment-unit-form.component.ts index ea597af554eb..766379c5847b 100644 --- a/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/attachment-unit-form/attachment-unit-form.component.ts +++ b/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/attachment-unit-form/attachment-unit-form.component.ts @@ -4,7 +4,7 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { TranslateService } from '@ngx-translate/core'; import { faQuestionCircle, faTimes } from '@fortawesome/free-solid-svg-icons'; import { UPLOAD_FILE_EXTENSIONS } from 'app/shared/constants/file-extensions.constants'; -import { Competency } from 'app/entities/competency.model'; +import { CompetencyLectureUnitLink } from 'app/entities/competency.model'; import { MAX_FILE_SIZE } from 'app/shared/constants/input.constants'; export interface AttachmentUnitFormData { @@ -19,7 +19,7 @@ export interface FormProperties { releaseDate?: dayjs.Dayjs; version?: number; updateNotificationText?: string; - competencies?: Competency[]; + competencyLinks?: CompetencyLectureUnitLink[]; } // file input is a special case and is not included in the reactive form structure @@ -90,7 +90,7 @@ export class AttachmentUnitFormComponent implements OnInit, OnChanges { releaseDate: [undefined as dayjs.Dayjs | undefined], version: [{ value: 1, disabled: true }], updateNotificationText: [undefined as string | undefined, [Validators.maxLength(1000)]], - competencies: [undefined as Competency[] | undefined], + competencyLinks: [undefined as CompetencyLectureUnitLink[] | undefined], }); } diff --git a/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/create-attachment-unit/create-attachment-unit.component.ts b/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/create-attachment-unit/create-attachment-unit.component.ts index 4d45889f29de..55a07861bac9 100644 --- a/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/create-attachment-unit/create-attachment-unit.component.ts +++ b/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/create-attachment-unit/create-attachment-unit.component.ts @@ -47,7 +47,7 @@ export class CreateAttachmentUnitComponent implements OnInit { if (!attachmentUnitFormData?.formProperties?.name || !attachmentUnitFormData?.fileProperties?.file || !attachmentUnitFormData?.fileProperties?.fileName) { return; } - const { description, name, releaseDate, competencies } = attachmentUnitFormData.formProperties; + const { description, name, releaseDate, competencyLinks } = attachmentUnitFormData.formProperties; const { file, fileName } = attachmentUnitFormData.fileProperties; // === Setting attachment === @@ -59,7 +59,7 @@ export class CreateAttachmentUnitComponent implements OnInit { // === Setting attachmentUnit === this.attachmentUnitToCreate.description = description; - this.attachmentUnitToCreate.competencies = competencies || []; + this.attachmentUnitToCreate.competencyLinks = competencyLinks || []; this.isLoading = true; diff --git a/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/create-online-unit/create-online-unit.component.ts b/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/create-online-unit/create-online-unit.component.ts index 7297df122b17..a042bd6ee535 100644 --- a/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/create-online-unit/create-online-unit.component.ts +++ b/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/create-online-unit/create-online-unit.component.ts @@ -41,13 +41,13 @@ export class CreateOnlineUnitComponent implements OnInit { return; } - const { name, description, releaseDate, source, competencies } = formData; + const { name, description, releaseDate, source, competencyLinks } = formData; this.onlineUnitToCreate.name = name || undefined; this.onlineUnitToCreate.releaseDate = releaseDate || undefined; this.onlineUnitToCreate.description = description || undefined; this.onlineUnitToCreate.source = source || undefined; - this.onlineUnitToCreate.competencies = competencies || []; + this.onlineUnitToCreate.competencyLinks = competencyLinks || []; this.isLoading = true; diff --git a/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/create-text-unit/create-text-unit.component.ts b/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/create-text-unit/create-text-unit.component.ts index 7030dfbaf72d..7e8974f2114e 100644 --- a/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/create-text-unit/create-text-unit.component.ts +++ b/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/create-text-unit/create-text-unit.component.ts @@ -40,12 +40,12 @@ export class CreateTextUnitComponent implements OnInit { return; } - const { name, releaseDate, content, competencies } = formData; + const { name, releaseDate, content, competencyLinks } = formData; this.textUnitToCreate.name = name; this.textUnitToCreate.releaseDate = releaseDate; this.textUnitToCreate.content = content; - this.textUnitToCreate.competencies = competencies || []; + this.textUnitToCreate.competencyLinks = competencyLinks || []; this.isLoading = true; diff --git a/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/create-video-unit/create-video-unit.component.ts b/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/create-video-unit/create-video-unit.component.ts index 620ab259d323..2eaf98ebcbc3 100644 --- a/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/create-video-unit/create-video-unit.component.ts +++ b/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/create-video-unit/create-video-unit.component.ts @@ -41,13 +41,13 @@ export class CreateVideoUnitComponent implements OnInit { return; } - const { name, description, releaseDate, source, competencies } = formData; + const { name, description, releaseDate, source, competencyLinks } = formData; this.videoUnitToCreate.name = name || undefined; this.videoUnitToCreate.releaseDate = releaseDate || undefined; this.videoUnitToCreate.description = description || undefined; this.videoUnitToCreate.source = source || undefined; - this.videoUnitToCreate.competencies = competencies || []; + this.videoUnitToCreate.competencyLinks = competencyLinks || []; this.isLoading = true; diff --git a/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/edit-attachment-unit/edit-attachment-unit.component.ts b/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/edit-attachment-unit/edit-attachment-unit.component.ts index f5568b9c0d2c..b3c5741ebcbd 100644 --- a/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/edit-attachment-unit/edit-attachment-unit.component.ts +++ b/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/edit-attachment-unit/edit-attachment-unit.component.ts @@ -62,7 +62,7 @@ export class EditAttachmentUnitComponent implements OnInit { description: this.attachmentUnit.description, releaseDate: this.attachment.releaseDate, version: this.attachment.version, - competencies: this.attachmentUnit.competencies, + competencyLinks: this.attachmentUnit.competencyLinks, }, fileProperties: { fileName: this.attachment.link, @@ -74,7 +74,7 @@ export class EditAttachmentUnitComponent implements OnInit { } updateAttachmentUnit(attachmentUnitFormData: AttachmentUnitFormData) { - const { description, name, releaseDate, updateNotificationText, competencies } = attachmentUnitFormData.formProperties; + const { description, name, releaseDate, updateNotificationText, competencyLinks } = attachmentUnitFormData.formProperties; const { file, fileName } = attachmentUnitFormData.fileProperties; // optional update notification text for students @@ -88,7 +88,7 @@ export class EditAttachmentUnitComponent implements OnInit { this.attachment.attachmentType = AttachmentType.FILE; // === Setting attachmentUnit === this.attachmentUnit.description = description; - this.attachmentUnit.competencies = competencies; + this.attachmentUnit.competencyLinks = competencyLinks; this.isLoading = true; diff --git a/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/edit-online-unit/edit-online-unit.component.ts b/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/edit-online-unit/edit-online-unit.component.ts index f169247b1a65..2c76f9690cdd 100644 --- a/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/edit-online-unit/edit-online-unit.component.ts +++ b/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/edit-online-unit/edit-online-unit.component.ts @@ -51,7 +51,7 @@ export class EditOnlineUnitComponent implements OnInit { description: this.onlineUnit.description, releaseDate: this.onlineUnit.releaseDate, source: this.onlineUnit.source, - competencies: this.onlineUnit.competencies, + competencyLinks: this.onlineUnit.competencyLinks, }; }, error: (res: HttpErrorResponse) => onError(this.alertService, res), @@ -59,12 +59,12 @@ export class EditOnlineUnitComponent implements OnInit { } updateOnlineUnit(formData: OnlineUnitFormData) { - const { name, description, releaseDate, source, competencies } = formData; + const { name, description, releaseDate, source, competencyLinks } = formData; this.onlineUnit.name = name; this.onlineUnit.description = description; this.onlineUnit.releaseDate = releaseDate; this.onlineUnit.source = source; - this.onlineUnit.competencies = competencies; + this.onlineUnit.competencyLinks = competencyLinks; this.isLoading = true; this.onlineUnitService .update(this.onlineUnit, this.lectureId) diff --git a/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/edit-text-unit/edit-text-unit.component.ts b/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/edit-text-unit/edit-text-unit.component.ts index 825539a24eeb..922412d811a2 100644 --- a/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/edit-text-unit/edit-text-unit.component.ts +++ b/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/edit-text-unit/edit-text-unit.component.ts @@ -48,7 +48,7 @@ export class EditTextUnitComponent implements OnInit { name: this.textUnit.name, releaseDate: this.textUnit.releaseDate, content: this.textUnit.content, - competencies: this.textUnit.competencies, + competencyLinks: this.textUnit.competencyLinks, }; }, error: (res: HttpErrorResponse) => onError(this.alertService, res), @@ -56,11 +56,12 @@ export class EditTextUnitComponent implements OnInit { } updateTextUnit(formData: TextUnitFormData) { - const { name, releaseDate, content, competencies } = formData; + const { name, releaseDate, content, competencyLinks } = formData; this.textUnit.name = name; this.textUnit.releaseDate = releaseDate; this.textUnit.content = content; - this.textUnit.competencies = competencies; + this.textUnit.competencyLinks = competencyLinks; + this.isLoading = true; this.textUnitService .update(this.textUnit, this.lectureId) diff --git a/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/edit-video-unit/edit-video-unit.component.ts b/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/edit-video-unit/edit-video-unit.component.ts index 732d7fba0e18..4cb4bf415436 100644 --- a/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/edit-video-unit/edit-video-unit.component.ts +++ b/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/edit-video-unit/edit-video-unit.component.ts @@ -51,7 +51,7 @@ export class EditVideoUnitComponent implements OnInit { description: this.videoUnit.description, releaseDate: this.videoUnit.releaseDate, source: this.videoUnit.source, - competencies: this.videoUnit.competencies, + competencyLinks: this.videoUnit.competencyLinks, }; }, error: (res: HttpErrorResponse) => onError(this.alertService, res), @@ -59,12 +59,12 @@ export class EditVideoUnitComponent implements OnInit { } updateVideoUnit(formData: VideoUnitFormData) { - const { name, description, releaseDate, source, competencies } = formData; + const { name, description, releaseDate, source, competencyLinks } = formData; this.videoUnit.name = name; this.videoUnit.description = description; this.videoUnit.releaseDate = releaseDate; this.videoUnit.source = source; - this.videoUnit.competencies = competencies; + this.videoUnit.competencyLinks = competencyLinks; this.isLoading = true; this.videoUnitService .update(this.videoUnit, this.lectureId) diff --git a/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/lecture-unit-management.component.html b/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/lecture-unit-management.component.html index 2dea431d4934..de1442861a96 100644 --- a/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/lecture-unit-management.component.html +++ b/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/lecture-unit-management.component.html @@ -69,7 +69,11 @@
@if (lecture.course?.id && showCompetencies) { - + } @if (viewButtonAvailable[lectureUnit.id!]) {
diff --git a/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/online-unit-form/online-unit-form.component.ts b/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/online-unit-form/online-unit-form.component.ts index c9dc829463cd..3648cb3bb8db 100644 --- a/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/online-unit-form/online-unit-form.component.ts +++ b/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/online-unit-form/online-unit-form.component.ts @@ -6,14 +6,14 @@ import { map } from 'rxjs'; import { HttpResponse } from '@angular/common/http'; import { OnlineResourceDTO } from 'app/lecture/lecture-unit/lecture-unit-management/online-resource-dto.model'; import { OnlineUnitService } from 'app/lecture/lecture-unit/lecture-unit-management/onlineUnit.service'; -import { Competency } from 'app/entities/competency.model'; +import { CompetencyLectureUnitLink } from 'app/entities/competency.model'; export interface OnlineUnitFormData { name?: string; description?: string; releaseDate?: dayjs.Dayjs; source?: string; - competencies?: Competency[]; + competencyLinks?: CompetencyLectureUnitLink[]; } function urlValidator(control: AbstractControl) { @@ -95,7 +95,7 @@ export class OnlineUnitFormComponent implements OnInit, OnChanges { description: [undefined, [Validators.maxLength(1000)]], releaseDate: [undefined], source: [undefined, [Validators.required, this.urlValidator]], - competencies: [undefined as Competency[] | undefined], + competencyLinks: [undefined as CompetencyLectureUnitLink[] | undefined], }); } diff --git a/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/text-unit-form/text-unit-form.component.html b/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/text-unit-form/text-unit-form.component.html index 8f0eccda8242..403f20a774cf 100644 --- a/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/text-unit-form/text-unit-form.component.html +++ b/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/text-unit-form/text-unit-form.component.html @@ -30,7 +30,7 @@ id="competencies" [labelName]="'artemisApp.competency.link.title' | artemisTranslate" [labelTooltip]="'artemisApp.competency.link.lectureUnit' | artemisTranslate" - formControlName="competencies" + formControlName="competencyLinks" />
diff --git a/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/text-unit-form/text-unit-form.component.ts b/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/text-unit-form/text-unit-form.component.ts index 82bfbcd5be94..28817538a1e4 100644 --- a/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/text-unit-form/text-unit-form.component.ts +++ b/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/text-unit-form/text-unit-form.component.ts @@ -6,13 +6,13 @@ import { Subject, Subscription } from 'rxjs'; import { debounceTime } from 'rxjs/operators'; import { TranslateService } from '@ngx-translate/core'; import { faTimes } from '@fortawesome/free-solid-svg-icons'; -import { Competency } from 'app/entities/competency.model'; +import { CompetencyLectureUnitLink } from 'app/entities/competency.model'; export interface TextUnitFormData { name?: string; releaseDate?: dayjs.Dayjs; content?: string; - competencies?: Competency[]; + competencyLinks?: CompetencyLectureUnitLink[]; } @Component({ @@ -95,7 +95,7 @@ export class TextUnitFormComponent implements OnInit, OnChanges, OnDestroy { this.form = this.fb.group({ name: [undefined as string | undefined, [Validators.required, Validators.maxLength(255)]], releaseDate: [undefined as dayjs.Dayjs | undefined], - competencies: [undefined as Competency[] | undefined], + competencyLinks: [undefined as CompetencyLectureUnitLink[] | undefined], }); } diff --git a/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/video-unit-form/video-unit-form.component.html b/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/video-unit-form/video-unit-form.component.html index 4253c17c54cb..28218967e14e 100644 --- a/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/video-unit-form/video-unit-form.component.html +++ b/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/video-unit-form/video-unit-form.component.html @@ -47,7 +47,7 @@ id="competencies" [labelName]="'artemisApp.competency.link.title' | artemisTranslate" [labelTooltip]="'artemisApp.competency.link.lectureUnit' | artemisTranslate" - formControlName="competencies" + formControlName="competencyLinks" />
diff --git a/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/video-unit-form/video-unit-form.component.ts b/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/video-unit-form/video-unit-form.component.ts index 0670285084c4..65ca7da964f2 100644 --- a/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/video-unit-form/video-unit-form.component.ts +++ b/src/main/webapp/app/lecture/lecture-unit/lecture-unit-management/video-unit-form/video-unit-form.component.ts @@ -3,14 +3,14 @@ import { Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angu import { AbstractControl, FormBuilder, FormGroup, ValidationErrors, Validators } from '@angular/forms'; import urlParser from 'js-video-url-parser'; import { faArrowLeft, faTimes } from '@fortawesome/free-solid-svg-icons'; -import { Competency } from 'app/entities/competency.model'; +import { CompetencyLectureUnitLink } from 'app/entities/competency.model'; export interface VideoUnitFormData { name?: string; description?: string; releaseDate?: dayjs.Dayjs; source?: string; - competencies?: Competency[]; + competencyLinks?: CompetencyLectureUnitLink[]; } function isTumLiveUrl(url: URL): boolean { @@ -123,7 +123,7 @@ export class VideoUnitFormComponent implements OnInit, OnChanges { releaseDate: [undefined as dayjs.Dayjs | undefined], source: [undefined as string | undefined, [Validators.required, this.videoSourceUrlValidator]], urlHelper: [undefined as string | undefined, this.videoSourceTransformUrlValidator], - competencies: [undefined as Competency[] | undefined], + competencyLinks: [undefined as CompetencyLectureUnitLink[] | undefined], }); } diff --git a/src/main/webapp/app/lecture/wizard-mode/lecture-wizard-units.component.ts b/src/main/webapp/app/lecture/wizard-mode/lecture-wizard-units.component.ts index 9d08f4063e84..1488598c3ad7 100644 --- a/src/main/webapp/app/lecture/wizard-mode/lecture-wizard-units.component.ts +++ b/src/main/webapp/app/lecture/wizard-mode/lecture-wizard-units.component.ts @@ -105,13 +105,13 @@ export class LectureUpdateWizardUnitsComponent implements OnInit { return; } - const { name, releaseDate, content, competencies } = formData; + const { name, releaseDate, content, competencyLinks } = formData; this.currentlyProcessedTextUnit = this.isEditingLectureUnit ? this.currentlyProcessedTextUnit : new TextUnit(); this.currentlyProcessedTextUnit.name = name; this.currentlyProcessedTextUnit.releaseDate = releaseDate; this.currentlyProcessedTextUnit.content = content; - this.currentlyProcessedTextUnit.competencies = competencies; + this.currentlyProcessedTextUnit.competencyLinks = competencyLinks; (this.isEditingLectureUnit ? this.textUnitService.update(this.currentlyProcessedTextUnit, this.lecture.id!) @@ -130,14 +130,14 @@ export class LectureUpdateWizardUnitsComponent implements OnInit { return; } - const { name, description, releaseDate, source, competencies } = formData; + const { name, description, releaseDate, source, competencyLinks } = formData; this.currentlyProcessedVideoUnit = this.isEditingLectureUnit ? this.currentlyProcessedVideoUnit : new VideoUnit(); this.currentlyProcessedVideoUnit.name = name || undefined; this.currentlyProcessedVideoUnit.releaseDate = releaseDate || undefined; this.currentlyProcessedVideoUnit.description = description || undefined; this.currentlyProcessedVideoUnit.source = source || undefined; - this.currentlyProcessedVideoUnit.competencies = competencies; + this.currentlyProcessedVideoUnit.competencyLinks = competencyLinks; (this.isEditingLectureUnit ? this.videoUnitService.update(this.currentlyProcessedVideoUnit, this.lecture.id!) @@ -156,14 +156,14 @@ export class LectureUpdateWizardUnitsComponent implements OnInit { return; } - const { name, description, releaseDate, source, competencies } = formData; + const { name, description, releaseDate, source, competencyLinks } = formData; this.currentlyProcessedOnlineUnit = this.isEditingLectureUnit ? this.currentlyProcessedOnlineUnit : new OnlineUnit(); this.currentlyProcessedOnlineUnit.name = name || undefined; this.currentlyProcessedOnlineUnit.releaseDate = releaseDate || undefined; this.currentlyProcessedOnlineUnit.description = description || undefined; this.currentlyProcessedOnlineUnit.source = source || undefined; - this.currentlyProcessedOnlineUnit.competencies = competencies || undefined; + this.currentlyProcessedOnlineUnit.competencyLinks = competencyLinks || undefined; (this.isEditingLectureUnit ? this.onlineUnitService.update(this.currentlyProcessedOnlineUnit, this.lecture.id!) @@ -182,7 +182,7 @@ export class LectureUpdateWizardUnitsComponent implements OnInit { return; } - const { description, name, releaseDate, updateNotificationText, competencies } = attachmentUnitFormData.formProperties; + const { description, name, releaseDate, updateNotificationText, competencyLinks } = attachmentUnitFormData.formProperties; const { file, fileName } = attachmentUnitFormData.fileProperties; this.currentlyProcessedAttachmentUnit = this.isEditingLectureUnit ? this.currentlyProcessedAttachmentUnit : new AttachmentUnit(); @@ -213,9 +213,7 @@ export class LectureUpdateWizardUnitsComponent implements OnInit { if (description) { this.currentlyProcessedAttachmentUnit.description = description; } - if (competencies) { - this.currentlyProcessedAttachmentUnit.competencies = competencies; - } + this.currentlyProcessedAttachmentUnit.competencyLinks = competencyLinks; const formData = new FormData(); formData.append('file', file, fileName); diff --git a/src/main/webapp/app/overview/course-competencies/course-competencies-details.component.html b/src/main/webapp/app/overview/course-competencies/course-competencies-details.component.html index 57a6aba8aafd..08227e77b002 100644 --- a/src/main/webapp/app/overview/course-competencies/course-competencies-details.component.html +++ b/src/main/webapp/app/overview/course-competencies/course-competencies-details.component.html @@ -51,26 +51,28 @@

- @for (lectureUnit of competency.lectureUnits; track lectureUnit) { -
- @switch (lectureUnit.type) { - @case (LectureUnitType.EXERCISE) { - + @for (lectureUnitLink of competency.lectureUnitLinks; track lectureUnitLink) { + @if (lectureUnitLink.lectureUnit; as lectureUnit) { +
+ @switch (lectureUnit.type) { + @case (LectureUnitType.EXERCISE) { + + } + @case (LectureUnitType.ATTACHMENT) { + + } + @case (LectureUnitType.VIDEO) { + + } + @case (LectureUnitType.TEXT) { + + } + @case (LectureUnitType.ONLINE) { + + } } - @case (LectureUnitType.ATTACHMENT) { - - } - @case (LectureUnitType.VIDEO) { - - } - @case (LectureUnitType.TEXT) { - - } - @case (LectureUnitType.ONLINE) { - - } - } -
+
+ } }
diff --git a/src/main/webapp/app/overview/course-competencies/course-competencies-details.component.ts b/src/main/webapp/app/overview/course-competencies/course-competencies-details.component.ts index b3806538ead4..f751f6176b99 100644 --- a/src/main/webapp/app/overview/course-competencies/course-competencies-details.component.ts +++ b/src/main/webapp/app/overview/course-competencies/course-competencies-details.component.ts @@ -1,7 +1,17 @@ import dayjs from 'dayjs/esm'; import { Component, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { Competency, CompetencyJol, CompetencyProgress, ConfidenceReason, getConfidence, getIcon, getMastery, getProgress } from 'app/entities/competency.model'; +import { + Competency, + CompetencyJol, + CompetencyLectureUnitLink, + CompetencyProgress, + ConfidenceReason, + getConfidence, + getIcon, + getMastery, + getProgress, +} from 'app/entities/competency.model'; import { AlertService } from 'app/core/util/alert.service'; import { onError } from 'app/shared/util/global.utils'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; @@ -107,15 +117,15 @@ export class CourseCompetenciesDetailsComponent implements OnInit, OnDestroy { } } - if (this.competency && this.competency.exercises) { + if (this.competency.exerciseLinks) { // Add exercises as lecture units for display - this.competency.lectureUnits = this.competency.lectureUnits ?? []; - this.competency.lectureUnits.push( - ...this.competency.exercises.map((exercise) => { + this.competency.lectureUnitLinks = this.competency.lectureUnitLinks ?? []; + this.competency.lectureUnitLinks.push( + ...this.competency.exerciseLinks.map((exerciseLink) => { const exerciseUnit = new ExerciseUnit(); - exerciseUnit.id = exercise.id; - exerciseUnit.exercise = exercise; - return exerciseUnit as LectureUnit; + exerciseUnit.id = exerciseLink.exercise?.id; + exerciseUnit.exercise = exerciseLink.exercise; + return new CompetencyLectureUnitLink(this.competency, exerciseUnit as LectureUnit, 1); }), ); } diff --git a/src/main/webapp/app/overview/course-lectures/course-lecture-details.component.html b/src/main/webapp/app/overview/course-lectures/course-lecture-details.component.html index 2bec1658d7e9..d22a6cd676ab 100644 --- a/src/main/webapp/app/overview/course-lectures/course-lecture-details.component.html +++ b/src/main/webapp/app/overview/course-lectures/course-lecture-details.component.html @@ -89,9 +89,9 @@

diff --git a/src/main/webapp/app/shared/competency-selection/competency-selection.component.html b/src/main/webapp/app/shared/competency-selection/competency-selection.component.html index 14e7fd44ceee..b15b8f4cfd8f 100644 --- a/src/main/webapp/app/shared/competency-selection/competency-selection.component.html +++ b/src/main/webapp/app/shared/competency-selection/competency-selection.component.html @@ -1,4 +1,4 @@ -@if (isLoading || (competencies && competencies.length)) { +@if (isLoading || competencyLinks?.length) {
@if (labelName) {
} @else {
- @for (competency of competencies; track competency) { - @if (competency.id) { + @for (competencyLink of competencyLinks; track competencyLink) { + @if (competencyLink && competencyLink.competency && competencyLink.competency.id) {
-
} diff --git a/src/main/webapp/app/shared/competency-selection/competency-selection.component.ts b/src/main/webapp/app/shared/competency-selection/competency-selection.component.ts index 50febc424a84..09b326be2f8d 100644 --- a/src/main/webapp/app/shared/competency-selection/competency-selection.component.ts +++ b/src/main/webapp/app/shared/competency-selection/competency-selection.component.ts @@ -1,7 +1,7 @@ import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output, forwardRef } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { faQuestionCircle } from '@fortawesome/free-solid-svg-icons'; -import { Competency, CourseCompetency, getIcon } from 'app/entities/competency.model'; +import { CompetencyLearningObjectLink, CourseCompetency, getIcon } from 'app/entities/competency.model'; import { ActivatedRoute } from '@angular/router'; import { CourseStorageService } from 'app/course/manage/course-storage.service'; import { finalize } from 'rxjs'; @@ -27,9 +27,9 @@ export class CompetencySelectionComponent implements OnInit, ControlValueAccesso disabled: boolean; // selected competencies - selectedCompetencies?: Competency[]; + selectedCompetencyLinks?: CompetencyLearningObjectLink[]; // all course competencies - competencies?: CourseCompetency[]; + competencyLinks?: CompetencyLearningObjectLink[]; isLoading = false; checkboxStates: Record; @@ -49,11 +49,11 @@ export class CompetencySelectionComponent implements OnInit, ControlValueAccesso ngOnInit(): void { const courseId = Number(this.route.snapshot.paramMap.get('courseId')); - if (!this.competencies && courseId) { + if (!this.competencyLinks && courseId) { const course = this.courseStorageService.getCourse(courseId); // an empty array is used as fallback, if a course is cached, where no competencies have been queried if (course?.competencies?.length || course?.prerequisites?.length) { - this.setCompetencies([...(course.competencies ?? []), ...(course.prerequisites ?? [])]); + this.setCompetencyLinks([...(course.competencies ?? []), ...(course.prerequisites ?? [])]); } else { this.isLoading = true; this.courseCompetencyService @@ -69,8 +69,8 @@ export class CompetencySelectionComponent implements OnInit, ControlValueAccesso ) .subscribe({ next: (response) => { - this.setCompetencies(response.body!); - this.writeValue(this.selectedCompetencies); + this.setCompetencyLinks(response.body!); + this.writeValue(this.selectedCompetencyLinks); }, error: () => { this.disabled = true; @@ -81,20 +81,20 @@ export class CompetencySelectionComponent implements OnInit, ControlValueAccesso } /** - * Set the available competencies for selection + * Set the available competencyLinks for selection * @param competencies The competencies of the course */ - setCompetencies(competencies: CourseCompetency[]) { - this.competencies = competencies.map((competency) => { + setCompetencyLinks(competencies: CourseCompetency[]) { + this.competencyLinks = competencies.map((competency) => { // Remove unnecessary properties competency.course = undefined; competency.userProgress = undefined; - return competency; + return new CompetencyLearningObjectLink(competency, 1); }); - this.checkboxStates = this.competencies.reduce( - (states, competency) => { - if (competency.id) { - states[competency.id] = !!this.selectedCompetencies?.find((value) => value.id === competency.id); + this.checkboxStates = this.competencyLinks.reduce( + (states, competencyLink) => { + if (competencyLink.competency?.id) { + states[competencyLink.competency.id] = !!this.selectedCompetencyLinks?.find((value) => value.competency?.id === competencyLink.competency?.id); } return states; }, @@ -102,33 +102,33 @@ export class CompetencySelectionComponent implements OnInit, ControlValueAccesso ); } - toggleCompetency(newValue: Competency) { - if (newValue.id) { - if (this.checkboxStates[newValue.id]) { - this.selectedCompetencies = this.selectedCompetencies?.filter((value) => value.id !== newValue.id); + toggleCompetency(newValue: CompetencyLearningObjectLink) { + if (newValue.competency?.id) { + if (this.checkboxStates[newValue.competency.id]) { + this.selectedCompetencyLinks = this.selectedCompetencyLinks?.filter((value) => value.competency?.id !== newValue.competency?.id); } else { - this.selectedCompetencies = [...(this.selectedCompetencies ?? []), newValue]; + this.selectedCompetencyLinks = [...(this.selectedCompetencyLinks ?? []), newValue]; } - this.checkboxStates[newValue.id] = !this.checkboxStates[newValue.id]; + this.checkboxStates[newValue.competency.id] = !this.checkboxStates[newValue.competency.id]; // make sure to do not send an empty list to server - if (!this.selectedCompetencies?.length) { - this.selectedCompetencies = undefined; + if (!this.selectedCompetencyLinks?.length) { + this.selectedCompetencyLinks = undefined; } - this._onChange(this.selectedCompetencies); - this.valueChange.emit(this.selectedCompetencies); + this._onChange(this.selectedCompetencyLinks); + this.valueChange.emit(this.selectedCompetencyLinks); } } - writeValue(value?: Competency[]): void { - if (value && this.competencies) { + writeValue(value?: CompetencyLearningObjectLink[]): void { + if (value && this.competencyLinks) { // Compare the ids of the competencies instead of the whole objects - const ids = value.map((el) => el.id); - this.selectedCompetencies = this.competencies.filter((competency) => ids.includes(competency.id)); + const ids = value.map((el) => el.competency?.id); + this.selectedCompetencyLinks = this.competencyLinks.filter((competencyLink) => ids.includes(competencyLink.competency?.id)); } else { - this.selectedCompetencies = value ?? []; + this.selectedCompetencyLinks = value ?? []; } } diff --git a/src/test/java/de/tum/cit/aet/artemis/atlas/competency/AbstractCompetencyPrerequisiteIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/atlas/competency/AbstractCompetencyPrerequisiteIntegrationTest.java index a9ed4ea6736a..c0e2a307b3a8 100644 --- a/src/test/java/de/tum/cit/aet/artemis/atlas/competency/AbstractCompetencyPrerequisiteIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/atlas/competency/AbstractCompetencyPrerequisiteIntegrationTest.java @@ -18,6 +18,8 @@ import org.springframework.http.HttpStatus; import de.tum.cit.aet.artemis.atlas.AbstractAtlasIntegrationTest; +import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyExerciseLink; +import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyLectureUnitLink; import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyRelation; import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyTaxonomy; import de.tum.cit.aet.artemis.atlas.domain.competency.CourseCompetency; @@ -95,13 +97,13 @@ void creatingLectureUnitsOfLecture(CourseCompetency competency) { // creating lecture units for lecture one TextUnit textUnit = new TextUnit(); textUnit.setName("TextUnitOfLectureOne"); - textUnit.setCompetencies(Set.of(competency)); + textUnit.setCompetencyLinks(Set.of(new CompetencyLectureUnitLink(competency, textUnit, 1))); textUnit = textUnitRepository.save(textUnit); textUnitOfLectureOne = textUnit; AttachmentUnit attachmentUnit = lectureUtilService.createAttachmentUnit(true); attachmentUnit.setName("AttachmentUnitOfLectureOne"); - attachmentUnit.setCompetencies(Set.of(competency)); + attachmentUnit.setCompetencyLinks(Set.of(new CompetencyLectureUnitLink(competency, attachmentUnit, 1))); attachmentUnit = attachmentUnitRepository.save(attachmentUnit); attachmentUnitOfLectureOne = attachmentUnit; @@ -143,7 +145,8 @@ TextExercise createTextExercise(ZonedDateTime releaseDate, ZonedDateTime dueDate textExercise.setMaxPoints(10.0); textExercise.setBonusPoints(0.0); - textExercise.setCompetencies(competencies); + Set exerciseLinks = competencies.stream().map(competency -> new CompetencyExerciseLink(competency, textExercise, 1)).collect(Collectors.toSet()); + textExercise.setCompetencyLinks(exerciseLinks); return exerciseRepository.save(textExercise); } @@ -151,7 +154,7 @@ TextExercise createTextExercise(ZonedDateTime releaseDate, ZonedDateTime dueDate private ProgrammingExercise createProgrammingExercise(ZonedDateTime releaseDate, ZonedDateTime dueDate) { ProgrammingExercise programmingExercise = ProgrammingExerciseFactory.generateProgrammingExercise(releaseDate, dueDate, course, ProgrammingLanguage.JAVA); programmingExercise.setBuildConfig(programmingExerciseBuildConfigRepository.save(programmingExercise.getBuildConfig())); - programmingExercise.setCompetencies(Set.of(courseCompetency)); + programmingExercise.setCompetencyLinks(Set.of(new CompetencyExerciseLink(courseCompetency, programmingExercise, 1))); return exerciseRepository.save(programmingExercise); } @@ -181,7 +184,7 @@ void testShouldOnlySendUserSpecificData(String TEST_PREFIX) throws Exception { assertThat(response.getUserProgress()).hasSize(1); // only student2 has completed the textUnit - assertThat(response.getLectureUnits().stream().findFirst().get().getCompletedUsers()).isEmpty(); + assertThat(response.getLectureUnitLinks().stream().map(CompetencyLectureUnitLink::getLectureUnit).findFirst().get().getCompletedUsers()).isEmpty(); } // Test @@ -208,13 +211,13 @@ void shouldReturnCompetenciesForCourse(CourseCompetency newCompetency) throws Ex newCompetency.setTitle("Title"); newCompetency.setDescription("Description"); newCompetency.setCourse(course); - newCompetency.setLectureUnits(new HashSet<>(List.of(unreleasedLectureUnit))); + newCompetency.setLectureUnitLinks(new HashSet<>(List.of(new CompetencyLectureUnitLink(newCompetency, unreleasedLectureUnit, 1)))); courseCompetencyRepository.save(newCompetency); List competenciesOfCourse = getAllCall(course.getId(), HttpStatus.OK); assertThat(competenciesOfCourse).anyMatch(c -> c.getId().equals(courseCompetency.getId())); - assertThat(competenciesOfCourse.stream().filter(l -> l.getId().equals(newCompetency.getId())).findFirst().orElseThrow().getLectureUnits()).isEmpty(); + assertThat(competenciesOfCourse.stream().filter(l -> l.getId().equals(newCompetency.getId())).findFirst().orElseThrow().getLectureUnitLinks()).isEmpty(); } // Test @@ -263,14 +266,14 @@ void deleteLectureShouldUpdateCompetency() throws Exception { request.delete("/api/lectures/" + lecture.getId(), HttpStatus.OK); CourseCompetency result = getCall(course.getId(), courseCompetency.getId(), HttpStatus.OK); - assertThat(result.getLectureUnits()).isEmpty(); + assertThat(result.getLectureUnitLinks()).isEmpty(); } // Test void deleteLectureUnitShouldUpdateCompetency() throws Exception { request.delete("/api/lectures/" + lecture.getId() + "/lecture-units/" + textUnitOfLectureOne.getId(), HttpStatus.OK); CourseCompetency result = getCall(course.getId(), courseCompetency.getId(), HttpStatus.OK); - assertThat(result.getLectureUnits()).map(LectureUnit::getId).containsExactly(attachmentUnitOfLectureOne.getId()); + assertThat(result.getLectureUnitLinks()).map(CompetencyLectureUnitLink::getLectureUnit).map(LectureUnit::getId).containsExactly(attachmentUnitOfLectureOne.getId()); } abstract CourseCompetency updateCall(long courseId, CourseCompetency competency, HttpStatus expectedStatus) throws Exception; @@ -286,7 +289,8 @@ void shouldUpdateCompetency() throws Exception { assertThat(result.getTitle()).isEqualTo("Updated"); assertThat(result.getDescription()).isEqualTo("Updated Description"); - assertThat(result.getLectureUnits().stream().map(DomainObject::getId).collect(Collectors.toSet())).doesNotContain(textLectureUnit.getId()); + assertThat(result.getLectureUnitLinks().stream().map(CompetencyLectureUnitLink::getLectureUnit).map(DomainObject::getId).collect(Collectors.toSet())) + .doesNotContain(textLectureUnit.getId()); } // Test @@ -306,7 +310,7 @@ void shouldUpdateCompetencyToOptionalWhenSettingOptional(CourseCompetency newCom TextExercise exercise = TextExerciseFactory.generateTextExercise(ZonedDateTime.now(), ZonedDateTime.now(), ZonedDateTime.now(), course); exercise.setMaxPoints(1.0); exercise.setIncludedInOverallScore(includedInOverallScore); - exercise.setCompetencies(Set.of(newCompetency)); + exercise.setCompetencyLinks(Set.of(new CompetencyExerciseLink(newCompetency, exercise, 1))); exerciseRepository.save(exercise); newCompetency.setOptional(true); @@ -326,7 +330,8 @@ void shouldCreateValidCompetency(CourseCompetency newCompetency) throws Exceptio newCompetency.setMasteryThreshold(42); List allLectureUnits = lectureUnitRepository.findAll(); Set connectedLectureUnits = new HashSet<>(allLectureUnits); - newCompetency.setLectureUnits(connectedLectureUnits); + Set lectureUnitLinks = connectedLectureUnits.stream().map(lu -> new CompetencyLectureUnitLink(newCompetency, lu, 1)).collect(Collectors.toSet()); + newCompetency.setLectureUnitLinks(lectureUnitLinks); CourseCompetency result = createCall(course.getId(), newCompetency, HttpStatus.CREATED); @@ -370,8 +375,8 @@ void shouldImportCompetency() throws Exception { assertThat(importedCompetency.getDescription()).isEqualTo(courseCompetency.getDescription()); assertThat(importedCompetency.getMasteryThreshold()).isEqualTo(courseCompetency.getMasteryThreshold()); assertThat(importedCompetency.getTaxonomy()).isEqualTo(courseCompetency.getTaxonomy()); - assertThat(importedCompetency.getExercises()).isEmpty(); - assertThat(importedCompetency.getLectureUnits()).isEmpty(); + assertThat(importedCompetency.getExerciseLinks()).isEmpty(); + assertThat(importedCompetency.getLectureUnitLinks()).isEmpty(); assertThat(importedCompetency.getUserProgress()).isEmpty(); verify(competencyProgressService, never()).updateProgressByCompetencyAsync(importedCompetency); } @@ -393,9 +398,9 @@ void shouldImportExerciseAndLectureWithCompetency() throws Exception { // Test void shouldImportExerciseAndLectureWithCompetencyAndChangeDates() throws Exception { - teamTextExercise.setCompetencies(null); + teamTextExercise.setCompetencyLinks(null); exerciseRepository.save(teamTextExercise); - attachmentUnitOfLectureOne.setCompetencies(null); + attachmentUnitOfLectureOne.setCompetencyLinks(null); attachmentUnitRepository.save(attachmentUnitOfLectureOne); ZonedDateTime releaseDate = ZonedDateTime.of(2022, 2, 21, 23, 45, 0, 0, ZoneId.of("UTC")); @@ -529,9 +534,9 @@ void shouldImportAllExerciseAndLectureWithCompetency() throws Exception { // Test void shouldImportAllExerciseAndLectureWithCompetencyAndChangeDates() throws Exception { - teamTextExercise.setCompetencies(null); + teamTextExercise.setCompetencyLinks(null); exerciseRepository.save(teamTextExercise); - attachmentUnitOfLectureOne.setCompetencies(null); + attachmentUnitOfLectureOne.setCompetencyLinks(null); attachmentUnitRepository.save(attachmentUnitOfLectureOne); ZonedDateTime releaseDate = ZonedDateTime.of(2022, 2, 21, 23, 45, 0, 0, ZoneId.of("UTC")); @@ -661,9 +666,9 @@ void shouldImportCompetenciesExerciseAndLectureWithCompetency() throws Exception // Test void shouldImportCompetenciesExerciseAndLectureWithCompetencyAndChangeDates() throws Exception { - teamTextExercise.setCompetencies(null); + teamTextExercise.setCompetencyLinks(null); exerciseRepository.save(teamTextExercise); - attachmentUnitOfLectureOne.setCompetencies(null); + attachmentUnitOfLectureOne.setCompetencyLinks(null); attachmentUnitRepository.save(attachmentUnitOfLectureOne); ZonedDateTime releaseDate = ZonedDateTime.of(2022, 2, 21, 23, 45, 0, 0, ZoneId.of("UTC")); diff --git a/src/test/java/de/tum/cit/aet/artemis/atlas/competency/CourseCompetencyIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/atlas/competency/CourseCompetencyIntegrationTest.java index df2c561bda7e..924f05a14a56 100644 --- a/src/test/java/de/tum/cit/aet/artemis/atlas/competency/CourseCompetencyIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/atlas/competency/CourseCompetencyIntegrationTest.java @@ -7,6 +7,7 @@ import java.time.ZonedDateTime; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; @@ -19,6 +20,7 @@ import de.tum.cit.aet.artemis.assessment.domain.Result; import de.tum.cit.aet.artemis.atlas.domain.competency.Competency; +import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyExerciseLink; import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyProgress; import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyRelation; import de.tum.cit.aet.artemis.atlas.domain.competency.CourseCompetency; @@ -94,7 +96,9 @@ private ProgrammingExercise createProgrammingExercise(int i, Set competencyExerciseLinks = competencies.stream().map(competency -> new CompetencyExerciseLink(competency, programmingExercise, 1)) + .collect(Collectors.toSet()); + programmingExercise.setCompetencyLinks(competencyExerciseLinks); programmingExercise.setDifficulty(i == 1 ? DifficultyLevel.EASY : i == 2 ? DifficultyLevel.MEDIUM : DifficultyLevel.HARD); programmingExerciseBuildConfigRepository.save(programmingExercise.getBuildConfig()); return programmingExerciseRepository.save(programmingExercise); diff --git a/src/test/java/de/tum/cit/aet/artemis/atlas/competency/util/CompetencyUtilService.java b/src/test/java/de/tum/cit/aet/artemis/atlas/competency/util/CompetencyUtilService.java index bca2072b19d0..c35f2b94593b 100644 --- a/src/test/java/de/tum/cit/aet/artemis/atlas/competency/util/CompetencyUtilService.java +++ b/src/test/java/de/tum/cit/aet/artemis/atlas/competency/util/CompetencyUtilService.java @@ -8,7 +8,9 @@ import org.springframework.stereotype.Service; import de.tum.cit.aet.artemis.atlas.domain.competency.Competency; +import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyExerciseLink; import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyJol; +import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyLectureUnitLink; import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyRelation; import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyTaxonomy; import de.tum.cit.aet.artemis.atlas.domain.competency.RelationType; @@ -72,7 +74,9 @@ public Competency createCompetencyWithExercise(Course course, Exercise exercise) Competency competency = new Competency(); competency.setTitle("ExampleCompetency"); competency.setCourse(course); - competency.addExercise(exercise); + var link = new CompetencyExerciseLink(); + link.setExercise(exercise); + competency.getExerciseLinks().add(link); return competencyRepo.save(competency); } @@ -136,7 +140,9 @@ public Competency[] createCompetencies(Course course, int numberOfCompetencies) * @param lectureUnit The LectureUnit to update */ public LectureUnit linkLectureUnitToCompetency(Competency competency, LectureUnit lectureUnit) { - lectureUnit.getCompetencies().add(competency); + CompetencyLectureUnitLink link = new CompetencyLectureUnitLink(competency, lectureUnit, 1); + lectureUnit.getCompetencyLinks().add(link); + competency.getLectureUnitLinks().add(link); return lectureUnitRepository.save(lectureUnit); } @@ -148,7 +154,9 @@ public LectureUnit linkLectureUnitToCompetency(Competency competency, LectureUni * @return The updated Exercise */ public Exercise linkExerciseToCompetency(Competency competency, Exercise exercise) { - exercise.getCompetencies().add(competency); + CompetencyExerciseLink link = new CompetencyExerciseLink(competency, exercise, 1); + exercise.getCompetencyLinks().add(link); + competency.getExerciseLinks().add(link); return exerciseRepository.save(exercise); } diff --git a/src/test/java/de/tum/cit/aet/artemis/atlas/learningpath/LearningPathIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/atlas/learningpath/LearningPathIntegrationTest.java index b217564b469f..070bae9411e4 100644 --- a/src/test/java/de/tum/cit/aet/artemis/atlas/learningpath/LearningPathIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/atlas/learningpath/LearningPathIntegrationTest.java @@ -27,6 +27,7 @@ import de.tum.cit.aet.artemis.atlas.AbstractAtlasIntegrationTest; import de.tum.cit.aet.artemis.atlas.domain.LearningObject; import de.tum.cit.aet.artemis.atlas.domain.competency.Competency; +import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyLectureUnitLink; import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyRelation; import de.tum.cit.aet.artemis.atlas.domain.competency.LearningPath; import de.tum.cit.aet.artemis.atlas.domain.competency.RelationType; @@ -144,7 +145,7 @@ private Competency createCompetencyRESTCall() throws Exception { final var competencyToCreate = new Competency(); competencyToCreate.setTitle("CompetencyToCreateTitle"); competencyToCreate.setCourse(course); - competencyToCreate.setLectureUnits(Set.of(textUnit)); + competencyToCreate.setLectureUnitLinks(Set.of(new CompetencyLectureUnitLink(competencyToCreate, textUnit, 1))); competencyToCreate.setMasteryThreshold(42); return request.postWithResponseBody("/api/courses/" + course.getId() + "/competencies", competencyToCreate, Competency.class, HttpStatus.CREATED); } @@ -662,7 +663,7 @@ void testGetLearningPathNavigationEmptyCompetencies() throws Exception { final var student = userTestRepository.findOneByLogin(STUDENT1_OF_COURSE).orElseThrow(); final var learningPath = learningPathRepository.findByCourseIdAndUserIdElseThrow(course.getId(), student.getId()); - textExercise.setCompetencies(Set.of()); + textExercise.setCompetencyLinks(Set.of()); textExercise = exerciseRepository.save(textExercise); TextUnit secondTextUnit = createAndLinkTextUnit(student, competencies[2], false); @@ -687,7 +688,7 @@ void testGetLearningPathNavigationDoesNotLeakUnreleasedLearningObjects() throws final var student = userTestRepository.findOneByLogin(STUDENT1_OF_COURSE).orElseThrow(); final var learningPath = learningPathRepository.findByCourseIdAndUserIdElseThrow(course.getId(), student.getId()); - textExercise.setCompetencies(Set.of()); + textExercise.setCompetencyLinks(Set.of()); textExercise = exerciseRepository.save(textExercise); TextUnit secondTextUnit = createAndLinkTextUnit(student, competencies[1], false); diff --git a/src/test/java/de/tum/cit/aet/artemis/atlas/service/LearningObjectServiceTest.java b/src/test/java/de/tum/cit/aet/artemis/atlas/service/LearningObjectServiceTest.java index d6a8f34228a2..aeceb3c6cd9c 100644 --- a/src/test/java/de/tum/cit/aet/artemis/atlas/service/LearningObjectServiceTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/atlas/service/LearningObjectServiceTest.java @@ -17,7 +17,7 @@ import de.tum.cit.aet.artemis.assessment.domain.AssessmentType; import de.tum.cit.aet.artemis.assessment.util.StudentScoreUtilService; import de.tum.cit.aet.artemis.atlas.domain.LearningObject; -import de.tum.cit.aet.artemis.atlas.domain.competency.CourseCompetency; +import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyLearningObjectLink; import de.tum.cit.aet.artemis.core.domain.Course; import de.tum.cit.aet.artemis.core.domain.User; import de.tum.cit.aet.artemis.exercise.domain.participation.Participation; @@ -110,7 +110,7 @@ public Long getId() { } @Override - public Set getCompetencies() { + public Set getCompetencyLinks() { return Set.of(); } diff --git a/src/test/java/de/tum/cit/aet/artemis/core/MetricsIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/core/MetricsIntegrationTest.java index 4aefa856aef2..0a85177005eb 100644 --- a/src/test/java/de/tum/cit/aet/artemis/core/MetricsIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/core/MetricsIntegrationTest.java @@ -25,6 +25,8 @@ import de.tum.cit.aet.artemis.assessment.service.ParticipantScoreScheduleService; import de.tum.cit.aet.artemis.assessment.util.StudentScoreUtilService; import de.tum.cit.aet.artemis.atlas.competency.util.CompetencyUtilService; +import de.tum.cit.aet.artemis.atlas.domain.competency.Competency; +import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyLectureUnitLink; import de.tum.cit.aet.artemis.atlas.dto.metrics.CompetencyInformationDTO; import de.tum.cit.aet.artemis.atlas.dto.metrics.LectureUnitInformationDTO; import de.tum.cit.aet.artemis.atlas.dto.metrics.ResourceTimestampDTO; @@ -309,7 +311,9 @@ class LectureMetrics { void shouldReturnLectureUnitInformation() throws Exception { final var lectureUnit = lectureUtilService.createTextUnit(); - lectureUnitService.linkLectureUnitsToCompetency(competencyUtilService.createCompetency(course), Set.of(lectureUnit), Set.of()); + Competency competency = competencyUtilService.createCompetency(course); + lectureUnitService.linkLectureUnitsToCompetency(competencyUtilService.createCompetency(course), Set.of(new CompetencyLectureUnitLink(competency, lectureUnit, 1)), + Set.of()); final var testLecture = lectureUtilService.createLecture(course, null); lectureUtilService.addLectureUnitsToLecture(testLecture, List.of(lectureUnit)); diff --git a/src/test/java/de/tum/cit/aet/artemis/fileupload/FileUploadExerciseIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/fileupload/FileUploadExerciseIntegrationTest.java index d1deb2f81694..d68555e2e5da 100644 --- a/src/test/java/de/tum/cit/aet/artemis/fileupload/FileUploadExerciseIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/fileupload/FileUploadExerciseIntegrationTest.java @@ -1,7 +1,6 @@ package de.tum.cit.aet.artemis.fileupload; import static de.tum.cit.aet.artemis.core.util.TestResourceUtils.HalfSecond; -import static java.time.ZonedDateTime.now; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; @@ -34,6 +33,7 @@ import de.tum.cit.aet.artemis.assessment.domain.Result; import de.tum.cit.aet.artemis.assessment.util.GradingCriterionUtil; import de.tum.cit.aet.artemis.atlas.domain.competency.Competency; +import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyExerciseLink; import de.tum.cit.aet.artemis.communication.domain.conversation.Channel; import de.tum.cit.aet.artemis.core.domain.Course; import de.tum.cit.aet.artemis.core.dto.CourseForDashboardDTO; @@ -328,7 +328,7 @@ void testDeleteFileUploadExerciseWithChannel() throws Exception { @Test @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") void testDeleteFileUploadExerciseWithCompetency() throws Exception { - fileUploadExercise.setCompetencies(Set.of(competency)); + fileUploadExercise.setCompetencyLinks(Set.of(new CompetencyExerciseLink(competency, fileUploadExercise, 1))); fileUploadExercise = fileUploadExerciseRepository.save(fileUploadExercise); request.delete("/api/file-upload-exercises/" + fileUploadExercise.getId(), HttpStatus.OK); @@ -381,7 +381,7 @@ void updateFileUploadExercise_asInstructor() throws Exception { final ZonedDateTime dueDate = ZonedDateTime.now().plusDays(10); fileUploadExercise.setDueDate(dueDate); fileUploadExercise.setAssessmentDueDate(ZonedDateTime.now().plusDays(11)); - fileUploadExercise.setCompetencies(Set.of(competency)); + fileUploadExercise.setCompetencyLinks(Set.of(new CompetencyExerciseLink(competency, fileUploadExercise, 1))); FileUploadExercise receivedFileUploadExercise = request.putWithResponseBody("/api/file-upload-exercises/" + fileUploadExercise.getId() + "?notificationText=notification", fileUploadExercise, FileUploadExercise.class, HttpStatus.OK); @@ -681,7 +681,7 @@ void testImportFileUploadExerciseFromCourseToCourseAsEditorSuccess() throws Exce expectedFileUploadExercise.setCourse(course2); String uniqueChannelName = "test" + UUID.randomUUID().toString().substring(0, 8); expectedFileUploadExercise.setChannelName(uniqueChannelName); - expectedFileUploadExercise.setCompetencies(Set.of(competency)); + fileUploadExercise.setCompetencyLinks(Set.of(new CompetencyExerciseLink(competency, fileUploadExercise, 1))); var sourceExerciseId = expectedFileUploadExercise.getId(); var importedFileUploadExercise = request.postWithResponseBody("/api/file-upload-exercises/import/" + sourceExerciseId, expectedFileUploadExercise, FileUploadExercise.class, diff --git a/src/test/java/de/tum/cit/aet/artemis/lecture/AttachmentUnitIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/lecture/AttachmentUnitIntegrationTest.java index 8bd6f3f6edd4..9806b26b8737 100644 --- a/src/test/java/de/tum/cit/aet/artemis/lecture/AttachmentUnitIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/lecture/AttachmentUnitIntegrationTest.java @@ -37,6 +37,7 @@ import de.tum.cit.aet.artemis.atlas.competency.util.CompetencyUtilService; import de.tum.cit.aet.artemis.atlas.domain.competency.Competency; +import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyLectureUnitLink; import de.tum.cit.aet.artemis.core.security.SecurityUtils; import de.tum.cit.aet.artemis.lecture.domain.Attachment; import de.tum.cit.aet.artemis.lecture.domain.AttachmentUnit; @@ -219,7 +220,7 @@ void updateLectureAttachmentUnitWithSameFileName() throws Exception { @Test @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") void createAttachmentUnit_asInstructor_shouldCreateAttachmentUnit() throws Exception { - attachmentUnit.setCompetencies(Set.of(competency)); + attachmentUnit.setCompetencyLinks(Set.of(new CompetencyLectureUnitLink(competency, attachmentUnit, 1))); var result = request.performMvcRequest(buildCreateAttachmentUnit(attachmentUnit, attachment)).andExpect(status().isCreated()).andReturn(); var persistedAttachmentUnit = mapper.readValue(result.getResponse().getContentAsString(), AttachmentUnit.class); assertThat(persistedAttachmentUnit.getId()).isNotNull(); @@ -256,7 +257,7 @@ void createAttachmentUnit_withAttachmentId_shouldReturnBadRequest() throws Excep @Test @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") void updateAttachmentUnit_asInstructor_shouldUpdateAttachmentUnit() throws Exception { - attachmentUnit.setCompetencies(Set.of(competency)); + attachmentUnit.setCompetencyLinks(Set.of(new CompetencyLectureUnitLink(competency, attachmentUnit, 1))); var createResult = request.performMvcRequest(buildCreateAttachmentUnit(attachmentUnit, attachment)).andExpect(status().isCreated()).andReturn(); var attachmentUnit = mapper.readValue(createResult.getResponse().getContentAsString(), AttachmentUnit.class); var attachment = attachmentUnit.getAttachment(); @@ -345,7 +346,7 @@ void getAttachmentUnit_correctId_shouldReturnAttachmentUnit() throws Exception { @Test @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") void deleteAttachmentUnit_withAttachment_shouldDeleteAttachment() throws Exception { - attachmentUnit.setCompetencies(Set.of(competency)); + attachmentUnit.setCompetencyLinks(Set.of(new CompetencyLectureUnitLink(competency, attachmentUnit, 1))); var result = request.performMvcRequest(buildCreateAttachmentUnit(attachmentUnit, attachment)).andExpect(status().isCreated()).andReturn(); var persistedAttachmentUnit = mapper.readValue(result.getResponse().getContentAsString(), AttachmentUnit.class); assertThat(persistedAttachmentUnit.getId()).isNotNull(); diff --git a/src/test/java/de/tum/cit/aet/artemis/lecture/LectureIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/lecture/LectureIntegrationTest.java index 0ea000015653..75587b96f77d 100644 --- a/src/test/java/de/tum/cit/aet/artemis/lecture/LectureIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/lecture/LectureIntegrationTest.java @@ -23,6 +23,7 @@ import de.tum.cit.aet.artemis.atlas.competency.util.CompetencyUtilService; import de.tum.cit.aet.artemis.atlas.domain.competency.Competency; +import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyLectureUnitLink; import de.tum.cit.aet.artemis.communication.domain.conversation.Channel; import de.tum.cit.aet.artemis.communication.repository.conversation.ChannelRepository; import de.tum.cit.aet.artemis.communication.util.ConversationUtilService; @@ -357,7 +358,7 @@ void getLecture_AttachmentNotReleased_shouldGetLectureWithoutAttachmentUnitAndAt @Test @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") void deleteLecture_lectureExists_shouldDeleteLecture() throws Exception { - attachmentUnit.setCompetencies(Set.of(competency)); + attachmentUnit.setCompetencyLinks(Set.of(new CompetencyLectureUnitLink(competency, attachmentUnit, 1))); lectureUnitRepository.save(attachmentUnit); request.delete("/api/lectures/" + lecture1.getId(), HttpStatus.OK); diff --git a/src/test/java/de/tum/cit/aet/artemis/lecture/LectureUnitIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/lecture/LectureUnitIntegrationTest.java index de053a49ec99..6adc7f57e40a 100644 --- a/src/test/java/de/tum/cit/aet/artemis/lecture/LectureUnitIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/lecture/LectureUnitIntegrationTest.java @@ -17,6 +17,7 @@ import org.springframework.security.test.context.support.WithMockUser; import de.tum.cit.aet.artemis.atlas.competency.util.CompetencyUtilService; +import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyLectureUnitLink; import de.tum.cit.aet.artemis.core.domain.Course; import de.tum.cit.aet.artemis.core.domain.DomainObject; import de.tum.cit.aet.artemis.lecture.domain.AttachmentUnit; @@ -116,11 +117,11 @@ void deleteLectureUnit() throws Exception { void deleteLectureUnit_shouldUnlinkCompetency() throws Exception { var lectureUnit = lecture1.getLectureUnits().getFirst(); var competency = competencyUtilService.createCompetency(lecture1.getCourse()); - lectureUnit.setCompetencies(Set.of(competency)); + lectureUnit.setCompetencyLinks(Set.of(new CompetencyLectureUnitLink(competency, lectureUnit, 1))); lectureRepository.save(lecture1); var lecture = lectureRepository.findByIdWithLectureUnitsAndCompetenciesElseThrow(lecture1.getId()); - assertThat(lecture.getLectureUnits().getFirst().getCompetencies()).isNotEmpty(); + assertThat(lecture.getLectureUnits().getFirst().getCompetencyLinks()).isNotEmpty(); request.delete("/api/lectures/" + lecture1.getId() + "/lecture-units/" + lectureUnit.getId(), HttpStatus.OK); this.lecture1 = lectureRepository.findByIdWithLectureUnitsAndAttachmentsElseThrow(lecture1.getId()); diff --git a/src/test/java/de/tum/cit/aet/artemis/lecture/OnlineUnitIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/lecture/OnlineUnitIntegrationTest.java index ac8fab7c6e38..bcc97f02037a 100644 --- a/src/test/java/de/tum/cit/aet/artemis/lecture/OnlineUnitIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/lecture/OnlineUnitIntegrationTest.java @@ -30,6 +30,7 @@ import de.tum.cit.aet.artemis.atlas.competency.util.CompetencyUtilService; import de.tum.cit.aet.artemis.atlas.domain.competency.Competency; +import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyLectureUnitLink; import de.tum.cit.aet.artemis.core.dto.OnlineResourceDTO; import de.tum.cit.aet.artemis.lecture.domain.Lecture; import de.tum.cit.aet.artemis.lecture.domain.LectureUnit; @@ -108,7 +109,7 @@ void testAll_asStudent() throws Exception { @Test @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") void createOnlineUnit_asInstructor_shouldCreateOnlineUnit() throws Exception { - onlineUnit.setCompetencies(Set.of(competency)); + onlineUnit.setCompetencyLinks(Set.of(new CompetencyLectureUnitLink(competency, onlineUnit, 1))); onlineUnit.setSource("https://www.youtube.com/embed/8iU8LPEa4o0"); var persistedOnlineUnit = request.postWithResponseBody("/api/lectures/" + this.lecture1.getId() + "/online-units", onlineUnit, OnlineUnit.class, HttpStatus.CREATED); assertThat(persistedOnlineUnit.getId()).isNotNull(); @@ -139,7 +140,7 @@ void createOnlineUnit_withId_shouldReturnBadRequest() throws Exception { @Test @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") void updateOnlineUnit_asInstructor_shouldUpdateOnlineUnit() throws Exception { - onlineUnit.setCompetencies(Set.of(competency)); + onlineUnit.setCompetencyLinks(Set.of(new CompetencyLectureUnitLink(competency, onlineUnit, 1))); persistOnlineUnitWithLecture(); this.onlineUnit = (OnlineUnit) lectureRepository.findByIdWithLectureUnitsAndAttachmentsElseThrow(lecture1.getId()).getLectureUnits().stream().findFirst().orElseThrow(); @@ -257,7 +258,7 @@ void getOnlineResource_malformedUrl(String link) throws Exception { @Test @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") void deleteOnlineUnit_correctId_shouldDeleteOnlineUnit() throws Exception { - onlineUnit.setCompetencies(Set.of(competency)); + onlineUnit.setCompetencyLinks(Set.of(new CompetencyLectureUnitLink(competency, onlineUnit, 1))); persistOnlineUnitWithLecture(); this.onlineUnit = (OnlineUnit) lectureRepository.findByIdWithLectureUnitsAndAttachmentsElseThrow(lecture1.getId()).getLectureUnits().stream().findFirst().orElseThrow(); diff --git a/src/test/java/de/tum/cit/aet/artemis/lecture/TextUnitIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/lecture/TextUnitIntegrationTest.java index 0c03d3616562..b906b3a00e94 100644 --- a/src/test/java/de/tum/cit/aet/artemis/lecture/TextUnitIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/lecture/TextUnitIntegrationTest.java @@ -17,6 +17,7 @@ import de.tum.cit.aet.artemis.atlas.competency.util.CompetencyUtilService; import de.tum.cit.aet.artemis.atlas.domain.competency.Competency; +import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyLectureUnitLink; import de.tum.cit.aet.artemis.lecture.domain.Lecture; import de.tum.cit.aet.artemis.lecture.domain.LectureUnit; import de.tum.cit.aet.artemis.lecture.domain.TextUnit; @@ -85,7 +86,7 @@ void testAll_asStudent() throws Exception { @Test @WithMockUser(username = TEST_PREFIX + "editor1", roles = "EDITOR") void createTextUnit_asEditor_shouldCreateTextUnitUnit() throws Exception { - textUnit.setCompetencies(Set.of(competency)); + textUnit.setCompetencyLinks(Set.of(new CompetencyLectureUnitLink(competency, textUnit, 1))); var persistedTextUnit = request.postWithResponseBody("/api/lectures/" + this.lecture.getId() + "/text-units", textUnit, TextUnit.class, HttpStatus.CREATED); assertThat(persistedTextUnit.getId()).isNotNull(); assertThat(persistedTextUnit.getName()).isEqualTo("LoremIpsum"); @@ -106,7 +107,7 @@ void createTextUnit_EditorNotInCourse_shouldReturnForbidden() throws Exception { @Test @WithMockUser(username = TEST_PREFIX + "editor1", roles = "EDITOR") void updateTextUnit_asEditor_shouldUpdateTextUnit() throws Exception { - textUnit.setCompetencies(Set.of(competency)); + textUnit.setCompetencyLinks(Set.of(new CompetencyLectureUnitLink(competency, textUnit, 1))); persistTextUnitWithLecture(); TextUnit textUnitFromRequest = request.get("/api/lectures/" + lecture.getId() + "/text-units/" + this.textUnit.getId(), HttpStatus.OK, TextUnit.class); textUnitFromRequest.setContent("Changed"); @@ -156,7 +157,7 @@ void getTextUnit_correctId_shouldReturnTextUnit() throws Exception { @Test @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") void deleteTextUnit_correctId_shouldDeleteTextUnit() throws Exception { - textUnit.setCompetencies(Set.of(competency)); + textUnit.setCompetencyLinks(Set.of(new CompetencyLectureUnitLink(competency, textUnit, 1))); persistTextUnitWithLecture(); assertThat(this.textUnit.getId()).isNotNull(); request.delete("/api/lectures/" + lecture.getId() + "/lecture-units/" + this.textUnit.getId(), HttpStatus.OK); diff --git a/src/test/java/de/tum/cit/aet/artemis/lecture/VideoUnitIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/lecture/VideoUnitIntegrationTest.java index 51d622d187bc..02313677828d 100644 --- a/src/test/java/de/tum/cit/aet/artemis/lecture/VideoUnitIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/lecture/VideoUnitIntegrationTest.java @@ -17,6 +17,7 @@ import de.tum.cit.aet.artemis.atlas.competency.util.CompetencyUtilService; import de.tum.cit.aet.artemis.atlas.domain.competency.Competency; +import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyLectureUnitLink; import de.tum.cit.aet.artemis.lecture.domain.Lecture; import de.tum.cit.aet.artemis.lecture.domain.LectureUnit; import de.tum.cit.aet.artemis.lecture.domain.VideoUnit; @@ -86,7 +87,7 @@ void testAll_asStudent() throws Exception { @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") void createVideoUnit_asInstructor_shouldCreateVideoUnit() throws Exception { videoUnit.setSource("https://www.youtube.com/embed/8iU8LPEa4o0"); - videoUnit.setCompetencies(Set.of(competency)); + videoUnit.setCompetencyLinks(Set.of(new CompetencyLectureUnitLink(competency, videoUnit, 1))); var persistedVideoUnit = request.postWithResponseBody("/api/lectures/" + this.lecture1.getId() + "/video-units", videoUnit, VideoUnit.class, HttpStatus.CREATED); assertThat(persistedVideoUnit.getId()).isNotNull(); verify(competencyProgressService).updateProgressByLearningObjectAsync(eq(persistedVideoUnit)); @@ -116,7 +117,7 @@ void createVideoUnit_InstructorNotInCourse_shouldReturnForbidden() throws Except @Test @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") void updateVideoUnit_asInstructor_shouldUpdateVideoUnit() throws Exception { - videoUnit.setCompetencies(Set.of(competency)); + videoUnit.setCompetencyLinks(Set.of(new CompetencyLectureUnitLink(competency, videoUnit, 1))); persistVideoUnitWithLecture(); this.videoUnit = (VideoUnit) lectureRepository.findByIdWithLectureUnitsAndAttachments(lecture1.getId()).orElseThrow().getLectureUnits().stream().findFirst().orElseThrow(); @@ -197,7 +198,7 @@ void getVideoUnit_correctId_shouldReturnVideoUnit() throws Exception { @Test @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") void deleteVideoUnit_correctId_shouldDeleteVideoUnit() throws Exception { - videoUnit.setCompetencies(Set.of(competency)); + videoUnit.setCompetencyLinks(Set.of(new CompetencyLectureUnitLink(competency, videoUnit, 1))); persistVideoUnitWithLecture(); this.videoUnit = (VideoUnit) lectureRepository.findByIdWithLectureUnitsAndAttachments(lecture1.getId()).orElseThrow().getLectureUnits().stream().findFirst().orElseThrow(); diff --git a/src/test/java/de/tum/cit/aet/artemis/lecture/util/LectureUtilService.java b/src/test/java/de/tum/cit/aet/artemis/lecture/util/LectureUtilService.java index 3646ecaa7214..ccbd304b6099 100644 --- a/src/test/java/de/tum/cit/aet/artemis/lecture/util/LectureUtilService.java +++ b/src/test/java/de/tum/cit/aet/artemis/lecture/util/LectureUtilService.java @@ -15,6 +15,7 @@ import org.springframework.stereotype.Service; import org.springframework.util.ResourceUtils; +import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyLectureUnitLink; import de.tum.cit.aet.artemis.atlas.domain.competency.CourseCompetency; import de.tum.cit.aet.artemis.communication.domain.conversation.Channel; import de.tum.cit.aet.artemis.communication.test_repository.ConversationTestRepository; @@ -173,7 +174,10 @@ public List createCoursesWithExercisesAndLecturesAndLectureUnits(String public Lecture addCompetencyToLectureUnits(Lecture lecture, Set competencies) { Lecture l = lectureRepo.findByIdWithLectureUnitsAndCompetenciesElseThrow(lecture.getId()); l.getLectureUnits().forEach(lectureUnit -> { - lectureUnit.setCompetencies(competencies); + competencies.forEach(competency -> { + CompetencyLectureUnitLink link = new CompetencyLectureUnitLink(competency, lectureUnit, 1); + lectureUnit.getCompetencyLinks().add(link); + }); lectureUnitRepository.save(lectureUnit); }); return l; diff --git a/src/test/java/de/tum/cit/aet/artemis/modeling/ModelingExerciseIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/modeling/ModelingExerciseIntegrationTest.java index ed0fd1106333..c1beaadf0278 100644 --- a/src/test/java/de/tum/cit/aet/artemis/modeling/ModelingExerciseIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/modeling/ModelingExerciseIntegrationTest.java @@ -42,6 +42,7 @@ import de.tum.cit.aet.artemis.assessment.util.GradingCriterionUtil; import de.tum.cit.aet.artemis.atlas.competency.util.CompetencyUtilService; import de.tum.cit.aet.artemis.atlas.domain.competency.Competency; +import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyExerciseLink; import de.tum.cit.aet.artemis.communication.domain.conversation.Channel; import de.tum.cit.aet.artemis.communication.repository.conversation.ChannelRepository; import de.tum.cit.aet.artemis.core.domain.Course; @@ -226,7 +227,7 @@ void testUpdateModelingExercise_asInstructor() throws Exception { ModelingExercise modelingExercise = ModelingExerciseFactory.createModelingExercise(classExercise.getCourseViaExerciseGroupOrCourseMember().getId()); courseUtilService.enableMessagingForCourse(modelingExercise.getCourseViaExerciseGroupOrCourseMember()); modelingExercise.setChannelName("testchannel-" + UUID.randomUUID().toString().substring(0, 8)); - modelingExercise.setCompetencies(Set.of(competency)); + modelingExercise.setCompetencyLinks(Set.of(new CompetencyExerciseLink(competency, modelingExercise, 1))); ModelingExercise createdModelingExercise = request.postWithResponseBody("/api/modeling-exercises", modelingExercise, ModelingExercise.class, HttpStatus.CREATED); gradingCriteria = exerciseUtilService.addGradingInstructionsToExercise(modelingExercise); @@ -407,7 +408,7 @@ void testDeleteModelingExercise_asInstructor() throws Exception { @Test @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") void testDeleteModelingExerciseWithCompetency() throws Exception { - classExercise.setCompetencies(Set.of(competency)); + classExercise.setCompetencyLinks(Set.of(new CompetencyExerciseLink(competency, classExercise, 1))); modelingExerciseRepository.save(classExercise); request.delete("/api/modeling-exercises/" + classExercise.getId(), HttpStatus.OK); @@ -473,7 +474,7 @@ void importModelingExerciseFromCourseToCourse() throws Exception { modelingExerciseToImport.setCourse(course2); String uniqueChannelName = "channel-" + UUID.randomUUID().toString().substring(0, 8); modelingExerciseToImport.setChannelName(uniqueChannelName); - modelingExerciseToImport.setCompetencies(Set.of(competency)); + modelingExerciseToImport.setCompetencyLinks(Set.of(new CompetencyExerciseLink(competency, modelingExerciseToImport, 1))); var importedExercise = request.postWithResponseBody("/api/modeling-exercises/import/" + modelingExerciseToImport.getId(), modelingExerciseToImport, ModelingExercise.class, HttpStatus.CREATED); diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/icl/ProgrammingExerciseLocalVCLocalCIIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/icl/ProgrammingExerciseLocalVCLocalCIIntegrationTest.java index f5ec144c6bf0..ed98eded276c 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/icl/ProgrammingExerciseLocalVCLocalCIIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/icl/ProgrammingExerciseLocalVCLocalCIIntegrationTest.java @@ -34,6 +34,7 @@ import de.tum.cit.aet.artemis.atlas.competency.util.CompetencyUtilService; import de.tum.cit.aet.artemis.atlas.domain.competency.Competency; +import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyExerciseLink; import de.tum.cit.aet.artemis.core.connector.AeolusRequestMockProvider; import de.tum.cit.aet.artemis.core.domain.Course; import de.tum.cit.aet.artemis.exercise.participation.util.ParticipationUtilService; @@ -166,7 +167,7 @@ void testCreateProgrammingExercise() throws Exception { ProgrammingExercise newExercise = ProgrammingExerciseFactory.generateProgrammingExercise(ZonedDateTime.now().minusDays(1), ZonedDateTime.now().plusDays(7), course); newExercise.setProjectType(ProjectType.PLAIN_GRADLE); - newExercise.setCompetencies(Set.of(competency)); + newExercise.setCompetencyLinks(Set.of(new CompetencyExerciseLink(competency, newExercise, 1))); // Mock dockerClient.copyArchiveFromContainerCmd() such that it returns a dummy commitHash for both the assignment and the test repository. // Note: The stub needs to receive the same object twice because there are two requests to the same method (one for the template participation and one for the solution @@ -218,7 +219,7 @@ void testCreateProgrammingExercise_Invalid_CheckoutPaths() throws Exception { @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") void testUpdateProgrammingExercise() throws Exception { programmingExercise.setReleaseDate(ZonedDateTime.now().plusHours(1)); - programmingExercise.setCompetencies(Set.of(competency)); + programmingExercise.setCompetencyLinks(Set.of(new CompetencyExerciseLink(competency, programmingExercise, 1))); ProgrammingExercise updatedExercise = request.putWithResponseBody("/api/programming-exercises", programmingExercise, ProgrammingExercise.class, HttpStatus.OK); @@ -240,7 +241,7 @@ void testUpdateProgrammingExercise_templateRepositoryUriIsInvalid() throws Excep @Test @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") void testDeleteProgrammingExercise() throws Exception { - programmingExercise.setCompetencies(Set.of(competency)); + programmingExercise.setCompetencyLinks(Set.of(new CompetencyExerciseLink(competency, programmingExercise, 1))); programmingExerciseRepository.save(programmingExercise); // Delete the exercise @@ -291,7 +292,7 @@ void testImportProgrammingExercise() throws Exception { var params = new LinkedMultiValueMap(); params.add("recreateBuildPlans", "true"); exerciseToBeImported.setChannelName("testchannel-pe-imported"); - exerciseToBeImported.setCompetencies(Set.of(competency)); + exerciseToBeImported.setCompetencyLinks(Set.of(new CompetencyExerciseLink(competency, exerciseToBeImported, 1))); var importedExercise = request.postWithResponseBody("/api/programming-exercises/import/" + programmingExercise.getId(), exerciseToBeImported, ProgrammingExercise.class, params, HttpStatus.OK); diff --git a/src/test/java/de/tum/cit/aet/artemis/text/TextExerciseIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/text/TextExerciseIntegrationTest.java index cbcd54bb78f3..e0edf1621d57 100644 --- a/src/test/java/de/tum/cit/aet/artemis/text/TextExerciseIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/text/TextExerciseIntegrationTest.java @@ -46,6 +46,7 @@ import de.tum.cit.aet.artemis.assessment.util.GradingCriterionUtil; import de.tum.cit.aet.artemis.atlas.competency.util.CompetencyUtilService; import de.tum.cit.aet.artemis.atlas.domain.competency.Competency; +import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyExerciseLink; import de.tum.cit.aet.artemis.communication.domain.conversation.Channel; import de.tum.cit.aet.artemis.communication.repository.conversation.ChannelRepository; import de.tum.cit.aet.artemis.core.domain.Course; @@ -194,7 +195,7 @@ void testDeleteTextExerciseWithChannel() throws Exception { @Test @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") void testDeleteTextExerciseWithCompetency() throws Exception { - textExercise.setCompetencies(Set.of(competency)); + textExercise.setCompetencyLinks(Set.of(new CompetencyExerciseLink(competency, textExercise, 1))); textExerciseRepository.save(textExercise); request.delete("/api/text-exercises/" + textExercise.getId(), HttpStatus.OK); @@ -418,7 +419,7 @@ void updateTextExercise() throws Exception { exampleSubmission.setExercise(textExercise); exampleSubmissionRepo.save(exampleSubmission); textExercise.addExampleSubmission(exampleSubmission); - textExercise.setCompetencies(Set.of(competency)); + textExercise.setCompetencyLinks(Set.of(new CompetencyExerciseLink(competency, textExercise, 1))); TextExercise updatedTextExercise = request.putWithResponseBody("/api/text-exercises", textExercise, TextExercise.class, HttpStatus.OK); @@ -588,7 +589,7 @@ void importTextExerciseFromCourseToCourse() throws Exception { textExerciseRepository.save(textExercise); textExercise.setCourse(course2); textExercise.setChannelName("testchannel" + textExercise.getId()); - textExercise.setCompetencies(Set.of(competency)); + textExercise.setCompetencyLinks(Set.of(new CompetencyExerciseLink(competency, textExercise, 1))); var newTextExercise = request.postWithResponseBody("/api/text-exercises/import/" + textExercise.getId(), textExercise, TextExercise.class, HttpStatus.CREATED); Channel channel = channelRepository.findChannelByExerciseId(newTextExercise.getId()); assertThat(channel).isNotNull(); diff --git a/src/test/javascript/spec/component/competencies/competency-form.component.spec.ts b/src/test/javascript/spec/component/competencies/competency-form.component.spec.ts index 40ef3c893792..1d97e4b9a671 100644 --- a/src/test/javascript/spec/component/competencies/competency-form.component.spec.ts +++ b/src/test/javascript/spec/component/competencies/competency-form.component.spec.ts @@ -3,7 +3,7 @@ import { ComponentFixture, TestBed, discardPeriodicTasks, fakeAsync, flush, tick import { ReactiveFormsModule } from '@angular/forms'; import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'; import { CompetencyService } from 'app/course/competencies/competency.service'; -import { Competency, CompetencyTaxonomy } from 'app/entities/competency.model'; +import { Competency, CompetencyLectureUnitLink, CompetencyTaxonomy } from 'app/entities/competency.model'; import { TextUnit } from 'app/entities/lecture-unit/textUnit.model'; import { Lecture } from 'app/entities/lecture.model'; import { LectureUnitService } from 'app/lecture/lecture-unit/lecture-unit-management/lectureUnit.service'; @@ -113,20 +113,20 @@ describe('CompetencyFormComponent', () => { title: 'test', description: 'lorem ipsum', softDueDate: dayjs(), - connectedLectureUnits: [textUnit], + lectureUnitLinks: [new CompetencyLectureUnitLink(undefined, textUnit, 1)], taxonomy: CompetencyTaxonomy.ANALYZE, optional: true, }; competencyFormComponentFixture.detectChanges(); competencyFormComponent.formData = formData; - competencyFormComponent['onLectureUnitSelectionChange']([textUnit]); + competencyFormComponent['onLectureUnitSelectionChange']([new CompetencyLectureUnitLink(undefined, textUnit, 1)]); competencyFormComponent.ngOnChanges(); expect(competencyFormComponent.titleControl?.value).toEqual(formData.title); expect(competencyFormComponent.descriptionControl?.value).toEqual(formData.description); expect(competencyFormComponent.softDueDateControl?.value).toEqual(formData.softDueDate); expect(competencyFormComponent.optionalControl?.value).toEqual(formData.optional); - expect(competencyFormComponent.selectedLectureUnitsInTable).toEqual(formData.connectedLectureUnits); + expect(competencyFormComponent.selectedLectureUnitLinksInTable).toEqual(formData.lectureUnitLinks); }); it('should suggest taxonomy when title changes', () => { diff --git a/src/test/javascript/spec/component/competencies/competency-management/competency-management.component.spec.ts b/src/test/javascript/spec/component/competencies/competency-management/competency-management.component.spec.ts index b0305c56b76d..0bd377db613a 100644 --- a/src/test/javascript/spec/component/competencies/competency-management/competency-management.component.spec.ts +++ b/src/test/javascript/spec/component/competencies/competency-management/competency-management.component.spec.ts @@ -2,7 +2,14 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; import { MockComponent, MockDirective, MockPipe, MockProvider } from 'ng-mocks'; import { of } from 'rxjs'; -import { Competency, CompetencyRelation, CompetencyWithTailRelationDTO, CourseCompetencyProgress, CourseCompetencyType } from 'app/entities/competency.model'; +import { + Competency, + CompetencyLectureUnitLink, + CompetencyRelation, + CompetencyWithTailRelationDTO, + CourseCompetencyProgress, + CourseCompetencyType, +} from 'app/entities/competency.model'; import { CompetencyManagementComponent } from 'app/course/competencies/competency-management/competency-management.component'; import { ActivatedRoute, provideRouter } from '@angular/router'; import { DeleteButtonDirective } from 'app/shared/delete-dialog/delete-button.directive'; @@ -88,7 +95,7 @@ describe('CompetencyManagementComponent', () => { const textUnit = new TextUnit(); competency.id = 1; competency.description = 'test'; - competency.lectureUnits = [textUnit]; + competency.lectureUnitLinks = [new CompetencyLectureUnitLink(competency, textUnit, 1)]; const courseCompetencyProgress = new CourseCompetencyProgress(); courseCompetencyProgress.competencyId = 1; courseCompetencyProgress.numberOfStudents = 8; diff --git a/src/test/javascript/spec/component/competencies/competency-popover.component.spec.ts b/src/test/javascript/spec/component/competencies/competency-popover.component.spec.ts index 551d03446489..8834a670b530 100644 --- a/src/test/javascript/spec/component/competencies/competency-popover.component.spec.ts +++ b/src/test/javascript/spec/component/competencies/competency-popover.component.spec.ts @@ -57,7 +57,7 @@ describe('CompetencyPopoverComponent', () => { it('should navigate to course competencies', fakeAsync(() => { const location: Location = TestBed.inject(Location); competencyPopoverComponent.navigateTo = 'courseCompetencies'; - competencyPopoverComponent.competencies = [{}]; + competencyPopoverComponent.competencyLinks = [{ weight: 1 }]; competencyPopoverComponent.courseId = 1; competencyPopoverComponentFixture.detectChanges(); const popoverButton = competencyPopoverComponentFixture.debugElement.nativeElement.querySelector('button'); @@ -72,7 +72,7 @@ describe('CompetencyPopoverComponent', () => { it('should navigate to competency management', fakeAsync(() => { const location: Location = TestBed.inject(Location); competencyPopoverComponent.navigateTo = 'competencyManagement'; - competencyPopoverComponent.competencies = [{}]; + competencyPopoverComponent.competencyLinks = [{ weight: 1 }]; competencyPopoverComponent.courseId = 1; competencyPopoverComponentFixture.detectChanges(); const popoverButton = competencyPopoverComponentFixture.debugElement.nativeElement.querySelector('button'); diff --git a/src/test/javascript/spec/component/competencies/competency.service.spec.ts b/src/test/javascript/spec/component/competencies/competency.service.spec.ts index 0b88bd1810ff..542291b3b448 100644 --- a/src/test/javascript/spec/component/competencies/competency.service.spec.ts +++ b/src/test/javascript/spec/component/competencies/competency.service.spec.ts @@ -8,7 +8,9 @@ import { LectureUnit } from 'app/entities/lecture-unit/lectureUnit.model'; import { CompetencyService } from 'app/course/competencies/competency.service'; import { Competency, + CompetencyExerciseLink, CompetencyImportResponseDTO, + CompetencyLectureUnitLink, CompetencyProgress, CompetencyRelation, CompetencyRelationType, @@ -364,10 +366,10 @@ describe('CompetencyService', () => { const accountService = TestBed.inject(AccountService); const convertDateSpy = jest.spyOn(dateUtils, 'convertDateFromServer'); - const convertLectureUnitsSpy = jest.spyOn(lectureUnitService, 'convertLectureUnitArrayDatesFromServer'); + const convertLectureUnitSpy = jest.spyOn(lectureUnitService, 'convertLectureUnitDateFromServer'); const setAccessRightsCourseSpy = jest.spyOn(accountService, 'setAccessRightsForCourse'); const setAccessRightsExerciseSpy = jest.spyOn(accountService, 'setAccessRightsForExercise'); - const convertExercisesSpy = jest.spyOn(ExerciseService, 'convertExercisesDateFromServer'); + const convertExerciseSpy = jest.spyOn(ExerciseService, 'convertExerciseDatesFromServer'); const parseCategoriesSpy = jest.spyOn(ExerciseService, 'parseExerciseCategories'); const exercise: Exercise = { @@ -378,27 +380,24 @@ describe('CompetencyService', () => { const competencyFromServer: Competency = { softDueDate: dayjs('2022-02-20') as Dayjs, course: { id: 1 }, - lectureUnits: [{ id: 1 }, { id: 2 }], - exercises: [ - { id: 3, ...exercise }, - { id: 4, ...exercise }, - ], + lectureUnitLinks: [new CompetencyLectureUnitLink(this, { id: 1 }, 1), new CompetencyLectureUnitLink(this, { id: 2 }, 1)], + exerciseLinks: [new CompetencyExerciseLink(this, { id: 3, ...exercise }, 1), new CompetencyExerciseLink(this, { id: 4, ...exercise }, 1)], }; competencyService['convertCompetencyResponseFromServer']({} as HttpResponse); expect(convertDateSpy).not.toHaveBeenCalled(); - expect(convertLectureUnitsSpy).not.toHaveBeenCalled(); + expect(convertLectureUnitSpy).not.toHaveBeenCalled(); expect(setAccessRightsCourseSpy).not.toHaveBeenCalled(); - expect(convertExercisesSpy).not.toHaveBeenCalled(); + expect(convertExerciseSpy).not.toHaveBeenCalled(); expect(parseCategoriesSpy).not.toHaveBeenCalled(); expect(setAccessRightsExerciseSpy).not.toHaveBeenCalled(); competencyService['convertCompetencyResponseFromServer']({ body: competencyFromServer } as HttpResponse); expect(convertDateSpy).toHaveBeenCalled(); - expect(convertLectureUnitsSpy).toHaveBeenCalledOnce(); + expect(convertLectureUnitSpy).toHaveBeenCalledTimes(2); expect(setAccessRightsCourseSpy).toHaveBeenCalledOnce(); - expect(convertExercisesSpy).toHaveBeenCalledOnce(); + expect(convertExerciseSpy).toHaveBeenCalledTimes(2); expect(parseCategoriesSpy).toHaveBeenCalledTimes(2); expect(setAccessRightsExerciseSpy).toHaveBeenCalledTimes(2); }); diff --git a/src/test/javascript/spec/component/competencies/course-competencies.component.spec.ts b/src/test/javascript/spec/component/competencies/course-competencies.component.spec.ts index 5f9f93ad370b..60abc07b50d7 100644 --- a/src/test/javascript/spec/component/competencies/course-competencies.component.spec.ts +++ b/src/test/javascript/spec/component/competencies/course-competencies.component.spec.ts @@ -3,7 +3,7 @@ import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; import { MockPipe, MockProvider } from 'ng-mocks'; import { CompetencyService } from 'app/course/competencies/competency.service'; import { of } from 'rxjs'; -import { Competency, CompetencyProgress, CourseCompetencyType } from 'app/entities/competency.model'; +import { Competency, CompetencyLectureUnitLink, CompetencyProgress, CourseCompetencyType } from 'app/entities/competency.model'; import { ActivatedRoute } from '@angular/router'; import { AlertService } from 'app/core/util/alert.service'; import { CourseCompetenciesComponent } from 'app/overview/course-competencies/course-competencies.component'; @@ -99,7 +99,7 @@ describe('CourseCompetencies', () => { const textUnit = new TextUnit(); competency.id = 1; competency.description = 'test'; - competency.lectureUnits = [textUnit]; + competency.lectureUnitLinks = [new CompetencyLectureUnitLink(competency, textUnit, 1)]; competency.userProgress = [{ progress: 70, confidence: 45 } as CompetencyProgress]; const getAllCourseCompetenciesForCourseSpy = jest.spyOn(courseCompetencyService, 'getAllForCourse').mockReturnValue( diff --git a/src/test/javascript/spec/component/competencies/create-competency.component.spec.ts b/src/test/javascript/spec/component/competencies/create-competency.component.spec.ts index 23b97f5fc7e3..6a981ca5f361 100644 --- a/src/test/javascript/spec/component/competencies/create-competency.component.spec.ts +++ b/src/test/javascript/spec/component/competencies/create-competency.component.spec.ts @@ -10,7 +10,7 @@ import { LectureService } from 'app/lecture/lecture.service'; import { MockRouter } from '../../helpers/mocks/mock-router'; import { TextUnit } from 'app/entities/lecture-unit/textUnit.model'; import { HttpResponse } from '@angular/common/http'; -import { Competency } from 'app/entities/competency.model'; +import { Competency, CompetencyLectureUnitLink } from 'app/entities/competency.model'; import { By } from '@angular/platform-browser'; import { DocumentationButtonComponent } from 'app/shared/components/documentation-button/documentation-button.component'; import { Lecture } from 'app/entities/lecture.model'; @@ -112,7 +112,7 @@ describe('CreateCompetency', () => { title: 'Test', description: 'Lorem Ipsum', optional: true, - connectedLectureUnits: [textUnit], + lectureUnitLinks: [new CompetencyLectureUnitLink(undefined, textUnit, 1)], }; const response: HttpResponse = new HttpResponse({ @@ -133,7 +133,7 @@ describe('CreateCompetency', () => { competency.title = formData.title; competency.description = formData.description; competency.optional = formData.optional; - competency.lectureUnits = formData.connectedLectureUnits; + competency.lectureUnitLinks = formData.lectureUnitLinks; expect(createSpy).toHaveBeenCalledWith(competency, 1); expect(createSpy).toHaveBeenCalledOnce(); diff --git a/src/test/javascript/spec/component/competencies/create-prerequisite.component.spec.ts b/src/test/javascript/spec/component/competencies/create-prerequisite.component.spec.ts index f2794e9f48c9..c73b6c8e9a23 100644 --- a/src/test/javascript/spec/component/competencies/create-prerequisite.component.spec.ts +++ b/src/test/javascript/spec/component/competencies/create-prerequisite.component.spec.ts @@ -20,6 +20,7 @@ import { CreatePrerequisiteComponent } from 'app/course/competencies/create/crea import { PrerequisiteService } from 'app/course/competencies/prerequisite.service'; import { PrerequisiteFormComponent } from 'app/course/competencies/forms/prerequisite/prerequisite-form.component'; import { Prerequisite } from 'app/entities/prerequisite.model'; +import { CompetencyLectureUnitLink } from '../../../../../main/webapp/app/entities/competency.model'; describe('CreatePrerequisite', () => { let createPrerequisiteComponentFixture: ComponentFixture; @@ -117,7 +118,7 @@ describe('CreatePrerequisite', () => { title: 'Test', description: 'Lorem Ipsum', optional: true, - connectedLectureUnits: [textUnit], + lectureUnitLinks: [new CompetencyLectureUnitLink(undefined, textUnit, 1)], }; const response: HttpResponse = new HttpResponse({ @@ -138,7 +139,7 @@ describe('CreatePrerequisite', () => { competency.title = formData.title; competency.description = formData.description; competency.optional = formData.optional; - competency.lectureUnits = formData.connectedLectureUnits; + competency.lectureUnitLinks = formData.lectureUnitLinks; expect(createSpy).toHaveBeenCalledWith(competency, 1); expect(createSpy).toHaveBeenCalledOnce(); diff --git a/src/test/javascript/spec/component/competencies/edit-competency.component.spec.ts b/src/test/javascript/spec/component/competencies/edit-competency.component.spec.ts index 88b261712f1f..e40c039a8ba4 100644 --- a/src/test/javascript/spec/component/competencies/edit-competency.component.spec.ts +++ b/src/test/javascript/spec/component/competencies/edit-competency.component.spec.ts @@ -9,7 +9,7 @@ import { Lecture } from 'app/entities/lecture.model'; import { EditCompetencyComponent } from 'app/course/competencies/edit/edit-competency.component'; import { CompetencyService } from 'app/course/competencies/competency.service'; import { LectureService } from 'app/lecture/lecture.service'; -import { Competency, CourseCompetencyProgress } from 'app/entities/competency.model'; +import { Competency, CompetencyLectureUnitLink, CourseCompetencyProgress } from 'app/entities/competency.model'; import { TextUnit } from 'app/entities/lecture-unit/textUnit.model'; import { MockRouter } from '../../helpers/mocks/mock-router'; import { CompetencyFormStubComponent } from './competency-form-stub.component'; @@ -89,7 +89,7 @@ describe('EditCompetencyComponent', () => { competencyOfResponse.title = 'test'; competencyOfResponse.description = 'lorem ipsum'; competencyOfResponse.optional = true; - competencyOfResponse.lectureUnits = [lectureUnit]; + competencyOfResponse.lectureUnitLinks = [new CompetencyLectureUnitLink(competencyOfResponse, lectureUnit, 1)]; const competencyResponse: HttpResponse = new HttpResponse({ body: competencyOfResponse, @@ -125,7 +125,7 @@ describe('EditCompetencyComponent', () => { expect(editCompetencyComponent.formData.title).toEqual(competencyOfResponse.title); expect(editCompetencyComponent.formData.description).toEqual(competencyOfResponse.description); expect(editCompetencyComponent.formData.optional).toEqual(competencyOfResponse.optional); - expect(editCompetencyComponent.formData.connectedLectureUnits).toEqual(competencyOfResponse.lectureUnits); + expect(editCompetencyComponent.formData.lectureUnitLinks).toEqual(competencyOfResponse.lectureUnitLinks); expect(editCompetencyComponent.lecturesWithLectureUnits).toEqual([lectureOfResponse]); expect(competencyFormComponent.formData).toEqual(editCompetencyComponent.formData); }); @@ -143,7 +143,7 @@ describe('EditCompetencyComponent', () => { competencyDatabase.title = 'test'; competencyDatabase.description = 'lorem ipsum'; competencyDatabase.optional = true; - competencyDatabase.lectureUnits = [textUnit]; + competencyDatabase.lectureUnitLinks = [new CompetencyLectureUnitLink(competencyDatabase, textUnit, 1)]; const findByIdResponse: HttpResponse = new HttpResponse({ body: competencyDatabase, @@ -188,7 +188,7 @@ describe('EditCompetencyComponent', () => { title: changedUnit.title, description: changedUnit.description, optional: changedUnit.optional, - connectedLectureUnits: changedUnit.lectureUnits, + lectureUnitLinks: changedUnit.lectureUnitLinks, }); expect(updatedSpy).toHaveBeenCalledOnce(); diff --git a/src/test/javascript/spec/component/competencies/edit-prerequisite.component.spec.ts b/src/test/javascript/spec/component/competencies/edit-prerequisite.component.spec.ts index 7e1009b2d1e8..c3436bf0685f 100644 --- a/src/test/javascript/spec/component/competencies/edit-prerequisite.component.spec.ts +++ b/src/test/javascript/spec/component/competencies/edit-prerequisite.component.spec.ts @@ -7,7 +7,7 @@ import { HttpResponse } from '@angular/common/http'; import { By } from '@angular/platform-browser'; import { Lecture } from 'app/entities/lecture.model'; import { LectureService } from 'app/lecture/lecture.service'; -import { CourseCompetencyProgress } from 'app/entities/competency.model'; +import { CompetencyLectureUnitLink, CourseCompetencyProgress } from 'app/entities/competency.model'; import { TextUnit } from 'app/entities/lecture-unit/textUnit.model'; import { MockRouter } from '../../helpers/mocks/mock-router'; import { ArtemisTestModule } from '../../test.module'; @@ -90,7 +90,7 @@ describe('EditPrerequisiteComponent', () => { competencyOfResponse.title = 'test'; competencyOfResponse.description = 'lorem ipsum'; competencyOfResponse.optional = true; - competencyOfResponse.lectureUnits = [lectureUnit]; + competencyOfResponse.lectureUnitLinks = [new CompetencyLectureUnitLink(competencyOfResponse, lectureUnit, 1)]; const competencyResponse: HttpResponse = new HttpResponse({ body: competencyOfResponse, @@ -126,7 +126,7 @@ describe('EditPrerequisiteComponent', () => { expect(editPrerequisiteComponent.formData.title).toEqual(competencyOfResponse.title); expect(editPrerequisiteComponent.formData.description).toEqual(competencyOfResponse.description); expect(editPrerequisiteComponent.formData.optional).toEqual(competencyOfResponse.optional); - expect(editPrerequisiteComponent.formData.connectedLectureUnits).toEqual(competencyOfResponse.lectureUnits); + expect(editPrerequisiteComponent.formData.lectureUnitLinks).toEqual(competencyOfResponse.lectureUnitLinks); expect(editPrerequisiteComponent.lecturesWithLectureUnits).toEqual([lectureOfResponse]); expect(competencyFormComponent.formData).toEqual(editPrerequisiteComponent.formData); }); @@ -144,7 +144,7 @@ describe('EditPrerequisiteComponent', () => { competencyDatabase.title = 'test'; competencyDatabase.description = 'lorem ipsum'; competencyDatabase.optional = true; - competencyDatabase.lectureUnits = [textUnit]; + competencyDatabase.lectureUnitLinks = [new CompetencyLectureUnitLink(competencyDatabase, textUnit, 1)]; const findByIdResponse: HttpResponse = new HttpResponse({ body: competencyDatabase, @@ -189,7 +189,7 @@ describe('EditPrerequisiteComponent', () => { title: changedUnit.title, description: changedUnit.description, optional: changedUnit.optional, - connectedLectureUnits: changedUnit.lectureUnits, + lectureUnitLinks: changedUnit.lectureUnitLinks, }); expect(updatedSpy).toHaveBeenCalledOnce(); diff --git a/src/test/javascript/spec/component/competencies/prerequisite-form.component.spec.ts b/src/test/javascript/spec/component/competencies/prerequisite-form.component.spec.ts index 4471d024976b..3013d118068d 100644 --- a/src/test/javascript/spec/component/competencies/prerequisite-form.component.spec.ts +++ b/src/test/javascript/spec/component/competencies/prerequisite-form.component.spec.ts @@ -2,7 +2,7 @@ import { HttpResponse } from '@angular/common/http'; import { ComponentFixture, TestBed, discardPeriodicTasks, fakeAsync, flush, tick } from '@angular/core/testing'; import { ReactiveFormsModule } from '@angular/forms'; import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'; -import { CompetencyTaxonomy } from 'app/entities/competency.model'; +import { CompetencyLectureUnitLink, CompetencyTaxonomy } from 'app/entities/competency.model'; import { TextUnit } from 'app/entities/lecture-unit/textUnit.model'; import { Lecture } from 'app/entities/lecture.model'; import { LectureUnitService } from 'app/lecture/lecture-unit/lecture-unit-management/lectureUnit.service'; @@ -114,7 +114,7 @@ describe('PrerequisiteFormComponent', () => { title: 'test', description: 'lorem ipsum', softDueDate: dayjs(), - connectedLectureUnits: [textUnit], + lectureUnitLinks: [new CompetencyLectureUnitLink(undefined, textUnit, 1)], taxonomy: CompetencyTaxonomy.ANALYZE, optional: true, }; diff --git a/src/test/javascript/spec/component/competencies/prerequisite.service.spec.ts b/src/test/javascript/spec/component/competencies/prerequisite.service.spec.ts index c3c69d7980d2..e2e8687c1bcc 100644 --- a/src/test/javascript/spec/component/competencies/prerequisite.service.spec.ts +++ b/src/test/javascript/spec/component/competencies/prerequisite.service.spec.ts @@ -7,7 +7,9 @@ import { take } from 'rxjs/operators'; import { LectureUnit } from 'app/entities/lecture-unit/lectureUnit.model'; import { Competency, + CompetencyExerciseLink, CompetencyImportResponseDTO, + CompetencyLectureUnitLink, CompetencyProgress, CompetencyRelation, CompetencyRelationType, @@ -365,10 +367,10 @@ describe('PrerequisiteService', () => { const accountService = TestBed.inject(AccountService); const convertDateSpy = jest.spyOn(dateUtils, 'convertDateFromServer'); - const convertLectureUnitsSpy = jest.spyOn(lectureUnitService, 'convertLectureUnitArrayDatesFromServer'); + const convertLectureUnitSpy = jest.spyOn(lectureUnitService, 'convertLectureUnitDateFromServer'); const setAccessRightsCourseSpy = jest.spyOn(accountService, 'setAccessRightsForCourse'); const setAccessRightsExerciseSpy = jest.spyOn(accountService, 'setAccessRightsForExercise'); - const convertExercisesSpy = jest.spyOn(ExerciseService, 'convertExercisesDateFromServer'); + const convertExerciseSpy = jest.spyOn(ExerciseService, 'convertExerciseDatesFromServer'); const parseCategoriesSpy = jest.spyOn(ExerciseService, 'parseExerciseCategories'); const exercise: Exercise = { @@ -379,16 +381,13 @@ describe('PrerequisiteService', () => { const competencyFromServer: Competency = { softDueDate: dayjs('2022-02-20') as Dayjs, course: { id: 1 }, - lectureUnits: [{ id: 1 }, { id: 2 }], - exercises: [ - { id: 3, ...exercise }, - { id: 4, ...exercise }, - ], + lectureUnitLinks: [new CompetencyLectureUnitLink(this, { id: 1 }, 1), new CompetencyLectureUnitLink(this, { id: 2 }, 1)], + exerciseLinks: [new CompetencyExerciseLink(this, { id: 3, ...exercise }, 1), new CompetencyExerciseLink(this, { id: 4, ...exercise }, 1)], }; prerequisiteService['convertCompetencyResponseFromServer']({} as HttpResponse); expect(convertDateSpy).not.toHaveBeenCalled(); - expect(convertLectureUnitsSpy).not.toHaveBeenCalled(); + expect(convertLectureUnitSpy).not.toHaveBeenCalled(); expect(setAccessRightsCourseSpy).not.toHaveBeenCalled(); expect(convertExercisesSpy).not.toHaveBeenCalled(); expect(parseCategoriesSpy).not.toHaveBeenCalled(); @@ -397,9 +396,9 @@ describe('PrerequisiteService', () => { prerequisiteService['convertCompetencyResponseFromServer']({ body: competencyFromServer } as HttpResponse); expect(convertDateSpy).toHaveBeenCalled(); - expect(convertLectureUnitsSpy).toHaveBeenCalledOnce(); + expect(convertLectureUnitSpy).toHaveBeenCalledTimes(2); expect(setAccessRightsCourseSpy).toHaveBeenCalledOnce(); - expect(convertExercisesSpy).toHaveBeenCalledOnce(); + expect(convertExerciseSpy).toHaveBeenCalledTimes(2); expect(parseCategoriesSpy).toHaveBeenCalledTimes(2); expect(setAccessRightsExerciseSpy).toHaveBeenCalledTimes(2); }); diff --git a/src/test/javascript/spec/component/lecture-unit/attachment-unit/attachment-unit-form.component.spec.ts b/src/test/javascript/spec/component/lecture-unit/attachment-unit/attachment-unit-form.component.spec.ts index 764930b97f19..4037d5764924 100644 --- a/src/test/javascript/spec/component/lecture-unit/attachment-unit/attachment-unit-form.component.spec.ts +++ b/src/test/javascript/spec/component/lecture-unit/attachment-unit/attachment-unit-form.component.spec.ts @@ -107,7 +107,7 @@ describe('AttachmentUnitFormComponent', () => { name: exampleName, description: exampleDescription, releaseDate: exampleReleaseDate, - competencies: null, + competencyLinks: null, version: exampleVersion, updateNotificationText: exampleUpdateNotificationText, }, diff --git a/src/test/javascript/spec/component/lecture-unit/online-unit/online-unit-form.component.spec.ts b/src/test/javascript/spec/component/lecture-unit/online-unit/online-unit-form.component.spec.ts index e6cc8933495b..6c36b6f44af5 100644 --- a/src/test/javascript/spec/component/lecture-unit/online-unit/online-unit-form.component.spec.ts +++ b/src/test/javascript/spec/component/lecture-unit/online-unit/online-unit-form.component.spec.ts @@ -111,7 +111,7 @@ describe('OnlineUnitFormComponent', () => { name: exampleName, description: exampleDescription, releaseDate: exampleReleaseDate, - competencies: null, + competencyLinks: null, source: 'https://www.example.com', }); diff --git a/src/test/javascript/spec/component/lecture-unit/text-unit/text-unit-form.component.spec.ts b/src/test/javascript/spec/component/lecture-unit/text-unit/text-unit-form.component.spec.ts index 32f0d6889eec..4b9fcac77a25 100644 --- a/src/test/javascript/spec/component/lecture-unit/text-unit/text-unit-form.component.spec.ts +++ b/src/test/javascript/spec/component/lecture-unit/text-unit/text-unit-form.component.spec.ts @@ -123,7 +123,7 @@ describe('TextUnitFormComponent', () => { expect(submitFormEventSpy).toHaveBeenCalledWith({ name: 'Test', releaseDate: null, - competencies: null, + competencyLinks: null, content: exampleMarkdown, }); }); diff --git a/src/test/javascript/spec/component/lecture-unit/video-unit/video-unit-form.component.spec.ts b/src/test/javascript/spec/component/lecture-unit/video-unit/video-unit-form.component.spec.ts index 5e00cff9c987..bf48893cf982 100644 --- a/src/test/javascript/spec/component/lecture-unit/video-unit/video-unit-form.component.spec.ts +++ b/src/test/javascript/spec/component/lecture-unit/video-unit/video-unit-form.component.spec.ts @@ -117,7 +117,7 @@ describe('VideoUnitFormComponent', () => { name: exampleName, description: exampleDescription, releaseDate: exampleReleaseDate, - competencies: null, + competencyLinks: null, source: validYouTubeUrlInEmbeddableFormat, urlHelper: null, }); diff --git a/src/test/javascript/spec/component/lecture/wizard-mode/lecture-wizard-units.component.spec.ts b/src/test/javascript/spec/component/lecture/wizard-mode/lecture-wizard-units.component.spec.ts index 58113a1c5b16..5c95475131dd 100644 --- a/src/test/javascript/spec/component/lecture/wizard-mode/lecture-wizard-units.component.spec.ts +++ b/src/test/javascript/spec/component/lecture/wizard-mode/lecture-wizard-units.component.spec.ts @@ -201,7 +201,7 @@ describe('LectureWizardUnitComponent', () => { expect(videoUnitCallArgument.description).toEqual(formData.description); expect(videoUnitCallArgument.releaseDate).toEqual(formData.releaseDate); expect(videoUnitCallArgument.source).toEqual(formData.source); - expect(videoUnitCallArgument.competencies).toEqual(formData.competencies); + expect(videoUnitCallArgument.competencyLinks).toEqual(formData.competencyLinks); expect(lectureIdCallArgument).toBe(1); expect(createStub).toHaveBeenCalledOnce(); @@ -279,7 +279,7 @@ describe('LectureWizardUnitComponent', () => { expect(textUnitCallArgument.name).toEqual(formData.name); expect(textUnitCallArgument.content).toEqual(formData.content); expect(textUnitCallArgument.releaseDate).toEqual(formData.releaseDate); - expect(textUnitCallArgument.competencies).toEqual(formData.competencies); + expect(textUnitCallArgument.competencyLinks).toEqual(formData.competencyLinks); expect(lectureIdCallArgument).toBe(1); expect(createStub).toHaveBeenCalledOnce(); @@ -433,7 +433,7 @@ describe('LectureWizardUnitComponent', () => { expect(onlineUnitCallArgument.description).toEqual(formDate.description); expect(onlineUnitCallArgument.releaseDate).toEqual(formDate.releaseDate); expect(onlineUnitCallArgument.source).toEqual(formDate.source); - expect(onlineUnitCallArgument.competencies).toEqual(formDate.competencies); + expect(onlineUnitCallArgument.competencyLinks).toEqual(formDate.competencyLinks); expect(lectureIdCallArgument).toBe(1); expect(createStub).toHaveBeenCalledOnce(); diff --git a/src/test/javascript/spec/component/overview/course-competencies/course-competencies-details.component.spec.ts b/src/test/javascript/spec/component/overview/course-competencies/course-competencies-details.component.spec.ts index 4fbb28e42077..25c68dadee69 100644 --- a/src/test/javascript/spec/component/overview/course-competencies/course-competencies-details.component.spec.ts +++ b/src/test/javascript/spec/component/overview/course-competencies/course-competencies-details.component.spec.ts @@ -19,7 +19,7 @@ import { FireworksComponent } from 'app/shared/fireworks/fireworks.component'; import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap'; import { MockRouter } from '../../../helpers/mocks/mock-router'; import { provideHttpClientTesting } from '@angular/common/http/testing'; -import { Competency, CompetencyProgress } from 'app/entities/competency.model'; +import { Competency, CompetencyExerciseLink, CompetencyLectureUnitLink, CompetencyProgress } from 'app/entities/competency.model'; import { TextExercise } from 'app/entities/text/text-exercise.model'; import { TextUnit } from 'app/entities/lecture-unit/textUnit.model'; import { HttpResponse, provideHttpClient } from '@angular/common/http'; @@ -109,8 +109,8 @@ describe('CourseCompetenciesDetails', () => { it('should load competency to display progress and all lecture units', () => { const competency = { id: 1, - lectureUnits: [new TextUnit()], - exercises: [{ id: 5 } as TextExercise], + lectureUnitLinks: [new CompetencyLectureUnitLink(this, new TextUnit(), 1)], + exerciseLinks: [new CompetencyExerciseLink(this, { id: 5 } as TextExercise, 1)], } as Competency; const findByIdSpy = jest.spyOn(courseCompetencyService, 'findById').mockReturnValue(of(new HttpResponse({ body: competency }))); @@ -120,7 +120,7 @@ describe('CourseCompetenciesDetails', () => { const exerciseUnit = fixture.debugElement.query(By.directive(ExerciseUnitComponent)); expect(findByIdSpy).toHaveBeenCalledOnce(); - expect(component.competency.lectureUnits).toHaveLength(2); + expect(component.competency.lectureUnitLinks).toHaveLength(2); expect(textUnit).not.toBeNull(); expect(exerciseUnit).not.toBeNull(); }); @@ -128,7 +128,7 @@ describe('CourseCompetenciesDetails', () => { it('should load competency to display progress and the exercise unit', () => { const competency = { id: 1, - exercises: [{ id: 5 } as ModelingExercise], + exerciseLinks: [new CompetencyExerciseLink(this, { id: 5 } as ModelingExercise, 1)], } as Competency; const findByIdSpy = jest.spyOn(courseCompetencyService, 'findById').mockReturnValue(of(new HttpResponse({ body: competency }))); @@ -138,7 +138,7 @@ describe('CourseCompetenciesDetails', () => { const exerciseUnit = fixture.debugElement.query(By.directive(ExerciseUnitComponent)); expect(findByIdSpy).toHaveBeenCalledOnce(); - expect(component.competency.lectureUnits).toHaveLength(1); + expect(component.competency.lectureUnitLinks).toHaveLength(1); expect(exerciseUnit).not.toBeNull(); }); diff --git a/src/test/javascript/spec/component/shared/competency-selection.component.spec.ts b/src/test/javascript/spec/component/shared/competency-selection.component.spec.ts index 92e2d53dfcfb..8e41d06a5f74 100644 --- a/src/test/javascript/spec/component/shared/competency-selection.component.spec.ts +++ b/src/test/javascript/spec/component/shared/competency-selection.component.spec.ts @@ -7,7 +7,7 @@ import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap'; import { ActivatedRoute, Router, convertToParamMap } from '@angular/router'; import { MockRouter } from '../../helpers/mocks/mock-router'; import { NgModel, ReactiveFormsModule } from '@angular/forms'; -import { Competency } from 'app/entities/competency.model'; +import { Competency, CompetencyLearningObjectLink } from 'app/entities/competency.model'; import { of, throwError } from 'rxjs'; import { HttpResponse } from '@angular/common/http'; import { By } from '@angular/platform-browser'; @@ -62,11 +62,11 @@ describe('CompetencySelection', () => { fixture.detectChanges(); const selector = fixture.debugElement.nativeElement.querySelector('#competency-selector'); - expect(component.selectedCompetencies).toBeUndefined(); + expect(component.selectedCompetencyLinks).toBeUndefined(); expect(getCourseSpy).toHaveBeenCalledOnce(); expect(getAllForCourseSpy).not.toHaveBeenCalled(); expect(component.isLoading).toBeFalse(); - expect(component.competencies).toBeArrayOfSize(2); + expect(component.competencyLinks).toBeArrayOfSize(2); expect(selector).not.toBeNull(); }); @@ -81,9 +81,9 @@ describe('CompetencySelection', () => { expect(getCourseSpy).toHaveBeenCalledOnce(); expect(getAllForCourseSpy).toHaveBeenCalledOnce(); expect(component.isLoading).toBeFalse(); - expect(component.competencies).toBeArrayOfSize(2); - expect(component.competencies?.first()?.course).toBeUndefined(); - expect(component.competencies?.first()?.userProgress).toBeUndefined(); + expect(component.competencyLinks).toBeArrayOfSize(2); + expect(component.competencyLinks?.first()?.course).toBeUndefined(); + expect(component.competencyLinks?.first()?.userProgress).toBeUndefined(); }); it('should set disabled when error during loading', () => { @@ -108,7 +108,7 @@ describe('CompetencySelection', () => { expect(getCourseSpy).toHaveBeenCalledOnce(); expect(getAllForCourseSpy).toHaveBeenCalledOnce(); expect(component.isLoading).toBeFalse(); - expect(component.competencies).toBeEmpty(); + expect(component.competencyLinks).toBeEmpty(); expect(select).toBeNull(); }); @@ -117,9 +117,9 @@ describe('CompetencySelection', () => { fixture.detectChanges(); - component.writeValue([{ id: 1, title: 'other' } as Competency]); - expect(component.selectedCompetencies).toBeArrayOfSize(1); - expect(component.selectedCompetencies?.first()?.title).toBe('test'); + component.writeValue([new CompetencyLearningObjectLink({ id: 1, title: 'other' } as Competency, 1)]); + expect(component.selectedCompetencyLinks).toBeArrayOfSize(1); + expect(component.selectedCompetencyLinks?.first()?.title).toBe('test'); }); it('should trigger change detection after loading competencies', () => { @@ -139,31 +139,31 @@ describe('CompetencySelection', () => { jest.spyOn(courseStorageService, 'getCourse').mockReturnValue({ competencies: [competency1, competency2, competency3] }); fixture.detectChanges(); - expect(component.selectedCompetencies).toBeUndefined(); + expect(component.selectedCompetencyLinks).toBeUndefined(); - component.toggleCompetency(competency1); - component.toggleCompetency(competency2); - component.toggleCompetency(competency3); + component.toggleCompetency(new CompetencyLearningObjectLink(competency1, 1)); + component.toggleCompetency(new CompetencyLearningObjectLink(competency2, 1)); + component.toggleCompetency(new CompetencyLearningObjectLink(competency3, 1)); - expect(component.selectedCompetencies).toHaveLength(3); - expect(component.selectedCompetencies).toContain(competency3); + expect(component.selectedCompetencyLinks).toHaveLength(3); + expect(component.selectedCompetencyLinks).toContain(competency3); - component.toggleCompetency(competency2); + component.toggleCompetency(new CompetencyLearningObjectLink(competency2, 1)); - expect(component.selectedCompetencies).toHaveLength(2); - expect(component.selectedCompetencies).not.toContain(competency2); + expect(component.selectedCompetencyLinks).toHaveLength(2); + expect(component.selectedCompetencyLinks).not.toContain(competency2); - component.toggleCompetency(competency1); - component.toggleCompetency(competency3); + component.toggleCompetency(new CompetencyLearningObjectLink(competency1, 1)); + component.toggleCompetency(new CompetencyLearningObjectLink(competency3, 1)); - expect(component.selectedCompetencies).toBeUndefined(); + expect(component.selectedCompetencyLinks).toBeUndefined(); }); it('should register onchange', () => { component.checkboxStates = {}; const registerSpy = jest.fn(); component.registerOnChange(registerSpy); - component.toggleCompetency({ id: 1 }); + component.toggleCompetency(new CompetencyLearningObjectLink({ id: 1 }, 1)); expect(registerSpy).toHaveBeenCalled(); }); From 73f95973c5483b26af334fc1c19364acb28a0b87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20St=C3=B6hr?= Date: Fri, 18 Oct 2024 22:56:22 +0200 Subject: [PATCH 02/25] Fix most tests --- .../competency/CompetencyExerciseLink.java | 2 +- .../CompetencyLearningObjectLink.java | 7 +- .../competency/CompetencyLectureUnitLink.java | 2 +- .../CompetencyLectureUnitLinkRepository.java | 1 + .../service/LearningObjectImportService.java | 13 ++- .../service/competency/CompetencyService.java | 6 +- .../competency/CourseCompetencyService.java | 11 ++- .../competency/PrerequisiteService.java | 6 +- .../artemis/core/config/LoggingAspect.java | 4 +- .../service/ExerciseDeletionService.java | 14 +++- .../artemis/lecture/domain/ExerciseUnit.java | 9 +-- .../lecture/service/LectureService.java | 8 +- .../lecture/service/LectureUnitService.java | 2 + .../atlas/AbstractAtlasIntegrationTest.java | 8 ++ ...CompetencyPrerequisiteIntegrationTest.java | 63 +++++++++------ .../util/CompetencyUtilService.java | 13 ++- .../prerequisite-form.component.spec.ts | 4 +- .../competencies/prerequisite.service.spec.ts | 2 +- .../lecture-wizard-units.component.spec.ts | 81 +++++++++++-------- .../competency-selection.component.spec.ts | 6 +- 20 files changed, 172 insertions(+), 90 deletions(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyExerciseLink.java b/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyExerciseLink.java index 829c966b9149..a22fdd99ab6b 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyExerciseLink.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyExerciseLink.java @@ -15,7 +15,7 @@ @Table(name = "competency_exercise") public class CompetencyExerciseLink extends CompetencyLearningObjectLink { - @ManyToOne + @ManyToOne(optional = false) @MapsId("learningObjectId") private Exercise exercise; diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLearningObjectLink.java b/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLearningObjectLink.java index 9dea9865b873..39ff69550b0c 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLearningObjectLink.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLearningObjectLink.java @@ -23,7 +23,7 @@ public abstract class CompetencyLearningObjectLink implements Serializable { @JsonIgnore protected CompetencyLearningObjectId id = new CompetencyLearningObjectId(); - @ManyToOne + @ManyToOne(optional = false) @MapsId("competencyId") protected CourseCompetency competency; @@ -113,5 +113,10 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(learningObjectId, competencyId); } + + @Override + public String toString() { + return "CompetencyLearningObjectId{" + "learningObjectId=" + learningObjectId + ", competencyId=" + competencyId + '}'; + } } } diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLectureUnitLink.java b/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLectureUnitLink.java index 8f3707a6c9d8..5280e5314da4 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLectureUnitLink.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLectureUnitLink.java @@ -15,7 +15,7 @@ @Table(name = "competency_lecture_unit") public class CompetencyLectureUnitLink extends CompetencyLearningObjectLink { - @ManyToOne + @ManyToOne(optional = false) @MapsId("learningObjectId") private LectureUnit lectureUnit; diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyLectureUnitLinkRepository.java b/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyLectureUnitLinkRepository.java index 34971f8b786e..ae7bcf04cead 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyLectureUnitLinkRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyLectureUnitLinkRepository.java @@ -4,4 +4,5 @@ import de.tum.cit.aet.artemis.core.repository.base.ArtemisJpaRepository; public interface CompetencyLectureUnitLinkRepository extends ArtemisJpaRepository { + } diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/service/LearningObjectImportService.java b/src/main/java/de/tum/cit/aet/artemis/atlas/service/LearningObjectImportService.java index 5db97a2c9c8f..d20c671193de 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/service/LearningObjectImportService.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/service/LearningObjectImportService.java @@ -33,6 +33,8 @@ import de.tum.cit.aet.artemis.atlas.domain.competency.CourseCompetency; import de.tum.cit.aet.artemis.atlas.dto.CompetencyImportOptionsDTO; import de.tum.cit.aet.artemis.atlas.dto.CompetencyWithTailRelationDTO; +import de.tum.cit.aet.artemis.atlas.repository.CompetencyExerciseLinkRepository; +import de.tum.cit.aet.artemis.atlas.repository.CompetencyLectureUnitLinkRepository; import de.tum.cit.aet.artemis.atlas.repository.CourseCompetencyRepository; import de.tum.cit.aet.artemis.core.domain.Course; import de.tum.cit.aet.artemis.core.exception.NoUniqueQueryException; @@ -107,6 +109,10 @@ public class LearningObjectImportService { private final GradingCriterionRepository gradingCriterionRepository; + private final CompetencyExerciseLinkRepository competencyExerciseLinkRepository; + + private final CompetencyLectureUnitLinkRepository competencyLectureUnitLinkRepository; + public LearningObjectImportService(ExerciseRepository exerciseRepository, ProgrammingExerciseRepository programmingExerciseRepository, ProgrammingExerciseImportService programmingExerciseImportService, FileUploadExerciseRepository fileUploadExerciseRepository, FileUploadExerciseImportService fileUploadExerciseImportService, ModelingExerciseRepository modelingExerciseRepository, @@ -114,7 +120,8 @@ public LearningObjectImportService(ExerciseRepository exerciseRepository, Progra QuizExerciseRepository quizExerciseRepository, QuizExerciseImportService quizExerciseImportService, LectureRepository lectureRepository, LectureImportService lectureImportService, LectureUnitRepository lectureUnitRepository, LectureUnitImportService lectureUnitImportService, CourseCompetencyRepository courseCompetencyRepository, ProgrammingExerciseTaskRepository programmingExerciseTaskRepository, - GradingCriterionRepository gradingCriterionRepository) { + GradingCriterionRepository gradingCriterionRepository, CompetencyExerciseLinkRepository competencyExerciseLinkRepository, + CompetencyLectureUnitLinkRepository competencyLectureUnitLinkRepository) { this.exerciseRepository = exerciseRepository; this.programmingExerciseRepository = programmingExerciseRepository; this.programmingExerciseImportService = programmingExerciseImportService; @@ -133,6 +140,8 @@ public LearningObjectImportService(ExerciseRepository exerciseRepository, Progra this.courseCompetencyRepository = courseCompetencyRepository; this.programmingExerciseTaskRepository = programmingExerciseTaskRepository; this.gradingCriterionRepository = gradingCriterionRepository; + this.competencyExerciseLinkRepository = competencyExerciseLinkRepository; + this.competencyLectureUnitLinkRepository = competencyLectureUnitLinkRepository; } /** @@ -178,6 +187,7 @@ private void importOrLoadExercises(Collection source CourseCompetency importedCompetency = idToImportedCompetency.get(sourceCourseCompetency.getId()).competency(); CompetencyExerciseLink link = new CompetencyExerciseLink(importedCompetency, importedExercise, sourceExerciseLink.getWeight()); + link = competencyExerciseLinkRepository.save(link); importedExercise.getCompetencyLinks().add(link); importedCompetency.getExerciseLinks().add(link); } @@ -341,6 +351,7 @@ private void importOrLoadLectureUnit(CompetencyLectureUnitLink sourceLectureUnit CourseCompetency importedCompetency = idToImportedCompetency.get(sourceCourseCompetency.getId()).competency(); CompetencyLectureUnitLink link = new CompetencyLectureUnitLink(importedCompetency, importedLectureUnit, sourceLectureUnitLink.getWeight()); + link = competencyLectureUnitLinkRepository.save(link); importedLectureUnit.getCompetencyLinks().add(link); importedCompetency.getLectureUnitLinks().add(link); } diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CompetencyService.java b/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CompetencyService.java index e9ef9be7a960..7037da274568 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CompetencyService.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CompetencyService.java @@ -15,6 +15,7 @@ import de.tum.cit.aet.artemis.atlas.domain.competency.CourseCompetency; import de.tum.cit.aet.artemis.atlas.dto.CompetencyImportOptionsDTO; import de.tum.cit.aet.artemis.atlas.dto.CompetencyWithTailRelationDTO; +import de.tum.cit.aet.artemis.atlas.repository.CompetencyLectureUnitLinkRepository; import de.tum.cit.aet.artemis.atlas.repository.CompetencyProgressRepository; import de.tum.cit.aet.artemis.atlas.repository.CompetencyRelationRepository; import de.tum.cit.aet.artemis.atlas.repository.CompetencyRepository; @@ -41,9 +42,10 @@ public CompetencyService(CompetencyRepository competencyRepository, Authorizatio LearningPathService learningPathService, CompetencyProgressService competencyProgressService, LectureUnitService lectureUnitService, CompetencyProgressRepository competencyProgressRepository, LectureUnitCompletionRepository lectureUnitCompletionRepository, StandardizedCompetencyRepository standardizedCompetencyRepository, CourseCompetencyRepository courseCompetencyRepository, ExerciseService exerciseService, - LearningObjectImportService learningObjectImportService) { + LearningObjectImportService learningObjectImportService, CompetencyLectureUnitLinkRepository competencyLectureUnitLinkRepository) { super(competencyProgressRepository, courseCompetencyRepository, competencyRelationRepository, competencyProgressService, exerciseService, lectureUnitService, - learningPathService, authCheckService, standardizedCompetencyRepository, lectureUnitCompletionRepository, learningObjectImportService); + learningPathService, authCheckService, standardizedCompetencyRepository, lectureUnitCompletionRepository, learningObjectImportService, + competencyLectureUnitLinkRepository); this.competencyRepository = competencyRepository; } diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CourseCompetencyService.java b/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CourseCompetencyService.java index a0dc48a85d3a..71de4cee51bd 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CourseCompetencyService.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CourseCompetencyService.java @@ -30,6 +30,7 @@ import de.tum.cit.aet.artemis.atlas.dto.CompetencyImportOptionsDTO; import de.tum.cit.aet.artemis.atlas.dto.CompetencyRelationDTO; import de.tum.cit.aet.artemis.atlas.dto.CompetencyWithTailRelationDTO; +import de.tum.cit.aet.artemis.atlas.repository.CompetencyLectureUnitLinkRepository; import de.tum.cit.aet.artemis.atlas.repository.CompetencyProgressRepository; import de.tum.cit.aet.artemis.atlas.repository.CompetencyRelationRepository; import de.tum.cit.aet.artemis.atlas.repository.CourseCompetencyRepository; @@ -81,11 +82,13 @@ public class CourseCompetencyService { private final LearningObjectImportService learningObjectImportService; + private final CompetencyLectureUnitLinkRepository competencyLectureUnitLinkRepository; + public CourseCompetencyService(CompetencyProgressRepository competencyProgressRepository, CourseCompetencyRepository courseCompetencyRepository, CompetencyRelationRepository competencyRelationRepository, CompetencyProgressService competencyProgressService, ExerciseService exerciseService, LectureUnitService lectureUnitService, LearningPathService learningPathService, AuthorizationCheckService authCheckService, StandardizedCompetencyRepository standardizedCompetencyRepository, LectureUnitCompletionRepository lectureUnitCompletionRepository, - LearningObjectImportService learningObjectImportService) { + LearningObjectImportService learningObjectImportService, CompetencyLectureUnitLinkRepository competencyLectureUnitLinkRepository) { this.competencyProgressRepository = competencyProgressRepository; this.courseCompetencyRepository = courseCompetencyRepository; this.competencyRelationRepository = competencyRelationRepository; @@ -97,6 +100,7 @@ public CourseCompetencyService(CompetencyProgressRepository competencyProgressRe this.standardizedCompetencyRepository = standardizedCompetencyRepository; this.lectureUnitCompletionRepository = lectureUnitCompletionRepository; this.learningObjectImportService = learningObjectImportService; + this.competencyLectureUnitLinkRepository = competencyLectureUnitLinkRepository; } /** @@ -287,10 +291,11 @@ public List importStandardizedCompetencies(List competen */ public C createCourseCompetency(C competencyToCreate, Course course) { competencyToCreate.setCourse(course); - competencyToCreate.getLectureUnitLinks().forEach(link -> link.setCompetency(competencyToCreate)); - + Set lectureUnitLinks = competencyToCreate.getLectureUnitLinks(); + competencyToCreate.setLectureUnitLinks(new HashSet<>()); var persistedCompetency = courseCompetencyRepository.save(competencyToCreate); + persistedCompetency.setLectureUnitLinks(lectureUnitLinks); updateLectureUnits(competencyToCreate, persistedCompetency); if (course.getLearningPathsEnabled()) { diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/PrerequisiteService.java b/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/PrerequisiteService.java index 3fc520a21378..24ffc84980b5 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/PrerequisiteService.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/PrerequisiteService.java @@ -15,6 +15,7 @@ import de.tum.cit.aet.artemis.atlas.domain.competency.Prerequisite; import de.tum.cit.aet.artemis.atlas.dto.CompetencyImportOptionsDTO; import de.tum.cit.aet.artemis.atlas.dto.CompetencyWithTailRelationDTO; +import de.tum.cit.aet.artemis.atlas.repository.CompetencyLectureUnitLinkRepository; import de.tum.cit.aet.artemis.atlas.repository.CompetencyProgressRepository; import de.tum.cit.aet.artemis.atlas.repository.CompetencyRelationRepository; import de.tum.cit.aet.artemis.atlas.repository.CourseCompetencyRepository; @@ -41,9 +42,10 @@ public PrerequisiteService(PrerequisiteRepository prerequisiteRepository, Author LearningPathService learningPathService, CompetencyProgressService competencyProgressService, LectureUnitService lectureUnitService, CompetencyProgressRepository competencyProgressRepository, LectureUnitCompletionRepository lectureUnitCompletionRepository, StandardizedCompetencyRepository standardizedCompetencyRepository, CourseCompetencyRepository courseCompetencyRepository, ExerciseService exerciseService, - LearningObjectImportService learningObjectImportService) { + LearningObjectImportService learningObjectImportService, CompetencyLectureUnitLinkRepository competencyLectureUnitLinkRepository) { super(competencyProgressRepository, courseCompetencyRepository, competencyRelationRepository, competencyProgressService, exerciseService, lectureUnitService, - learningPathService, authCheckService, standardizedCompetencyRepository, lectureUnitCompletionRepository, learningObjectImportService); + learningPathService, authCheckService, standardizedCompetencyRepository, lectureUnitCompletionRepository, learningObjectImportService, + competencyLectureUnitLinkRepository); this.prerequisiteRepository = prerequisiteRepository; } diff --git a/src/main/java/de/tum/cit/aet/artemis/core/config/LoggingAspect.java b/src/main/java/de/tum/cit/aet/artemis/core/config/LoggingAspect.java index a916500b1bcc..ebf70f1a7b21 100644 --- a/src/main/java/de/tum/cit/aet/artemis/core/config/LoggingAspect.java +++ b/src/main/java/de/tum/cit/aet/artemis/core/config/LoggingAspect.java @@ -96,8 +96,8 @@ else if (e.getMessage() != null && e.getMessage().startsWith("The username has t @Around("applicationPackagePointcut() && springBeanPointcut()") public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable { if (log.isDebugEnabled()) { - log.debug("Enter: {}.{}() with argument[s] = {}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName(), - Arrays.toString(joinPoint.getArgs())); + // log.debug("Enter: {}.{}() with argument[s] = {}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName(), + // Arrays.toString(joinPoint.getArgs())); } try { Object result = joinPoint.proceed(); diff --git a/src/main/java/de/tum/cit/aet/artemis/exercise/service/ExerciseDeletionService.java b/src/main/java/de/tum/cit/aet/artemis/exercise/service/ExerciseDeletionService.java index 70ab783682fc..205b6dc1e8e8 100644 --- a/src/main/java/de/tum/cit/aet/artemis/exercise/service/ExerciseDeletionService.java +++ b/src/main/java/de/tum/cit/aet/artemis/exercise/service/ExerciseDeletionService.java @@ -16,6 +16,7 @@ import de.tum.cit.aet.artemis.assessment.repository.TutorParticipationRepository; import de.tum.cit.aet.artemis.assessment.service.ExampleSubmissionService; import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyExerciseLink; +import de.tum.cit.aet.artemis.atlas.repository.CompetencyExerciseLinkRepository; import de.tum.cit.aet.artemis.atlas.service.competency.CompetencyProgressService; import de.tum.cit.aet.artemis.communication.domain.conversation.Channel; import de.tum.cit.aet.artemis.communication.repository.conversation.ChannelRepository; @@ -78,11 +79,14 @@ public class ExerciseDeletionService { private final CompetencyProgressService competencyProgressService; + private final CompetencyExerciseLinkRepository competencyExerciseLinkRepository; + public ExerciseDeletionService(ExerciseRepository exerciseRepository, ExerciseUnitRepository exerciseUnitRepository, ParticipationService participationService, ProgrammingExerciseService programmingExerciseService, ModelingExerciseService modelingExerciseService, QuizExerciseService quizExerciseService, TutorParticipationRepository tutorParticipationRepository, ExampleSubmissionService exampleSubmissionService, StudentExamRepository studentExamRepository, LectureUnitService lectureUnitService, PlagiarismResultRepository plagiarismResultRepository, TextExerciseService textExerciseService, - ChannelRepository channelRepository, ChannelService channelService, CompetencyProgressService competencyProgressService) { + ChannelRepository channelRepository, ChannelService channelService, CompetencyProgressService competencyProgressService, + CompetencyExerciseLinkRepository competencyExerciseLinkRepository) { this.exerciseRepository = exerciseRepository; this.participationService = participationService; this.programmingExerciseService = programmingExerciseService; @@ -98,6 +102,7 @@ public ExerciseDeletionService(ExerciseRepository exerciseRepository, ExerciseUn this.channelRepository = channelRepository; this.channelService = channelService; this.competencyProgressService = competencyProgressService; + this.competencyExerciseLinkRepository = competencyExerciseLinkRepository; } /** @@ -143,7 +148,7 @@ public void cleanup(Long exerciseId, boolean deleteRepositories) { */ public void delete(long exerciseId, boolean deleteStudentReposBuildPlans, boolean deleteBaseReposBuildPlans) { var exercise = exerciseRepository.findWithCompetenciesByIdElseThrow(exerciseId); - Set competenciyLinks = exercise.getCompetencyLinks(); + Set competencyLinks = exercise.getCompetencyLinks(); log.info("Request to delete {} with id {}", exercise.getClass().getSimpleName(), exerciseId); long start = System.nanoTime(); @@ -175,6 +180,9 @@ public void delete(long exerciseId, boolean deleteStudentReposBuildPlans, boolea // delete all participations belonging to this exercise, this will also delete submissions, results, feedback, complaints, etc. participationService.deleteAllByExercise(exercise, deleteStudentReposBuildPlans, deleteStudentReposBuildPlans, false); + // delete all competency links belonging to this exercise + competencyExerciseLinkRepository.deleteAll(exercise.getCompetencyLinks()); + // clean up the many-to-many relationship to avoid problems when deleting the entities but not the relationship table exercise = exerciseRepository.findByIdWithEagerExampleSubmissionsElseThrow(exerciseId); exercise.getExampleSubmissions().forEach(exampleSubmission -> exampleSubmissionService.deleteById(exampleSubmission.getId())); @@ -204,7 +212,7 @@ public void delete(long exerciseId, boolean deleteStudentReposBuildPlans, boolea exerciseRepository.delete(exercise); } - competenciyLinks.stream().map(CompetencyExerciseLink::getCompetency).forEach(competencyProgressService::updateProgressByCompetencyAsync); + competencyLinks.stream().map(CompetencyExerciseLink::getCompetency).forEach(competencyProgressService::updateProgressByCompetencyAsync); } /** diff --git a/src/main/java/de/tum/cit/aet/artemis/lecture/domain/ExerciseUnit.java b/src/main/java/de/tum/cit/aet/artemis/lecture/domain/ExerciseUnit.java index 0883baf19e1b..da38f23f3995 100644 --- a/src/main/java/de/tum/cit/aet/artemis/lecture/domain/ExerciseUnit.java +++ b/src/main/java/de/tum/cit/aet/artemis/lecture/domain/ExerciseUnit.java @@ -1,7 +1,6 @@ package de.tum.cit.aet.artemis.lecture.domain; import java.time.ZonedDateTime; -import java.util.HashSet; import java.util.Set; import jakarta.persistence.DiscriminatorValue; @@ -66,13 +65,12 @@ public void setReleaseDate(ZonedDateTime releaseDate) { @Override public Set getCompetencyLinks() { - // Should be accessed via associated exercise - return null; + throw new UnsupportedOperationException("Create the link in the associated exercise instead"); } @Override - public void setCompetencyLinks(Set competencies) { - // Should be set in associated exercise + public void setCompetencyLinks(Set competencyLinks) { + throw new UnsupportedOperationException("Retrieve the link in the associated exercise instead"); } /** @@ -83,7 +81,6 @@ public void setCompetencyLinks(Set competencies) { public void prePersistOrUpdate() { this.name = null; this.releaseDate = null; - this.competencyLinks = new HashSet<>(); } // IMPORTANT NOTICE: The following string has to be consistent with the one defined in LectureUnit.java diff --git a/src/main/java/de/tum/cit/aet/artemis/lecture/service/LectureService.java b/src/main/java/de/tum/cit/aet/artemis/lecture/service/LectureService.java index e310bb6c4e5b..c2fda95f726e 100644 --- a/src/main/java/de/tum/cit/aet/artemis/lecture/service/LectureService.java +++ b/src/main/java/de/tum/cit/aet/artemis/lecture/service/LectureService.java @@ -13,6 +13,7 @@ import org.springframework.data.domain.Page; import org.springframework.stereotype.Service; +import de.tum.cit.aet.artemis.atlas.repository.CompetencyLectureUnitLinkRepository; import de.tum.cit.aet.artemis.atlas.service.competency.CompetencyProgressService; import de.tum.cit.aet.artemis.communication.domain.conversation.Channel; import de.tum.cit.aet.artemis.communication.repository.conversation.ChannelRepository; @@ -47,14 +48,18 @@ public class LectureService { private final CompetencyProgressService competencyProgressService; + private final CompetencyLectureUnitLinkRepository competencyLectureUnitLinkRepository; + public LectureService(LectureRepository lectureRepository, AuthorizationCheckService authCheckService, ChannelRepository channelRepository, ChannelService channelService, - Optional pyrisWebhookService, CompetencyProgressService competencyProgressService) { + Optional pyrisWebhookService, CompetencyProgressService competencyProgressService, + CompetencyLectureUnitLinkRepository competencyLectureUnitLinkRepository) { this.lectureRepository = lectureRepository; this.authCheckService = authCheckService; this.channelRepository = channelRepository; this.channelService = channelService; this.pyrisWebhookService = pyrisWebhookService; this.competencyProgressService = competencyProgressService; + this.competencyLectureUnitLinkRepository = competencyLectureUnitLinkRepository; } /** @@ -162,6 +167,7 @@ public void delete(Lecture lecture, boolean updateCompetencyProgress) { Channel lectureChannel = channelRepository.findChannelByLectureId(lecture.getId()); channelService.deleteChannel(lectureChannel); + lectureRepository.deleteById(lecture.getId()); } diff --git a/src/main/java/de/tum/cit/aet/artemis/lecture/service/LectureUnitService.java b/src/main/java/de/tum/cit/aet/artemis/lecture/service/LectureUnitService.java index ac06d5e14cb7..ae7282ed9fd5 100644 --- a/src/main/java/de/tum/cit/aet/artemis/lecture/service/LectureUnitService.java +++ b/src/main/java/de/tum/cit/aet/artemis/lecture/service/LectureUnitService.java @@ -184,6 +184,8 @@ public void removeLectureUnit(@NotNull LectureUnit lectureUnit) { lectureRepository.save(lecture); if (!(lectureUnitToDelete instanceof ExerciseUnit)) { + // Delete the links to competencies + competencyLectureUnitLinkRepository.deleteAll(lectureUnitToDelete.getCompetencyLinks()); // update associated competency progress objects competencyProgressService.updateProgressForUpdatedLearningObjectAsync(lectureUnitToDelete, Optional.empty()); } diff --git a/src/test/java/de/tum/cit/aet/artemis/atlas/AbstractAtlasIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/atlas/AbstractAtlasIntegrationTest.java index 744c27d2f937..24bb9f9bf0d8 100644 --- a/src/test/java/de/tum/cit/aet/artemis/atlas/AbstractAtlasIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/atlas/AbstractAtlasIntegrationTest.java @@ -9,7 +9,9 @@ import de.tum.cit.aet.artemis.atlas.competency.util.PrerequisiteUtilService; import de.tum.cit.aet.artemis.atlas.competency.util.StandardizedCompetencyUtilService; import de.tum.cit.aet.artemis.atlas.learningpath.util.LearningPathUtilService; +import de.tum.cit.aet.artemis.atlas.repository.CompetencyExerciseLinkRepository; import de.tum.cit.aet.artemis.atlas.repository.CompetencyJolRepository; +import de.tum.cit.aet.artemis.atlas.repository.CompetencyLectureUnitLinkRepository; import de.tum.cit.aet.artemis.atlas.repository.CompetencyRelationRepository; import de.tum.cit.aet.artemis.atlas.repository.CompetencyRepository; import de.tum.cit.aet.artemis.atlas.repository.CourseCompetencyRepository; @@ -79,6 +81,12 @@ public abstract class AbstractAtlasIntegrationTest extends AbstractSpringIntegra @Autowired protected CompetencyJolRepository competencyJolRepository; + @Autowired + protected CompetencyExerciseLinkRepository competencyExerciseLinkRepository; + + @Autowired + protected CompetencyLectureUnitLinkRepository competencyLectureUnitLinkRepository; + // External Repositories @Autowired protected LectureRepository lectureRepository; diff --git a/src/test/java/de/tum/cit/aet/artemis/atlas/competency/AbstractCompetencyPrerequisiteIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/atlas/competency/AbstractCompetencyPrerequisiteIntegrationTest.java index c0e2a307b3a8..de3c984c279c 100644 --- a/src/test/java/de/tum/cit/aet/artemis/atlas/competency/AbstractCompetencyPrerequisiteIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/atlas/competency/AbstractCompetencyPrerequisiteIntegrationTest.java @@ -97,29 +97,31 @@ void creatingLectureUnitsOfLecture(CourseCompetency competency) { // creating lecture units for lecture one TextUnit textUnit = new TextUnit(); textUnit.setName("TextUnitOfLectureOne"); - textUnit.setCompetencyLinks(Set.of(new CompetencyLectureUnitLink(competency, textUnit, 1))); - textUnit = textUnitRepository.save(textUnit); - textUnitOfLectureOne = textUnit; + + CompetencyLectureUnitLink textLink = new CompetencyLectureUnitLink(competency, textUnit, 1); + textLink = competencyLectureUnitLinkRepository.save(textLink); + textUnitOfLectureOne = (TextUnit) textLink.getLectureUnit(); AttachmentUnit attachmentUnit = lectureUtilService.createAttachmentUnit(true); attachmentUnit.setName("AttachmentUnitOfLectureOne"); - attachmentUnit.setCompetencyLinks(Set.of(new CompetencyLectureUnitLink(competency, attachmentUnit, 1))); - attachmentUnit = attachmentUnitRepository.save(attachmentUnit); - attachmentUnitOfLectureOne = attachmentUnit; + + CompetencyLectureUnitLink attachmentLink = new CompetencyLectureUnitLink(competency, attachmentUnit, 1); + attachmentLink = competencyLectureUnitLinkRepository.save(attachmentLink); + attachmentUnitOfLectureOne = (AttachmentUnit) attachmentLink.getLectureUnit(); ExerciseUnit textExerciseUnit = new ExerciseUnit(); textExerciseUnit.setExercise(textExercise); - exerciseUnitRepository.save(textExerciseUnit); + textExerciseUnit = exerciseUnitRepository.save(textExerciseUnit); ExerciseUnit teamTextExerciseUnit = new ExerciseUnit(); teamTextExerciseUnit.setExercise(teamTextExercise); - exerciseUnitRepository.save(teamTextExerciseUnit); + teamTextExerciseUnit = exerciseUnitRepository.save(teamTextExerciseUnit); - for (LectureUnit lectureUnit : List.of(textUnit, attachmentUnit, textExerciseUnit, teamTextExerciseUnit)) { + for (LectureUnit lectureUnit : List.of(textUnitOfLectureOne, attachmentUnitOfLectureOne, textExerciseUnit, teamTextExerciseUnit)) { lecture.addLectureUnit(lectureUnit); } - lectureRepository.save(lecture); + lecture = lectureRepository.save(lecture); } Lecture createLecture(Course course) { @@ -145,17 +147,24 @@ TextExercise createTextExercise(ZonedDateTime releaseDate, ZonedDateTime dueDate textExercise.setMaxPoints(10.0); textExercise.setBonusPoints(0.0); - Set exerciseLinks = competencies.stream().map(competency -> new CompetencyExerciseLink(competency, textExercise, 1)).collect(Collectors.toSet()); - textExercise.setCompetencyLinks(exerciseLinks); - return exerciseRepository.save(textExercise); + var persistedExercise = exerciseRepository.save(textExercise); + + Set exerciseLinks = competencies.stream().map(competency -> new CompetencyExerciseLink(competency, persistedExercise, 1)) + .collect(Collectors.toSet()); + persistedExercise.setCompetencyLinks(new HashSet<>(competencyExerciseLinkRepository.saveAll(exerciseLinks))); + + return exerciseRepository.save(persistedExercise); } private ProgrammingExercise createProgrammingExercise(ZonedDateTime releaseDate, ZonedDateTime dueDate) { ProgrammingExercise programmingExercise = ProgrammingExerciseFactory.generateProgrammingExercise(releaseDate, dueDate, course, ProgrammingLanguage.JAVA); programmingExercise.setBuildConfig(programmingExerciseBuildConfigRepository.save(programmingExercise.getBuildConfig())); - programmingExercise.setCompetencyLinks(Set.of(new CompetencyExerciseLink(courseCompetency, programmingExercise, 1))); - return exerciseRepository.save(programmingExercise); + programmingExercise = exerciseRepository.save(programmingExercise); + + CompetencyExerciseLink link = new CompetencyExerciseLink(courseCompetency, programmingExercise, 1); + competencyExerciseLinkRepository.save(link); + return (ProgrammingExercise) link.getExercise(); } abstract CourseCompetency getCall(long courseId, long competencyId, HttpStatus expectedStatus) throws Exception; @@ -211,6 +220,8 @@ void shouldReturnCompetenciesForCourse(CourseCompetency newCompetency) throws Ex newCompetency.setTitle("Title"); newCompetency.setDescription("Description"); newCompetency.setCourse(course); + courseCompetencyRepository.save(newCompetency); + newCompetency.setLectureUnitLinks(new HashSet<>(List.of(new CompetencyLectureUnitLink(newCompetency, unreleasedLectureUnit, 1)))); courseCompetencyRepository.save(newCompetency); @@ -310,8 +321,8 @@ void shouldUpdateCompetencyToOptionalWhenSettingOptional(CourseCompetency newCom TextExercise exercise = TextExerciseFactory.generateTextExercise(ZonedDateTime.now(), ZonedDateTime.now(), ZonedDateTime.now(), course); exercise.setMaxPoints(1.0); exercise.setIncludedInOverallScore(includedInOverallScore); - exercise.setCompetencyLinks(Set.of(new CompetencyExerciseLink(newCompetency, exercise, 1))); - exerciseRepository.save(exercise); + CompetencyExerciseLink link = new CompetencyExerciseLink(newCompetency, exercise, 1); + competencyExerciseLinkRepository.save(link); newCompetency.setOptional(true); updateCall(course.getId(), newCompetency, HttpStatus.OK); @@ -329,8 +340,7 @@ void shouldCreateValidCompetency(CourseCompetency newCompetency) throws Exceptio newCompetency.setCourse(course); newCompetency.setMasteryThreshold(42); List allLectureUnits = lectureUnitRepository.findAll(); - Set connectedLectureUnits = new HashSet<>(allLectureUnits); - Set lectureUnitLinks = connectedLectureUnits.stream().map(lu -> new CompetencyLectureUnitLink(newCompetency, lu, 1)).collect(Collectors.toSet()); + Set lectureUnitLinks = allLectureUnits.stream().map(lu -> new CompetencyLectureUnitLink(newCompetency, lu, 1)).collect(Collectors.toSet()); newCompetency.setLectureUnitLinks(lectureUnitLinks); CourseCompetency result = createCall(course.getId(), newCompetency, HttpStatus.CREATED); @@ -398,9 +408,10 @@ void shouldImportExerciseAndLectureWithCompetency() throws Exception { // Test void shouldImportExerciseAndLectureWithCompetencyAndChangeDates() throws Exception { - teamTextExercise.setCompetencyLinks(null); + teamTextExercise.getCompetencyLinks().clear(); exerciseRepository.save(teamTextExercise); - attachmentUnitOfLectureOne.setCompetencyLinks(null); + attachmentUnitOfLectureOne = attachmentUnitRepository.findOneWithSlidesAndCompetencies(attachmentUnitOfLectureOne.getId()); + attachmentUnitOfLectureOne.getCompetencyLinks().clear(); attachmentUnitRepository.save(attachmentUnitOfLectureOne); ZonedDateTime releaseDate = ZonedDateTime.of(2022, 2, 21, 23, 45, 0, 0, ZoneId.of("UTC")); @@ -534,9 +545,10 @@ void shouldImportAllExerciseAndLectureWithCompetency() throws Exception { // Test void shouldImportAllExerciseAndLectureWithCompetencyAndChangeDates() throws Exception { - teamTextExercise.setCompetencyLinks(null); + teamTextExercise.getCompetencyLinks().clear(); exerciseRepository.save(teamTextExercise); - attachmentUnitOfLectureOne.setCompetencyLinks(null); + attachmentUnitOfLectureOne = attachmentUnitRepository.findOneWithSlidesAndCompetencies(attachmentUnitOfLectureOne.getId()); + attachmentUnitOfLectureOne.getCompetencyLinks().clear(); attachmentUnitRepository.save(attachmentUnitOfLectureOne); ZonedDateTime releaseDate = ZonedDateTime.of(2022, 2, 21, 23, 45, 0, 0, ZoneId.of("UTC")); @@ -666,9 +678,10 @@ void shouldImportCompetenciesExerciseAndLectureWithCompetency() throws Exception // Test void shouldImportCompetenciesExerciseAndLectureWithCompetencyAndChangeDates() throws Exception { - teamTextExercise.setCompetencyLinks(null); + teamTextExercise.getCompetencyLinks().clear(); exerciseRepository.save(teamTextExercise); - attachmentUnitOfLectureOne.setCompetencyLinks(null); + attachmentUnitOfLectureOne = attachmentUnitRepository.findOneWithSlidesAndCompetencies(attachmentUnitOfLectureOne.getId()); + attachmentUnitOfLectureOne.getCompetencyLinks().clear(); attachmentUnitRepository.save(attachmentUnitOfLectureOne); ZonedDateTime releaseDate = ZonedDateTime.of(2022, 2, 21, 23, 45, 0, 0, ZoneId.of("UTC")); diff --git a/src/test/java/de/tum/cit/aet/artemis/atlas/competency/util/CompetencyUtilService.java b/src/test/java/de/tum/cit/aet/artemis/atlas/competency/util/CompetencyUtilService.java index c35f2b94593b..782c7c4444c8 100644 --- a/src/test/java/de/tum/cit/aet/artemis/atlas/competency/util/CompetencyUtilService.java +++ b/src/test/java/de/tum/cit/aet/artemis/atlas/competency/util/CompetencyUtilService.java @@ -14,6 +14,7 @@ import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyRelation; import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyTaxonomy; import de.tum.cit.aet.artemis.atlas.domain.competency.RelationType; +import de.tum.cit.aet.artemis.atlas.repository.CompetencyExerciseLinkRepository; import de.tum.cit.aet.artemis.atlas.repository.CompetencyJolRepository; import de.tum.cit.aet.artemis.atlas.repository.CompetencyRelationRepository; import de.tum.cit.aet.artemis.atlas.repository.CompetencyRepository; @@ -45,6 +46,9 @@ public class CompetencyUtilService { @Autowired private CompetencyJolRepository competencyJOLRepository; + @Autowired + private CompetencyExerciseLinkRepository competencyExerciseLinkRepository; + /** * Creates and saves a Competency for the given Course. * @@ -74,10 +78,11 @@ public Competency createCompetencyWithExercise(Course course, Exercise exercise) Competency competency = new Competency(); competency.setTitle("ExampleCompetency"); competency.setCourse(course); - var link = new CompetencyExerciseLink(); - link.setExercise(exercise); - competency.getExerciseLinks().add(link); - return competencyRepo.save(competency); + competency = competencyRepo.save(competency); + + var link = new CompetencyExerciseLink(competency, exercise, 1); + link = competencyExerciseLinkRepository.save(link); + return (Competency) link.getCompetency(); } /** diff --git a/src/test/javascript/spec/component/competencies/prerequisite-form.component.spec.ts b/src/test/javascript/spec/component/competencies/prerequisite-form.component.spec.ts index 3013d118068d..e320a2419b83 100644 --- a/src/test/javascript/spec/component/competencies/prerequisite-form.component.spec.ts +++ b/src/test/javascript/spec/component/competencies/prerequisite-form.component.spec.ts @@ -120,14 +120,14 @@ describe('PrerequisiteFormComponent', () => { }; prerequisiteFormComponentFixture.detectChanges(); prerequisiteFormComponent.formData = formData; - prerequisiteFormComponent['onLectureUnitSelectionChange']([textUnit]); + prerequisiteFormComponent['onLectureUnitSelectionChange']([new CompetencyLectureUnitLink(undefined, textUnit, 1)]); prerequisiteFormComponent.ngOnChanges(); expect(prerequisiteFormComponent.titleControl?.value).toEqual(formData.title); expect(prerequisiteFormComponent.descriptionControl?.value).toEqual(formData.description); expect(prerequisiteFormComponent.softDueDateControl?.value).toEqual(formData.softDueDate); expect(prerequisiteFormComponent.optionalControl?.value).toEqual(formData.optional); - expect(prerequisiteFormComponent.selectedLectureUnitsInTable).toEqual(formData.connectedLectureUnits); + expect(prerequisiteFormComponent.selectedLectureUnitLinksInTable).toEqual(formData.lectureUnitLinks); }); it('should suggest taxonomy when title changes', () => { diff --git a/src/test/javascript/spec/component/competencies/prerequisite.service.spec.ts b/src/test/javascript/spec/component/competencies/prerequisite.service.spec.ts index e2e8687c1bcc..04109dad55e7 100644 --- a/src/test/javascript/spec/component/competencies/prerequisite.service.spec.ts +++ b/src/test/javascript/spec/component/competencies/prerequisite.service.spec.ts @@ -389,7 +389,7 @@ describe('PrerequisiteService', () => { expect(convertDateSpy).not.toHaveBeenCalled(); expect(convertLectureUnitSpy).not.toHaveBeenCalled(); expect(setAccessRightsCourseSpy).not.toHaveBeenCalled(); - expect(convertExercisesSpy).not.toHaveBeenCalled(); + expect(convertExerciseSpy).not.toHaveBeenCalled(); expect(parseCategoriesSpy).not.toHaveBeenCalled(); expect(setAccessRightsExerciseSpy).not.toHaveBeenCalled(); diff --git a/src/test/javascript/spec/component/lecture/wizard-mode/lecture-wizard-units.component.spec.ts b/src/test/javascript/spec/component/lecture/wizard-mode/lecture-wizard-units.component.spec.ts index 5c95475131dd..6abf0ee067bd 100644 --- a/src/test/javascript/spec/component/lecture/wizard-mode/lecture-wizard-units.component.spec.ts +++ b/src/test/javascript/spec/component/lecture/wizard-mode/lecture-wizard-units.component.spec.ts @@ -27,6 +27,7 @@ import { Attachment, AttachmentType } from 'app/entities/attachment.model'; import { AttachmentUnit } from 'app/entities/lecture-unit/attachmentUnit.model'; import { objectToJsonBlob } from 'app/utils/blob-util'; import { CreateExerciseUnitComponent } from 'app/lecture/lecture-unit/lecture-unit-management/create-exercise-unit/create-exercise-unit.component'; +import { CompetencyLectureUnitLink } from '../../../../../../main/webapp/app/entities/competency.model'; @Component({ selector: 'jhi-video-unit-form', template: '' }) class VideoUnitFormStubComponent { @@ -164,14 +165,18 @@ describe('LectureWizardUnitComponent', () => { releaseDate: dayjs().year(2010).month(3).date(5), description: 'Lorem Ipsum', source: 'https://youtu.be/dQw4w9WgXcQ', - competencies: [ - { - id: 1, - masteryThreshold: 0, - optional: false, - taxonomy: undefined, - title: 'Test', - }, + competencyLinks: [ + new CompetencyLectureUnitLink( + { + id: 1, + masteryThreshold: 0, + optional: false, + taxonomy: undefined, + title: 'Test', + }, + undefined, + 1, + ), ], }; @@ -237,14 +242,18 @@ describe('LectureWizardUnitComponent', () => { name: 'Test', releaseDate: dayjs().year(2010).month(3).date(5), content: 'Lorem Ipsum', - competencies: [ - { - id: 1, - masteryThreshold: 0, - optional: false, - taxonomy: undefined, - title: 'Test', - }, + competencyLinks: [ + new CompetencyLectureUnitLink( + { + id: 1, + masteryThreshold: 0, + optional: false, + taxonomy: undefined, + title: 'Test', + }, + undefined, + 1, + ), ], }; @@ -396,14 +405,18 @@ describe('LectureWizardUnitComponent', () => { releaseDate: dayjs().year(2010).month(3).date(5), description: 'Lorem Ipsum', source: 'https://www.example.com', - competencies: [ - { - id: 1, - masteryThreshold: 0, - optional: false, - taxonomy: undefined, - title: 'Test', - }, + competencyLinks: [ + new CompetencyLectureUnitLink( + { + id: 1, + masteryThreshold: 0, + optional: false, + taxonomy: undefined, + title: 'Test', + }, + undefined, + 1, + ), ], }; @@ -474,14 +487,18 @@ describe('LectureWizardUnitComponent', () => { releaseDate: dayjs().year(2010).month(3).date(5), version: 2, updateNotificationText: 'lorem ipsum', - competencies: [ - { - id: 1, - masteryThreshold: 0, - optional: false, - taxonomy: undefined, - title: 'Test', - }, + competencyLinks: [ + new CompetencyLectureUnitLink( + { + id: 1, + masteryThreshold: 0, + optional: false, + taxonomy: undefined, + title: 'Test', + }, + undefined, + 1, + ), ], }, fileProperties: { diff --git a/src/test/javascript/spec/component/shared/competency-selection.component.spec.ts b/src/test/javascript/spec/component/shared/competency-selection.component.spec.ts index 8e41d06a5f74..9d23d1c736b5 100644 --- a/src/test/javascript/spec/component/shared/competency-selection.component.spec.ts +++ b/src/test/javascript/spec/component/shared/competency-selection.component.spec.ts @@ -82,8 +82,8 @@ describe('CompetencySelection', () => { expect(getAllForCourseSpy).toHaveBeenCalledOnce(); expect(component.isLoading).toBeFalse(); expect(component.competencyLinks).toBeArrayOfSize(2); - expect(component.competencyLinks?.first()?.course).toBeUndefined(); - expect(component.competencyLinks?.first()?.userProgress).toBeUndefined(); + expect(component.competencyLinks?.first()?.competency.course).toBeUndefined(); + expect(component.competencyLinks?.first()?.competency.userProgress).toBeUndefined(); }); it('should set disabled when error during loading', () => { @@ -119,7 +119,7 @@ describe('CompetencySelection', () => { component.writeValue([new CompetencyLearningObjectLink({ id: 1, title: 'other' } as Competency, 1)]); expect(component.selectedCompetencyLinks).toBeArrayOfSize(1); - expect(component.selectedCompetencyLinks?.first()?.title).toBe('test'); + expect(component.selectedCompetencyLinks?.first()?.competency.title).toBe('test'); }); it('should trigger change detection after loading competencies', () => { From a700139fa4cfee0329378a33ac635673d978a388 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20St=C3=B6hr?= Date: Sat, 19 Oct 2024 12:57:09 +0200 Subject: [PATCH 03/25] Try around --- .../competency/CompetencyExerciseLink.java | 3 +- .../CompetencyLearningObjectLink.java | 3 +- .../competency/CompetencyLectureUnitLink.java | 3 +- .../service/ExerciseDeletionService.java | 3 -- .../lecture/service/LectureUnitService.java | 13 ------- ...CompetencyPrerequisiteIntegrationTest.java | 37 ++++++++----------- 6 files changed, 22 insertions(+), 40 deletions(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyExerciseLink.java b/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyExerciseLink.java index a22fdd99ab6b..fe40eaef95cf 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyExerciseLink.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyExerciseLink.java @@ -1,5 +1,6 @@ package de.tum.cit.aet.artemis.atlas.domain.competency; +import jakarta.persistence.CascadeType; import jakarta.persistence.Entity; import jakarta.persistence.ManyToOne; import jakarta.persistence.MapsId; @@ -15,7 +16,7 @@ @Table(name = "competency_exercise") public class CompetencyExerciseLink extends CompetencyLearningObjectLink { - @ManyToOne(optional = false) + @ManyToOne(optional = false, cascade = CascadeType.ALL) @MapsId("learningObjectId") private Exercise exercise; diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLearningObjectLink.java b/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLearningObjectLink.java index 39ff69550b0c..5dec58a38ff8 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLearningObjectLink.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLearningObjectLink.java @@ -4,6 +4,7 @@ import java.io.Serializable; import java.util.Objects; +import jakarta.persistence.CascadeType; import jakarta.persistence.Column; import jakarta.persistence.Embeddable; import jakarta.persistence.EmbeddedId; @@ -23,7 +24,7 @@ public abstract class CompetencyLearningObjectLink implements Serializable { @JsonIgnore protected CompetencyLearningObjectId id = new CompetencyLearningObjectId(); - @ManyToOne(optional = false) + @ManyToOne(optional = false, cascade = CascadeType.ALL) @MapsId("competencyId") protected CourseCompetency competency; diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLectureUnitLink.java b/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLectureUnitLink.java index 5280e5314da4..f585a2198818 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLectureUnitLink.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLectureUnitLink.java @@ -1,5 +1,6 @@ package de.tum.cit.aet.artemis.atlas.domain.competency; +import jakarta.persistence.CascadeType; import jakarta.persistence.Entity; import jakarta.persistence.ManyToOne; import jakarta.persistence.MapsId; @@ -15,7 +16,7 @@ @Table(name = "competency_lecture_unit") public class CompetencyLectureUnitLink extends CompetencyLearningObjectLink { - @ManyToOne(optional = false) + @ManyToOne(optional = false, cascade = CascadeType.ALL) @MapsId("learningObjectId") private LectureUnit lectureUnit; diff --git a/src/main/java/de/tum/cit/aet/artemis/exercise/service/ExerciseDeletionService.java b/src/main/java/de/tum/cit/aet/artemis/exercise/service/ExerciseDeletionService.java index 205b6dc1e8e8..d9e355fadb9a 100644 --- a/src/main/java/de/tum/cit/aet/artemis/exercise/service/ExerciseDeletionService.java +++ b/src/main/java/de/tum/cit/aet/artemis/exercise/service/ExerciseDeletionService.java @@ -180,9 +180,6 @@ public void delete(long exerciseId, boolean deleteStudentReposBuildPlans, boolea // delete all participations belonging to this exercise, this will also delete submissions, results, feedback, complaints, etc. participationService.deleteAllByExercise(exercise, deleteStudentReposBuildPlans, deleteStudentReposBuildPlans, false); - // delete all competency links belonging to this exercise - competencyExerciseLinkRepository.deleteAll(exercise.getCompetencyLinks()); - // clean up the many-to-many relationship to avoid problems when deleting the entities but not the relationship table exercise = exerciseRepository.findByIdWithEagerExampleSubmissionsElseThrow(exerciseId); exercise.getExampleSubmissions().forEach(exampleSubmission -> exampleSubmissionService.deleteById(exampleSubmission.getId())); diff --git a/src/main/java/de/tum/cit/aet/artemis/lecture/service/LectureUnitService.java b/src/main/java/de/tum/cit/aet/artemis/lecture/service/LectureUnitService.java index ae7282ed9fd5..0afd5e9506cb 100644 --- a/src/main/java/de/tum/cit/aet/artemis/lecture/service/LectureUnitService.java +++ b/src/main/java/de/tum/cit/aet/artemis/lecture/service/LectureUnitService.java @@ -20,7 +20,6 @@ import org.springframework.dao.DataIntegrityViolationException; import org.springframework.stereotype.Service; -import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyLearningObjectLink; import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyLectureUnitLink; import de.tum.cit.aet.artemis.atlas.domain.competency.CourseCompetency; import de.tum.cit.aet.artemis.atlas.repository.CompetencyLectureUnitLinkRepository; @@ -156,16 +155,6 @@ private LectureUnitCompletion createLectureUnitCompletion(LectureUnit lectureUni public void removeLectureUnit(@NotNull LectureUnit lectureUnit) { LectureUnit lectureUnitToDelete = lectureUnitRepository.findByIdWithCompetenciesAndSlidesElseThrow(lectureUnit.getId()); - if (!(lectureUnitToDelete instanceof ExerciseUnit)) { - // update associated competencies - Set competencies = lectureUnitToDelete.getCompetencyLinks().stream().map(CompetencyLearningObjectLink::getCompetency).collect(Collectors.toSet()); - courseCompetencyRepository.saveAll(competencies.stream().map(competency -> { - competency = courseCompetencyRepository.findByIdWithLectureUnitsElseThrow(competency.getId()); - competency.getLectureUnitLinks().remove(lectureUnitToDelete); - return competency; - }).toList()); - } - if (lectureUnitToDelete instanceof AttachmentUnit attachmentUnit) { fileService.schedulePathForDeletion(FilePathService.actualPathForPublicPathOrThrow(URI.create((attachmentUnit.getAttachment().getLink()))), 5); if (attachmentUnit.getSlides() != null && !attachmentUnit.getSlides().isEmpty()) { @@ -184,8 +173,6 @@ public void removeLectureUnit(@NotNull LectureUnit lectureUnit) { lectureRepository.save(lecture); if (!(lectureUnitToDelete instanceof ExerciseUnit)) { - // Delete the links to competencies - competencyLectureUnitLinkRepository.deleteAll(lectureUnitToDelete.getCompetencyLinks()); // update associated competency progress objects competencyProgressService.updateProgressForUpdatedLearningObjectAsync(lectureUnitToDelete, Optional.empty()); } diff --git a/src/test/java/de/tum/cit/aet/artemis/atlas/competency/AbstractCompetencyPrerequisiteIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/atlas/competency/AbstractCompetencyPrerequisiteIntegrationTest.java index de3c984c279c..97d51e45236f 100644 --- a/src/test/java/de/tum/cit/aet/artemis/atlas/competency/AbstractCompetencyPrerequisiteIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/atlas/competency/AbstractCompetencyPrerequisiteIntegrationTest.java @@ -79,8 +79,8 @@ void setupTestScenario(String TEST_PREFIX, Function cr courseCompetency = createCourseCompetencyForCourse.apply(course); lecture = createLecture(course); - textExercise = createTextExercise(pastTimestamp, pastTimestamp, pastTimestamp, Set.of(courseCompetency), false); - teamTextExercise = createTextExercise(pastTimestamp, pastTimestamp, pastTimestamp, Set.of(courseCompetency), true); + textExercise = createTextExercise(pastTimestamp, pastTimestamp, pastTimestamp, courseCompetency, false); + teamTextExercise = createTextExercise(pastTimestamp, pastTimestamp, pastTimestamp, courseCompetency, true); creatingLectureUnitsOfLecture(courseCompetency); } @@ -95,19 +95,18 @@ CompetencyRelation createRelation(CourseCompetency tail, CourseCompetency head, void creatingLectureUnitsOfLecture(CourseCompetency competency) { // creating lecture units for lecture one - TextUnit textUnit = new TextUnit(); - textUnit.setName("TextUnitOfLectureOne"); + textUnitOfLectureOne = new TextUnit(); + textUnitOfLectureOne.setName("TextUnitOfLectureOne"); + CompetencyLectureUnitLink link = new CompetencyLectureUnitLink(competency, textUnitOfLectureOne, 1); + link = competencyLectureUnitLinkRepository.save(link); + textUnitOfLectureOne = (TextUnit) link.getLectureUnit(); - CompetencyLectureUnitLink textLink = new CompetencyLectureUnitLink(competency, textUnit, 1); - textLink = competencyLectureUnitLinkRepository.save(textLink); - textUnitOfLectureOne = (TextUnit) textLink.getLectureUnit(); + attachmentUnitOfLectureOne = lectureUtilService.createAttachmentUnit(true); + attachmentUnitOfLectureOne.setName("AttachmentUnitOfLectureOne"); - AttachmentUnit attachmentUnit = lectureUtilService.createAttachmentUnit(true); - attachmentUnit.setName("AttachmentUnitOfLectureOne"); - - CompetencyLectureUnitLink attachmentLink = new CompetencyLectureUnitLink(competency, attachmentUnit, 1); - attachmentLink = competencyLectureUnitLinkRepository.save(attachmentLink); - attachmentUnitOfLectureOne = (AttachmentUnit) attachmentLink.getLectureUnit(); + link = new CompetencyLectureUnitLink(competency, attachmentUnitOfLectureOne, 1); + link = competencyLectureUnitLinkRepository.save(link); + attachmentUnitOfLectureOne = (AttachmentUnit) link.getLectureUnit(); ExerciseUnit textExerciseUnit = new ExerciseUnit(); textExerciseUnit.setExercise(textExercise); @@ -133,7 +132,7 @@ Lecture createLecture(Course course) { return lecture; } - TextExercise createTextExercise(ZonedDateTime releaseDate, ZonedDateTime dueDate, ZonedDateTime assassmentDueDate, Set competencies, boolean isTeamExercise) { + TextExercise createTextExercise(ZonedDateTime releaseDate, ZonedDateTime dueDate, ZonedDateTime assassmentDueDate, CourseCompetency competency, boolean isTeamExercise) { // creating text exercise with Result TextExercise textExercise = TextExerciseFactory.generateTextExercise(releaseDate, dueDate, assassmentDueDate, course); @@ -147,14 +146,10 @@ TextExercise createTextExercise(ZonedDateTime releaseDate, ZonedDateTime dueDate textExercise.setMaxPoints(10.0); textExercise.setBonusPoints(0.0); + CompetencyExerciseLink link = new CompetencyExerciseLink(competency, textExercise, 1); + link = competencyExerciseLinkRepository.save(link); - var persistedExercise = exerciseRepository.save(textExercise); - - Set exerciseLinks = competencies.stream().map(competency -> new CompetencyExerciseLink(competency, persistedExercise, 1)) - .collect(Collectors.toSet()); - persistedExercise.setCompetencyLinks(new HashSet<>(competencyExerciseLinkRepository.saveAll(exerciseLinks))); - - return exerciseRepository.save(persistedExercise); + return (TextExercise) link.getExercise(); } private ProgrammingExercise createProgrammingExercise(ZonedDateTime releaseDate, ZonedDateTime dueDate) { From 4d68a02cba46483c2455dc14ebf449732df25223 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20St=C3=B6hr?= Date: Sat, 19 Oct 2024 13:01:30 +0200 Subject: [PATCH 04/25] Revert LoggingAspect --- .../de/tum/cit/aet/artemis/core/config/LoggingAspect.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/core/config/LoggingAspect.java b/src/main/java/de/tum/cit/aet/artemis/core/config/LoggingAspect.java index ebf70f1a7b21..a916500b1bcc 100644 --- a/src/main/java/de/tum/cit/aet/artemis/core/config/LoggingAspect.java +++ b/src/main/java/de/tum/cit/aet/artemis/core/config/LoggingAspect.java @@ -96,8 +96,8 @@ else if (e.getMessage() != null && e.getMessage().startsWith("The username has t @Around("applicationPackagePointcut() && springBeanPointcut()") public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable { if (log.isDebugEnabled()) { - // log.debug("Enter: {}.{}() with argument[s] = {}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName(), - // Arrays.toString(joinPoint.getArgs())); + log.debug("Enter: {}.{}() with argument[s] = {}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName(), + Arrays.toString(joinPoint.getArgs())); } try { Object result = joinPoint.proceed(); From 8c3dc7c971c96f6667241c0dc0e2898157d61fea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20St=C3=B6hr?= Date: Sun, 20 Oct 2024 19:39:53 +0200 Subject: [PATCH 05/25] Fix more tests --- .../competency/CompetencyExerciseLink.java | 78 +++++++++++++++++- .../CompetencyLearningObjectLink.java | 81 +------------------ .../competency/CompetencyLectureUnitLink.java | 78 +++++++++++++++++- .../domain/competency/CourseCompetency.java | 4 +- .../CompetencyExerciseLinkRepository.java | 7 ++ .../CompetencyLectureUnitLinkRepository.java | 15 ++++ .../repository/LearningPathRepository.java | 3 +- .../competency/CourseCompetencyService.java | 6 +- .../LearningPathNavigationService.java | 3 +- .../artemis/atlas/web/CompetencyResource.java | 1 - .../atlas/web/PrerequisiteResource.java | 1 - .../artemis/lecture/domain/ExerciseUnit.java | 2 + .../lecture/service/LectureService.java | 2 + .../lecture/service/LectureUnitService.java | 8 +- ...CompetencyPrerequisiteIntegrationTest.java | 30 +++---- .../util/CompetencyUtilService.java | 12 +-- .../competency-selection.component.spec.ts | 6 +- 17 files changed, 210 insertions(+), 127 deletions(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyExerciseLink.java b/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyExerciseLink.java index fe40eaef95cf..ff52358d9d6e 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyExerciseLink.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyExerciseLink.java @@ -1,6 +1,11 @@ package de.tum.cit.aet.artemis.atlas.domain.competency; -import jakarta.persistence.CascadeType; +import java.io.Serial; +import java.io.Serializable; +import java.util.Objects; + +import jakarta.persistence.Embeddable; +import jakarta.persistence.EmbeddedId; import jakarta.persistence.Entity; import jakarta.persistence.ManyToOne; import jakarta.persistence.MapsId; @@ -9,6 +14,8 @@ import org.hibernate.annotations.Cache; import org.hibernate.annotations.CacheConcurrencyStrategy; +import com.fasterxml.jackson.annotation.JsonIgnore; + import de.tum.cit.aet.artemis.exercise.domain.Exercise; @Entity @@ -16,8 +23,12 @@ @Table(name = "competency_exercise") public class CompetencyExerciseLink extends CompetencyLearningObjectLink { - @ManyToOne(optional = false, cascade = CascadeType.ALL) - @MapsId("learningObjectId") + @EmbeddedId + @JsonIgnore + protected CompetencyExerciseId id = new CompetencyExerciseId(); + + @ManyToOne(optional = false) + @MapsId("exerciseId") private Exercise exercise; public Exercise getExercise() { @@ -37,8 +48,69 @@ public CompetencyExerciseLink() { // Empty constructor for Spring } + public CompetencyExerciseId getId() { + return id; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof CompetencyExerciseLink that)) { + return false; + } + return Objects.equals(id, that.id); + } + + @Override + public int hashCode() { + return Objects.hashCode(id); + } + @Override public String toString() { return "CompetencyExerciseLink{" + "exercise=" + exercise + ", id=" + id + ", competency=" + competency + ", weight=" + weight + '}'; } + + @Embeddable + public static class CompetencyExerciseId implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + private long exerciseId; + + private long competencyId; + + public CompetencyExerciseId() { + // Empty constructor for Spring + } + + public CompetencyExerciseId(long exerciseId, long competencyId) { + this.exerciseId = exerciseId; + this.competencyId = competencyId; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof CompetencyExerciseId that)) { + return false; + } + return exerciseId == that.exerciseId && competencyId == that.competencyId; + } + + @Override + public int hashCode() { + return Objects.hash(exerciseId, competencyId); + } + + @Override + public String toString() { + return "CompetencyExerciseId{" + "exerciseId=" + exerciseId + ", competencyId=" + competencyId + '}'; + } + } } diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLearningObjectLink.java b/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLearningObjectLink.java index 5dec58a38ff8..8283b7db9910 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLearningObjectLink.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLearningObjectLink.java @@ -1,40 +1,22 @@ package de.tum.cit.aet.artemis.atlas.domain.competency; -import java.io.Serial; import java.io.Serializable; -import java.util.Objects; -import jakarta.persistence.CascadeType; import jakarta.persistence.Column; -import jakarta.persistence.Embeddable; -import jakarta.persistence.EmbeddedId; import jakarta.persistence.ManyToOne; import jakarta.persistence.MappedSuperclass; import jakarta.persistence.MapsId; -import com.fasterxml.jackson.annotation.JsonIgnore; - @MappedSuperclass public abstract class CompetencyLearningObjectLink implements Serializable { - /** - * The primary key of the association, composited through {@link CompetencyLearningObjectId}. - */ - @EmbeddedId - @JsonIgnore - protected CompetencyLearningObjectId id = new CompetencyLearningObjectId(); - - @ManyToOne(optional = false, cascade = CascadeType.ALL) + @ManyToOne(optional = false) @MapsId("competencyId") protected CourseCompetency competency; @Column(name = "link_weight") protected double weight; - public CompetencyLearningObjectId getId() { - return id; - } - public CourseCompetency getCompetency() { return competency; } @@ -59,65 +41,4 @@ public CompetencyLearningObjectLink(CourseCompetency competency, double weight) public CompetencyLearningObjectLink() { // Empty constructor for Spring } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof CompetencyLearningObjectLink that)) { - return false; - } - return Objects.equals(id, that.id); - } - - @Override - public int hashCode() { - return Objects.hashCode(id); - } - - /** - * This class is used to create a composite primary key (user_id, competency_id). - * See also
... - */ - @Embeddable - public static class CompetencyLearningObjectId implements Serializable { - - @Serial - private static final long serialVersionUID = 1L; - - private long learningObjectId; - - private long competencyId; - - public CompetencyLearningObjectId() { - // Empty constructor for Spring - } - - public CompetencyLearningObjectId(long learningObjectId, long competencyId) { - this.learningObjectId = learningObjectId; - this.competencyId = competencyId; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof CompetencyLearningObjectId that)) { - return false; - } - return learningObjectId == that.learningObjectId && competencyId == that.competencyId; - } - - @Override - public int hashCode() { - return Objects.hash(learningObjectId, competencyId); - } - - @Override - public String toString() { - return "CompetencyLearningObjectId{" + "learningObjectId=" + learningObjectId + ", competencyId=" + competencyId + '}'; - } - } } diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLectureUnitLink.java b/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLectureUnitLink.java index f585a2198818..983002e3c151 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLectureUnitLink.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLectureUnitLink.java @@ -1,6 +1,11 @@ package de.tum.cit.aet.artemis.atlas.domain.competency; -import jakarta.persistence.CascadeType; +import java.io.Serial; +import java.io.Serializable; +import java.util.Objects; + +import jakarta.persistence.Embeddable; +import jakarta.persistence.EmbeddedId; import jakarta.persistence.Entity; import jakarta.persistence.ManyToOne; import jakarta.persistence.MapsId; @@ -9,6 +14,8 @@ import org.hibernate.annotations.Cache; import org.hibernate.annotations.CacheConcurrencyStrategy; +import com.fasterxml.jackson.annotation.JsonIgnore; + import de.tum.cit.aet.artemis.lecture.domain.LectureUnit; @Entity @@ -16,8 +23,12 @@ @Table(name = "competency_lecture_unit") public class CompetencyLectureUnitLink extends CompetencyLearningObjectLink { - @ManyToOne(optional = false, cascade = CascadeType.ALL) - @MapsId("learningObjectId") + @EmbeddedId + @JsonIgnore + protected CompetencyLectureUnitId id = new CompetencyLectureUnitId(); + + @ManyToOne(optional = false) + @MapsId("lectureUnitId") private LectureUnit lectureUnit; public LectureUnit getLectureUnit() { @@ -37,8 +48,69 @@ public CompetencyLectureUnitLink() { // Empty constructor for Spring } + public CompetencyLectureUnitId getId() { + return id; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof CompetencyExerciseLink that)) { + return false; + } + return Objects.equals(id, that.id); + } + + @Override + public int hashCode() { + return Objects.hashCode(id); + } + @Override public String toString() { return "CompetencyLectureUnitLink{" + "lectureUnit=" + lectureUnit + ", id=" + id + ", competency=" + competency + ", weight=" + weight + '}'; } + + @Embeddable + public static class CompetencyLectureUnitId implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + private long lectureUnitId; + + private long competencyId; + + public CompetencyLectureUnitId() { + // Empty constructor for Spring + } + + public CompetencyLectureUnitId(long lectureUnitId, long competencyId) { + this.lectureUnitId = lectureUnitId; + this.competencyId = competencyId; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof CompetencyLectureUnitId that)) { + return false; + } + return lectureUnitId == that.lectureUnitId && competencyId == that.competencyId; + } + + @Override + public int hashCode() { + return Objects.hash(lectureUnitId, competencyId); + } + + @Override + public String toString() { + return "CompetencyLectureUnitId{" + "lectureUnitId=" + lectureUnitId + ", competencyId=" + competencyId + '}'; + } + } } diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CourseCompetency.java b/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CourseCompetency.java index 44a439589183..d83432982d7a 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CourseCompetency.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CourseCompetency.java @@ -187,8 +187,8 @@ public void removeLectureUnit(LectureUnit lectureUnit) { // The competencies of ExerciseUnits are taken from the corresponding exercise throw new IllegalArgumentException("ExerciseUnits can not be disconnected from competencies"); } - this.lectureUnitLinks.remove(lectureUnit); - lectureUnit.getCompetencyLinks().remove(this); + this.lectureUnitLinks.removeIf(lul -> lul.getLectureUnit().equals(lectureUnit)); + lectureUnit.getCompetencyLinks().removeIf(cl -> cl.getCompetency().equals(this)); } public Set getUserProgress() { diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyExerciseLinkRepository.java b/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyExerciseLinkRepository.java index 96d6f51ba9a8..0c0bd25b9c8a 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyExerciseLinkRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyExerciseLinkRepository.java @@ -1,7 +1,14 @@ package de.tum.cit.aet.artemis.atlas.repository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.transaction.annotation.Transactional; + import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyExerciseLink; import de.tum.cit.aet.artemis.core.repository.base.ArtemisJpaRepository; public interface CompetencyExerciseLinkRepository extends ArtemisJpaRepository { + + @Modifying + @Transactional + void deleteAllByExerciseId(long exerciseId); } diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyLectureUnitLinkRepository.java b/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyLectureUnitLinkRepository.java index ae7bcf04cead..02963879bab5 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyLectureUnitLinkRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyLectureUnitLinkRepository.java @@ -1,8 +1,23 @@ package de.tum.cit.aet.artemis.atlas.repository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.transaction.annotation.Transactional; + import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyLectureUnitLink; import de.tum.cit.aet.artemis.core.repository.base.ArtemisJpaRepository; public interface CompetencyLectureUnitLinkRepository extends ArtemisJpaRepository { + @Modifying + @Transactional + void deleteAllByLectureUnitId(long lectureUnitId); + + @Modifying + @Transactional + @Query(""" + DELETE FROM CompetencyLectureUnitLink clul + WHERE clul.lectureUnit.lecture.id = :lectureId + """) + void deleteAllByLectureId(long lectureId); } diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/repository/LearningPathRepository.java b/src/main/java/de/tum/cit/aet/artemis/atlas/repository/LearningPathRepository.java index 596b50ef2e78..d07a63bcc3b9 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/repository/LearningPathRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/repository/LearningPathRepository.java @@ -63,7 +63,8 @@ SELECT COUNT (learningPath) """) long countLearningPathsOfEnrolledStudentsInCourse(@Param("courseId") long courseId); - @EntityGraph(type = LOAD, attributePaths = { "competencies", "competencies.lectureUnits", "competencies.exercises" }) + @EntityGraph(type = LOAD, attributePaths = { "competencies", "competencies.lectureUnitLinks", "competencies.lectureUnitLinks.lectureUnit", "competencies.exerciseLinks", + "competencies.exerciseLinks.exercise" }) Optional findWithCompetenciesAndLectureUnitsAndExercisesById(long learningPathId); default LearningPath findWithCompetenciesAndLectureUnitsAndExercisesByIdElseThrow(long learningPathId) { diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CourseCompetencyService.java b/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CourseCompetencyService.java index 71de4cee51bd..786857d2d860 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CourseCompetencyService.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CourseCompetencyService.java @@ -349,6 +349,8 @@ public C updateCourseCompetency(C competencyToUpdat competencyToUpdate.setMasteryThreshold(competency.getMasteryThreshold()); competencyToUpdate.setTaxonomy(competency.getTaxonomy()); competencyToUpdate.setOptional(competency.isOptional()); + competencyToUpdate.getLectureUnitLinks().forEach(link -> link.setCompetency(competency)); + competencyToUpdate.setLectureUnitLinks(competency.getLectureUnitLinks()); final var persistedCompetency = courseCompetencyRepository.save(competencyToUpdate); // update competency progress if necessary @@ -445,7 +447,9 @@ public void checkIfCompetencyBelongsToCourse(long competencyId, long courseId) { private void updateLectureUnits(CourseCompetency competency, CourseCompetency createdCompetency) { if (!competency.getLectureUnitLinks().isEmpty()) { - lectureUnitService.linkLectureUnitsToCompetency(createdCompetency, competency.getLectureUnitLinks(), Set.of()); + competency.getLectureUnitLinks().forEach(link -> link.setCompetency(competency)); + competency.setLectureUnitLinks(competency.getLectureUnitLinks()); + courseCompetencyRepository.save(competency); competencyProgressService.updateProgressByCompetencyAndUsersInCourseAsync(createdCompetency); } } diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/service/learningpath/LearningPathNavigationService.java b/src/main/java/de/tum/cit/aet/artemis/atlas/service/learningpath/LearningPathNavigationService.java index 586caf54b905..a744a0688446 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/service/learningpath/LearningPathNavigationService.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/service/learningpath/LearningPathNavigationService.java @@ -82,7 +82,8 @@ public LearningPathNavigationDTO getNavigation(LearningPath learningPath) { private CourseCompetency findCorrespondingCompetencyForLearningObject(RecommendationState recommendationState, LearningObject learningObject, boolean firstCompetency) { Stream potentialCompetencies = recommendationState.recommendedOrderOfCompetencies().stream() .map(competencyId -> recommendationState.competencyIdMap().get(competencyId)) - .filter(competency -> competency.getLectureUnitLinks().contains(learningObject) || competency.getExerciseLinks().contains(learningObject)); + .filter(competency -> competency.getLectureUnitLinks().stream().anyMatch(lul -> lul.getLectureUnit().equals(learningObject)) + || competency.getExerciseLinks().stream().anyMatch(el -> el.getExercise().equals(learningObject))); // There will always be at least one competency that contains the learning object, otherwise the learning object would not be in the learning path Comparator comparator = Comparator.comparingInt(competency -> recommendationState.recommendedOrderOfCompetencies().indexOf(competency.getId())); diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/web/CompetencyResource.java b/src/main/java/de/tum/cit/aet/artemis/atlas/web/CompetencyResource.java index 315a86d1fdbf..2f7fd4206dc6 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/web/CompetencyResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/web/CompetencyResource.java @@ -301,7 +301,6 @@ public ResponseEntity updateCompetency(@PathVariable long courseId, checkCourseForCompetency(course, existingCompetency); var persistedCompetency = competencyService.updateCourseCompetency(existingCompetency, competency); - lectureUnitService.linkLectureUnitsToCompetency(persistedCompetency, competency.getLectureUnitLinks(), existingCompetency.getLectureUnitLinks()); return ResponseEntity.ok(persistedCompetency); } diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/web/PrerequisiteResource.java b/src/main/java/de/tum/cit/aet/artemis/atlas/web/PrerequisiteResource.java index 2723fdd7d6f5..71ae58330077 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/web/PrerequisiteResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/web/PrerequisiteResource.java @@ -305,7 +305,6 @@ public ResponseEntity updatePrerequisite(@PathVariable long course checkCourseForPrerequisite(course, existingPrerequisite); var persistedPrerequisite = prerequisiteService.updateCourseCompetency(existingPrerequisite, prerequisite); - lectureUnitService.linkLectureUnitsToCompetency(persistedPrerequisite, prerequisite.getLectureUnitLinks(), existingPrerequisite.getLectureUnitLinks()); return ResponseEntity.ok(persistedPrerequisite); } diff --git a/src/main/java/de/tum/cit/aet/artemis/lecture/domain/ExerciseUnit.java b/src/main/java/de/tum/cit/aet/artemis/lecture/domain/ExerciseUnit.java index da38f23f3995..70ff83ad509b 100644 --- a/src/main/java/de/tum/cit/aet/artemis/lecture/domain/ExerciseUnit.java +++ b/src/main/java/de/tum/cit/aet/artemis/lecture/domain/ExerciseUnit.java @@ -14,6 +14,7 @@ import org.hibernate.annotations.Cache; import org.hibernate.annotations.CacheConcurrencyStrategy; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonInclude; import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyLectureUnitLink; @@ -64,6 +65,7 @@ public void setReleaseDate(ZonedDateTime releaseDate) { } @Override + @JsonIgnore public Set getCompetencyLinks() { throw new UnsupportedOperationException("Create the link in the associated exercise instead"); } diff --git a/src/main/java/de/tum/cit/aet/artemis/lecture/service/LectureService.java b/src/main/java/de/tum/cit/aet/artemis/lecture/service/LectureService.java index c2fda95f726e..3095605139a1 100644 --- a/src/main/java/de/tum/cit/aet/artemis/lecture/service/LectureService.java +++ b/src/main/java/de/tum/cit/aet/artemis/lecture/service/LectureService.java @@ -168,6 +168,8 @@ public void delete(Lecture lecture, boolean updateCompetencyProgress) { Channel lectureChannel = channelRepository.findChannelByLectureId(lecture.getId()); channelService.deleteChannel(lectureChannel); + competencyLectureUnitLinkRepository.deleteAllByLectureId(lecture.getId()); + lectureRepository.deleteById(lecture.getId()); } diff --git a/src/main/java/de/tum/cit/aet/artemis/lecture/service/LectureUnitService.java b/src/main/java/de/tum/cit/aet/artemis/lecture/service/LectureUnitService.java index 0afd5e9506cb..cdc7488b1a8b 100644 --- a/src/main/java/de/tum/cit/aet/artemis/lecture/service/LectureUnitService.java +++ b/src/main/java/de/tum/cit/aet/artemis/lecture/service/LectureUnitService.java @@ -183,15 +183,13 @@ public void removeLectureUnit(@NotNull LectureUnit lectureUnit) { * * @param competency The competency to be linked * @param lectureUnitLinksToAdd A set of lecture unit links to add to the specified competency - * @param lectureUnitLinksToRemove A set of lecture unit link to remove from the specified competency + * @param lectureUnitLinksToRemove A set of lecture unit links to remove from the specified competency */ public void linkLectureUnitsToCompetency(CourseCompetency competency, Set lectureUnitLinksToAdd, Set lectureUnitLinksToRemove) { lectureUnitLinksToAdd.forEach(link -> link.setCompetency(competency)); - List persistedLinks = competencyLectureUnitLinkRepository.saveAll(lectureUnitLinksToAdd); - competencyLectureUnitLinkRepository.deleteAll(lectureUnitLinksToRemove); - - competency.getLectureUnitLinks().addAll(persistedLinks); + competency.setLectureUnitLinks(lectureUnitLinksToAdd); + courseCompetencyRepository.save(competency); } /** diff --git a/src/test/java/de/tum/cit/aet/artemis/atlas/competency/AbstractCompetencyPrerequisiteIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/atlas/competency/AbstractCompetencyPrerequisiteIntegrationTest.java index 97d51e45236f..dc7c47559427 100644 --- a/src/test/java/de/tum/cit/aet/artemis/atlas/competency/AbstractCompetencyPrerequisiteIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/atlas/competency/AbstractCompetencyPrerequisiteIntegrationTest.java @@ -135,6 +135,8 @@ Lecture createLecture(Course course) { TextExercise createTextExercise(ZonedDateTime releaseDate, ZonedDateTime dueDate, ZonedDateTime assassmentDueDate, CourseCompetency competency, boolean isTeamExercise) { // creating text exercise with Result TextExercise textExercise = TextExerciseFactory.generateTextExercise(releaseDate, dueDate, assassmentDueDate, course); + textExercise.setMaxPoints(10.0); + textExercise.setBonusPoints(0.0); if (isTeamExercise) { textExercise.setMode(ExerciseMode.TEAM); @@ -143,13 +145,10 @@ TextExercise createTextExercise(ZonedDateTime releaseDate, ZonedDateTime dueDate teamAssignmentConfig.setMaxTeamSize(2); textExercise.setTeamAssignmentConfig(teamAssignmentConfig); } + textExercise = exerciseRepository.save(textExercise); - textExercise.setMaxPoints(10.0); - textExercise.setBonusPoints(0.0); CompetencyExerciseLink link = new CompetencyExerciseLink(competency, textExercise, 1); - link = competencyExerciseLinkRepository.save(link); - - return (TextExercise) link.getExercise(); + return (TextExercise) competencyExerciseLinkRepository.save(link).getExercise(); } private ProgrammingExercise createProgrammingExercise(ZonedDateTime releaseDate, ZonedDateTime dueDate) { @@ -403,11 +402,8 @@ void shouldImportExerciseAndLectureWithCompetency() throws Exception { // Test void shouldImportExerciseAndLectureWithCompetencyAndChangeDates() throws Exception { - teamTextExercise.getCompetencyLinks().clear(); - exerciseRepository.save(teamTextExercise); - attachmentUnitOfLectureOne = attachmentUnitRepository.findOneWithSlidesAndCompetencies(attachmentUnitOfLectureOne.getId()); - attachmentUnitOfLectureOne.getCompetencyLinks().clear(); - attachmentUnitRepository.save(attachmentUnitOfLectureOne); + competencyExerciseLinkRepository.deleteAllByExerciseId(teamTextExercise.getId()); + competencyLectureUnitLinkRepository.deleteAllByLectureUnitId(attachmentUnitOfLectureOne.getId()); ZonedDateTime releaseDate = ZonedDateTime.of(2022, 2, 21, 23, 45, 0, 0, ZoneId.of("UTC")); textExercise.setReleaseDate(releaseDate); @@ -540,11 +536,8 @@ void shouldImportAllExerciseAndLectureWithCompetency() throws Exception { // Test void shouldImportAllExerciseAndLectureWithCompetencyAndChangeDates() throws Exception { - teamTextExercise.getCompetencyLinks().clear(); - exerciseRepository.save(teamTextExercise); - attachmentUnitOfLectureOne = attachmentUnitRepository.findOneWithSlidesAndCompetencies(attachmentUnitOfLectureOne.getId()); - attachmentUnitOfLectureOne.getCompetencyLinks().clear(); - attachmentUnitRepository.save(attachmentUnitOfLectureOne); + competencyExerciseLinkRepository.deleteAllByExerciseId(teamTextExercise.getId()); + competencyLectureUnitLinkRepository.deleteAllByLectureUnitId(attachmentUnitOfLectureOne.getId()); ZonedDateTime releaseDate = ZonedDateTime.of(2022, 2, 21, 23, 45, 0, 0, ZoneId.of("UTC")); textExercise.setReleaseDate(releaseDate); @@ -673,11 +666,8 @@ void shouldImportCompetenciesExerciseAndLectureWithCompetency() throws Exception // Test void shouldImportCompetenciesExerciseAndLectureWithCompetencyAndChangeDates() throws Exception { - teamTextExercise.getCompetencyLinks().clear(); - exerciseRepository.save(teamTextExercise); - attachmentUnitOfLectureOne = attachmentUnitRepository.findOneWithSlidesAndCompetencies(attachmentUnitOfLectureOne.getId()); - attachmentUnitOfLectureOne.getCompetencyLinks().clear(); - attachmentUnitRepository.save(attachmentUnitOfLectureOne); + competencyExerciseLinkRepository.deleteAllByExerciseId(teamTextExercise.getId()); + competencyLectureUnitLinkRepository.deleteAllByLectureUnitId(attachmentUnitOfLectureOne.getId()); ZonedDateTime releaseDate = ZonedDateTime.of(2022, 2, 21, 23, 45, 0, 0, ZoneId.of("UTC")); textExercise.setReleaseDate(releaseDate); diff --git a/src/test/java/de/tum/cit/aet/artemis/atlas/competency/util/CompetencyUtilService.java b/src/test/java/de/tum/cit/aet/artemis/atlas/competency/util/CompetencyUtilService.java index 782c7c4444c8..0b7cbc5e08d3 100644 --- a/src/test/java/de/tum/cit/aet/artemis/atlas/competency/util/CompetencyUtilService.java +++ b/src/test/java/de/tum/cit/aet/artemis/atlas/competency/util/CompetencyUtilService.java @@ -16,6 +16,7 @@ import de.tum.cit.aet.artemis.atlas.domain.competency.RelationType; import de.tum.cit.aet.artemis.atlas.repository.CompetencyExerciseLinkRepository; import de.tum.cit.aet.artemis.atlas.repository.CompetencyJolRepository; +import de.tum.cit.aet.artemis.atlas.repository.CompetencyLectureUnitLinkRepository; import de.tum.cit.aet.artemis.atlas.repository.CompetencyRelationRepository; import de.tum.cit.aet.artemis.atlas.repository.CompetencyRepository; import de.tum.cit.aet.artemis.core.domain.Course; @@ -49,6 +50,9 @@ public class CompetencyUtilService { @Autowired private CompetencyExerciseLinkRepository competencyExerciseLinkRepository; + @Autowired + private CompetencyLectureUnitLinkRepository competencyLectureUnitLinkRepository; + /** * Creates and saves a Competency for the given Course. * @@ -146,9 +150,7 @@ public Competency[] createCompetencies(Course course, int numberOfCompetencies) */ public LectureUnit linkLectureUnitToCompetency(Competency competency, LectureUnit lectureUnit) { CompetencyLectureUnitLink link = new CompetencyLectureUnitLink(competency, lectureUnit, 1); - lectureUnit.getCompetencyLinks().add(link); - competency.getLectureUnitLinks().add(link); - return lectureUnitRepository.save(lectureUnit); + return competencyLectureUnitLinkRepository.save(link).getLectureUnit(); } /** @@ -160,9 +162,7 @@ public LectureUnit linkLectureUnitToCompetency(Competency competency, LectureUni */ public Exercise linkExerciseToCompetency(Competency competency, Exercise exercise) { CompetencyExerciseLink link = new CompetencyExerciseLink(competency, exercise, 1); - exercise.getCompetencyLinks().add(link); - competency.getExerciseLinks().add(link); - return exerciseRepository.save(exercise); + return competencyExerciseLinkRepository.save(link).getExercise(); } /** diff --git a/src/test/javascript/spec/component/shared/competency-selection.component.spec.ts b/src/test/javascript/spec/component/shared/competency-selection.component.spec.ts index 9d23d1c736b5..d90a9cf4c747 100644 --- a/src/test/javascript/spec/component/shared/competency-selection.component.spec.ts +++ b/src/test/javascript/spec/component/shared/competency-selection.component.spec.ts @@ -82,8 +82,8 @@ describe('CompetencySelection', () => { expect(getAllForCourseSpy).toHaveBeenCalledOnce(); expect(component.isLoading).toBeFalse(); expect(component.competencyLinks).toBeArrayOfSize(2); - expect(component.competencyLinks?.first()?.competency.course).toBeUndefined(); - expect(component.competencyLinks?.first()?.competency.userProgress).toBeUndefined(); + expect(component.competencyLinks?.first()?.competency?.course).toBeUndefined(); + expect(component.competencyLinks?.first()?.competency?.userProgress).toBeUndefined(); }); it('should set disabled when error during loading', () => { @@ -119,7 +119,7 @@ describe('CompetencySelection', () => { component.writeValue([new CompetencyLearningObjectLink({ id: 1, title: 'other' } as Competency, 1)]); expect(component.selectedCompetencyLinks).toBeArrayOfSize(1); - expect(component.selectedCompetencyLinks?.first()?.competency.title).toBe('test'); + expect(component.selectedCompetencyLinks?.first()?.competency?.title).toBe('test'); }); it('should trigger change detection after loading competencies', () => { From 8d63e745b4a1ea5d2f8716b0c0aa09596d4bbb88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20St=C3=B6hr?= Date: Mon, 21 Oct 2024 14:50:05 +0200 Subject: [PATCH 06/25] Fix more tests --- .../competency/CompetencyExerciseLink.java | 23 +++++++----- .../CompetencyLearningObjectLink.java | 3 +- .../competency/CompetencyLectureUnitLink.java | 25 ++++++++----- .../CompetencyExerciseLinkRepository.java | 7 +--- .../CompetencyLectureUnitLinkRepository.java | 9 ++--- .../CompetencyMetricsRepository.java | 3 +- .../artemis/atlas/web/CompetencyResource.java | 8 +--- .../atlas/web/PrerequisiteResource.java | 8 +--- .../exercise/service/ExerciseService.java | 9 +++++ .../web/FileUploadExerciseResource.java | 2 + .../artemis/lecture/domain/ExerciseUnit.java | 6 ++- .../repository/AttachmentUnitRepository.java | 5 +++ .../service/AttachmentUnitService.java | 18 +++++++-- .../lecture/service/LectureUnitService.java | 37 +++++++++++++++---- .../lecture/web/OnlineUnitResource.java | 11 ++++++ .../artemis/lecture/web/TextUnitResource.java | 18 ++++++++- .../lecture/web/VideoUnitResource.java | 11 ++++++ .../atlas/AbstractAtlasIntegrationTest.java | 8 ++-- .../CourseCompetencyIntegrationTest.java | 14 +++---- .../util/CompetencyUtilService.java | 16 ++------ .../CompetencyExerciseLinkTestRepository.java | 17 +++++++++ ...mpetencyLectureUnitLinkTestRepository.java | 18 +++++++++ .../artemis/core/MetricsIntegrationTest.java | 3 +- .../AbstractFileUploadIntegrationTest.java | 4 ++ .../FileUploadExerciseIntegrationTest.java | 2 +- .../AttachmentUnitIntegrationTest.java | 3 +- .../lecture/OnlineUnitIntegrationTest.java | 10 +++-- .../lecture/TextUnitIntegrationTest.java | 15 +++++--- .../lecture/VideoUnitIntegrationTest.java | 10 +++-- .../ModelingExerciseIntegrationTest.java | 2 +- .../competency-selection.component.spec.ts | 4 +- 31 files changed, 230 insertions(+), 99 deletions(-) create mode 100644 src/test/java/de/tum/cit/aet/artemis/atlas/test_repository/CompetencyExerciseLinkTestRepository.java create mode 100644 src/test/java/de/tum/cit/aet/artemis/atlas/test_repository/CompetencyLectureUnitLinkTestRepository.java diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyExerciseLink.java b/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyExerciseLink.java index ff52358d9d6e..4a8104e7e891 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyExerciseLink.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyExerciseLink.java @@ -4,6 +4,7 @@ import java.io.Serializable; import java.util.Objects; +import jakarta.persistence.CascadeType; import jakarta.persistence.Embeddable; import jakarta.persistence.EmbeddedId; import jakarta.persistence.Entity; @@ -27,18 +28,10 @@ public class CompetencyExerciseLink extends CompetencyLearningObjectLink { @JsonIgnore protected CompetencyExerciseId id = new CompetencyExerciseId(); - @ManyToOne(optional = false) + @ManyToOne(optional = false, cascade = CascadeType.PERSIST) @MapsId("exerciseId") private Exercise exercise; - public Exercise getExercise() { - return exercise; - } - - public void setExercise(Exercise exercise) { - this.exercise = exercise; - } - public CompetencyExerciseLink(CourseCompetency competency, Exercise exercise, double weight) { super(competency, weight); this.exercise = exercise; @@ -48,10 +41,22 @@ public CompetencyExerciseLink() { // Empty constructor for Spring } + public Exercise getExercise() { + return exercise; + } + + public void setExercise(Exercise exercise) { + this.exercise = exercise; + } + public CompetencyExerciseId getId() { return id; } + public void setId(CompetencyExerciseId id) { + this.id = id; + } + @Override public boolean equals(Object o) { if (this == o) { diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLearningObjectLink.java b/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLearningObjectLink.java index 8283b7db9910..7791376aa08a 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLearningObjectLink.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLearningObjectLink.java @@ -2,6 +2,7 @@ import java.io.Serializable; +import jakarta.persistence.CascadeType; import jakarta.persistence.Column; import jakarta.persistence.ManyToOne; import jakarta.persistence.MappedSuperclass; @@ -10,7 +11,7 @@ @MappedSuperclass public abstract class CompetencyLearningObjectLink implements Serializable { - @ManyToOne(optional = false) + @ManyToOne(optional = false, cascade = CascadeType.PERSIST) @MapsId("competencyId") protected CourseCompetency competency; diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLectureUnitLink.java b/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLectureUnitLink.java index 983002e3c151..49541489835f 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLectureUnitLink.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLectureUnitLink.java @@ -4,6 +4,7 @@ import java.io.Serializable; import java.util.Objects; +import jakarta.persistence.CascadeType; import jakarta.persistence.Embeddable; import jakarta.persistence.EmbeddedId; import jakarta.persistence.Entity; @@ -27,18 +28,10 @@ public class CompetencyLectureUnitLink extends CompetencyLearningObjectLink { @JsonIgnore protected CompetencyLectureUnitId id = new CompetencyLectureUnitId(); - @ManyToOne(optional = false) + @ManyToOne(optional = false, cascade = CascadeType.PERSIST) @MapsId("lectureUnitId") private LectureUnit lectureUnit; - public LectureUnit getLectureUnit() { - return lectureUnit; - } - - public void setLectureUnit(LectureUnit lectureUnit) { - this.lectureUnit = lectureUnit; - } - public CompetencyLectureUnitLink(CourseCompetency competency, LectureUnit lectureUnit, double weight) { super(competency, weight); this.lectureUnit = lectureUnit; @@ -48,16 +41,28 @@ public CompetencyLectureUnitLink() { // Empty constructor for Spring } + public LectureUnit getLectureUnit() { + return lectureUnit; + } + + public void setLectureUnit(LectureUnit lectureUnit) { + this.lectureUnit = lectureUnit; + } + public CompetencyLectureUnitId getId() { return id; } + public void setId(CompetencyLectureUnitId id) { + this.id = id; + } + @Override public boolean equals(Object o) { if (this == o) { return true; } - if (!(o instanceof CompetencyExerciseLink that)) { + if (!(o instanceof CompetencyLectureUnitLink that)) { return false; } return Objects.equals(id, that.id); diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyExerciseLinkRepository.java b/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyExerciseLinkRepository.java index 0c0bd25b9c8a..bf60360f9022 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyExerciseLinkRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyExerciseLinkRepository.java @@ -1,14 +1,11 @@ package de.tum.cit.aet.artemis.atlas.repository; -import org.springframework.data.jpa.repository.Modifying; -import org.springframework.transaction.annotation.Transactional; +import org.springframework.stereotype.Repository; import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyExerciseLink; import de.tum.cit.aet.artemis.core.repository.base.ArtemisJpaRepository; +@Repository public interface CompetencyExerciseLinkRepository extends ArtemisJpaRepository { - @Modifying - @Transactional - void deleteAllByExerciseId(long exerciseId); } diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyLectureUnitLinkRepository.java b/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyLectureUnitLinkRepository.java index 02963879bab5..5086fe5eb3a8 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyLectureUnitLinkRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyLectureUnitLinkRepository.java @@ -2,22 +2,21 @@ import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyLectureUnitLink; import de.tum.cit.aet.artemis.core.repository.base.ArtemisJpaRepository; +@Repository public interface CompetencyLectureUnitLinkRepository extends ArtemisJpaRepository { - @Modifying - @Transactional - void deleteAllByLectureUnitId(long lectureUnitId); - @Modifying @Transactional @Query(""" DELETE FROM CompetencyLectureUnitLink clul WHERE clul.lectureUnit.lecture.id = :lectureId """) - void deleteAllByLectureId(long lectureId); + void deleteAllByLectureId(@Param("lectureId") long lectureId); } diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyMetricsRepository.java b/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyMetricsRepository.java index c2888428ec3b..abc050e0479a 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyMetricsRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyMetricsRepository.java @@ -45,7 +45,8 @@ public interface CompetencyMetricsRepository extends ArtemisJpaRepository findAllExerciseIdsByCompetencyIds(@Param("competencyIds") Set competencyIds); diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/web/CompetencyResource.java b/src/main/java/de/tum/cit/aet/artemis/atlas/web/CompetencyResource.java index 2f7fd4206dc6..9bfb3a92acfe 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/web/CompetencyResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/web/CompetencyResource.java @@ -43,7 +43,6 @@ import de.tum.cit.aet.artemis.core.security.annotations.enforceRoleInCourse.EnforceAtLeastStudentInCourse; import de.tum.cit.aet.artemis.core.service.AuthorizationCheckService; import de.tum.cit.aet.artemis.core.util.HeaderUtil; -import de.tum.cit.aet.artemis.lecture.service.LectureUnitService; @Profile(PROFILE_CORE) @RestController @@ -67,21 +66,18 @@ public class CompetencyResource { private final CompetencyService competencyService; - private final LectureUnitService lectureUnitService; - private final CourseCompetencyRepository courseCompetencyRepository; private final CourseCompetencyService courseCompetencyService; public CompetencyResource(CourseRepository courseRepository, AuthorizationCheckService authorizationCheckService, UserRepository userRepository, - CompetencyRepository competencyRepository, CompetencyService competencyService, LectureUnitService lectureUnitService, - CourseCompetencyRepository courseCompetencyRepository, CourseCompetencyService courseCompetencyService) { + CompetencyRepository competencyRepository, CompetencyService competencyService, CourseCompetencyRepository courseCompetencyRepository, + CourseCompetencyService courseCompetencyService) { this.courseRepository = courseRepository; this.authorizationCheckService = authorizationCheckService; this.userRepository = userRepository; this.competencyRepository = competencyRepository; this.competencyService = competencyService; - this.lectureUnitService = lectureUnitService; this.courseCompetencyRepository = courseCompetencyRepository; this.courseCompetencyService = courseCompetencyService; } diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/web/PrerequisiteResource.java b/src/main/java/de/tum/cit/aet/artemis/atlas/web/PrerequisiteResource.java index 71ae58330077..4f75c618282d 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/web/PrerequisiteResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/web/PrerequisiteResource.java @@ -44,7 +44,6 @@ import de.tum.cit.aet.artemis.core.security.annotations.enforceRoleInCourse.EnforceAtLeastStudentInCourse; import de.tum.cit.aet.artemis.core.service.AuthorizationCheckService; import de.tum.cit.aet.artemis.core.util.HeaderUtil; -import de.tum.cit.aet.artemis.lecture.service.LectureUnitService; /** * REST controller for managing {@link Prerequisite Prerequisite} entities. @@ -71,21 +70,18 @@ public class PrerequisiteResource { private final PrerequisiteService prerequisiteService; - private final LectureUnitService lectureUnitService; - private final CourseCompetencyRepository courseCompetencyRepository; private final CourseCompetencyService courseCompetencyService; public PrerequisiteResource(CourseRepository courseRepository, AuthorizationCheckService authorizationCheckService, UserRepository userRepository, - PrerequisiteRepository prerequisiteRepository, PrerequisiteService prerequisiteService, LectureUnitService lectureUnitService, - CourseCompetencyRepository courseCompetencyRepository, CourseCompetencyService courseCompetencyService) { + PrerequisiteRepository prerequisiteRepository, PrerequisiteService prerequisiteService, CourseCompetencyRepository courseCompetencyRepository, + CourseCompetencyService courseCompetencyService) { this.courseRepository = courseRepository; this.authorizationCheckService = authorizationCheckService; this.userRepository = userRepository; this.prerequisiteRepository = prerequisiteRepository; this.prerequisiteService = prerequisiteService; - this.lectureUnitService = lectureUnitService; this.courseCompetencyRepository = courseCompetencyRepository; this.courseCompetencyService = courseCompetencyService; } diff --git a/src/main/java/de/tum/cit/aet/artemis/exercise/service/ExerciseService.java b/src/main/java/de/tum/cit/aet/artemis/exercise/service/ExerciseService.java index 164fbd86a507..c7afead50288 100644 --- a/src/main/java/de/tum/cit/aet/artemis/exercise/service/ExerciseService.java +++ b/src/main/java/de/tum/cit/aet/artemis/exercise/service/ExerciseService.java @@ -794,4 +794,13 @@ else if (now().plusMinutes(EXAM_START_WAIT_TIME_MINUTES).isAfter(originalExercis this.examLiveEventsService.createAndSendProblemStatementUpdateEvent(updatedExercise, notificationText, instructor); } } + + /** + * Reconnects the competency exercise links to the exercise after the cycle was broken by the deserialization. + * + * @param exercise exercise to reconnect the links + */ + public void reconnectCompetencyExerciseLinks(Exercise exercise) { + exercise.getCompetencyLinks().forEach(link -> link.setExercise(exercise)); + } } diff --git a/src/main/java/de/tum/cit/aet/artemis/fileupload/web/FileUploadExerciseResource.java b/src/main/java/de/tum/cit/aet/artemis/fileupload/web/FileUploadExerciseResource.java index 84e361d2b4fe..724cb3820a6e 100644 --- a/src/main/java/de/tum/cit/aet/artemis/fileupload/web/FileUploadExerciseResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/fileupload/web/FileUploadExerciseResource.java @@ -281,6 +281,8 @@ public ResponseEntity updateFileUploadExercise(@RequestBody channelService.updateExerciseChannel(fileUploadExerciseBeforeUpdate, fileUploadExercise); + exerciseService.reconnectCompetencyExerciseLinks(fileUploadExercise); + var updatedExercise = fileUploadExerciseRepository.save(fileUploadExercise); exerciseService.logUpdate(updatedExercise, updatedExercise.getCourseViaExerciseGroupOrCourseMember(), user); exerciseService.updatePointsInRelatedParticipantScores(fileUploadExerciseBeforeUpdate, updatedExercise); diff --git a/src/main/java/de/tum/cit/aet/artemis/lecture/domain/ExerciseUnit.java b/src/main/java/de/tum/cit/aet/artemis/lecture/domain/ExerciseUnit.java index 70ff83ad509b..c609a9f7b049 100644 --- a/src/main/java/de/tum/cit/aet/artemis/lecture/domain/ExerciseUnit.java +++ b/src/main/java/de/tum/cit/aet/artemis/lecture/domain/ExerciseUnit.java @@ -1,6 +1,7 @@ package de.tum.cit.aet.artemis.lecture.domain; import java.time.ZonedDateTime; +import java.util.HashSet; import java.util.Set; import jakarta.persistence.DiscriminatorValue; @@ -67,12 +68,13 @@ public void setReleaseDate(ZonedDateTime releaseDate) { @Override @JsonIgnore public Set getCompetencyLinks() { - throw new UnsupportedOperationException("Create the link in the associated exercise instead"); + // Set the links in the associated exercise instead + return new HashSet<>(); } @Override public void setCompetencyLinks(Set competencyLinks) { - throw new UnsupportedOperationException("Retrieve the link in the associated exercise instead"); + // Retrieve the link in the associated exercise instead" } /** diff --git a/src/main/java/de/tum/cit/aet/artemis/lecture/repository/AttachmentUnitRepository.java b/src/main/java/de/tum/cit/aet/artemis/lecture/repository/AttachmentUnitRepository.java index 61f268d75b95..56363cb84e03 100644 --- a/src/main/java/de/tum/cit/aet/artemis/lecture/repository/AttachmentUnitRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/lecture/repository/AttachmentUnitRepository.java @@ -1,12 +1,14 @@ package de.tum.cit.aet.artemis.lecture.repository; import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE; +import static org.springframework.data.jpa.repository.EntityGraph.EntityGraphType.LOAD; import java.util.List; import jakarta.validation.constraints.NotNull; import org.springframework.context.annotation.Profile; +import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; @@ -63,4 +65,7 @@ default List findAllByLectureIdAndAttachmentTypeElseThrow(Long l WHERE attachmentUnit.id = :attachmentUnitId """) AttachmentUnit findOneWithSlidesAndCompetencies(@Param("attachmentUnitId") long attachmentUnitId); + + @EntityGraph(type = LOAD, attributePaths = "competencyLinks") + AttachmentUnit findOneWithCompetencyLinksById(long attachmentUnitId); } diff --git a/src/main/java/de/tum/cit/aet/artemis/lecture/service/AttachmentUnitService.java b/src/main/java/de/tum/cit/aet/artemis/lecture/service/AttachmentUnitService.java index 01ccdcd7616d..2e0b51a1fdab 100644 --- a/src/main/java/de/tum/cit/aet/artemis/lecture/service/AttachmentUnitService.java +++ b/src/main/java/de/tum/cit/aet/artemis/lecture/service/AttachmentUnitService.java @@ -5,6 +5,7 @@ import java.net.URI; import java.nio.file.Path; import java.time.ZonedDateTime; +import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Optional; @@ -50,9 +51,11 @@ public class AttachmentUnitService { private final CompetencyProgressService competencyProgressService; + private final LectureUnitService lectureUnitService; + public AttachmentUnitService(SlideRepository slideRepository, SlideSplitterService slideSplitterService, AttachmentUnitRepository attachmentUnitRepository, AttachmentRepository attachmentRepository, FileService fileService, Optional pyrisWebhookService, - Optional irisSettingsRepository, CompetencyProgressService competencyProgressService) { + Optional irisSettingsRepository, CompetencyProgressService competencyProgressService, LectureUnitService lectureUnitService) { this.attachmentUnitRepository = attachmentUnitRepository; this.attachmentRepository = attachmentRepository; this.fileService = fileService; @@ -61,6 +64,7 @@ public AttachmentUnitService(SlideRepository slideRepository, SlideSplitterServi this.pyrisWebhookService = pyrisWebhookService; this.irisSettingsRepository = irisSettingsRepository; this.competencyProgressService = competencyProgressService; + this.lectureUnitService = lectureUnitService; } /** @@ -76,8 +80,14 @@ public AttachmentUnitService(SlideRepository slideRepository, SlideSplitterServi public AttachmentUnit createAttachmentUnit(AttachmentUnit attachmentUnit, Attachment attachment, Lecture lecture, MultipartFile file, boolean keepFilename) { // persist lecture unit before lecture to prevent "null index column for collection" error attachmentUnit.setLecture(null); + // persist lecture unit before competency links to prevent error + Set links = attachmentUnit.getCompetencyLinks(); + attachmentUnit.setCompetencyLinks(new HashSet<>()); + AttachmentUnit savedAttachmentUnit = attachmentUnitRepository.saveAndFlush(attachmentUnit); attachmentUnit.setLecture(lecture); + savedAttachmentUnit.setCompetencyLinks(links); + lectureUnitService.reconnectCompetencyLectureUnitLinks(savedAttachmentUnit); lecture.addLectureUnit(savedAttachmentUnit); handleFile(file, attachment, keepFilename, savedAttachmentUnit.getId()); @@ -106,12 +116,14 @@ public AttachmentUnit createAttachmentUnit(AttachmentUnit attachmentUnit, Attach */ public AttachmentUnit updateAttachmentUnit(AttachmentUnit existingAttachmentUnit, AttachmentUnit updateUnit, Attachment updateAttachment, MultipartFile updateFile, boolean keepFilename) { - Set existingCompetencyLinks = existingAttachmentUnit.getCompetencyLinks(); + Set existingCompetencyLinks = new HashSet<>(existingAttachmentUnit.getCompetencyLinks()); existingAttachmentUnit.setDescription(updateUnit.getDescription()); existingAttachmentUnit.setName(updateUnit.getName()); existingAttachmentUnit.setReleaseDate(updateUnit.getReleaseDate()); - existingAttachmentUnit.setCompetencyLinks(updateUnit.getCompetencyLinks()); + + lectureUnitService.updateCompetencyLectureUnitLinks(existingAttachmentUnit, updateUnit); + lectureUnitService.reconnectCompetencyLectureUnitLinks(existingAttachmentUnit); AttachmentUnit savedAttachmentUnit = attachmentUnitRepository.saveAndFlush(existingAttachmentUnit); diff --git a/src/main/java/de/tum/cit/aet/artemis/lecture/service/LectureUnitService.java b/src/main/java/de/tum/cit/aet/artemis/lecture/service/LectureUnitService.java index cdc7488b1a8b..fa6b1184804f 100644 --- a/src/main/java/de/tum/cit/aet/artemis/lecture/service/LectureUnitService.java +++ b/src/main/java/de/tum/cit/aet/artemis/lecture/service/LectureUnitService.java @@ -179,16 +179,14 @@ public void removeLectureUnit(@NotNull LectureUnit lectureUnit) { } /** - * Link the competency to a set of lecture units (and exercises if it includes exercise units) + * Link the competency to a set of lecture units * - * @param competency The competency to be linked - * @param lectureUnitLinksToAdd A set of lecture unit links to add to the specified competency - * @param lectureUnitLinksToRemove A set of lecture unit links to remove from the specified competency + * @param competency The competency to be linked + * @param lectureUnitLinks New set of lecture unit links to associate with the competency */ - public void linkLectureUnitsToCompetency(CourseCompetency competency, Set lectureUnitLinksToAdd, - Set lectureUnitLinksToRemove) { - lectureUnitLinksToAdd.forEach(link -> link.setCompetency(competency)); - competency.setLectureUnitLinks(lectureUnitLinksToAdd); + public void linkLectureUnitsToCompetency(CourseCompetency competency, Set lectureUnitLinks) { + lectureUnitLinks.forEach(link -> link.setCompetency(competency)); + competency.setLectureUnitLinks(lectureUnitLinks); courseCompetencyRepository.save(competency); } @@ -218,4 +216,27 @@ public URL validateUrlStringAndReturnUrl(String urlString) { throw new BadRequestException(); } } + + /** + * Reconnects the competency exercise links to the exercise after the cycle was broken by the deserialization. + * + * @param lectureUnit The lecture unit to reconnect the competency links + */ + public void reconnectCompetencyLectureUnitLinks(LectureUnit lectureUnit) { + lectureUnit.getCompetencyLinks().forEach(link -> link.setLectureUnit(lectureUnit)); + } + + /** + * Updates the competency lecture unit links of the existing lecture unit with the updated lecture unit. + * + * @param existingLectureUnit The existing lecture unit + * @param updatedLectureUnit The updated lecture unit + */ + public void updateCompetencyLectureUnitLinks(LectureUnit existingLectureUnit, LectureUnit updatedLectureUnit) { + existingLectureUnit.getCompetencyLinks().removeIf( + link -> updatedLectureUnit.getCompetencyLinks().stream().noneMatch(updateLink -> updateLink.getCompetency().getId().equals(link.getCompetency().getId()))); + existingLectureUnit.getCompetencyLinks().addAll(updatedLectureUnit.getCompetencyLinks().stream().filter( + link -> existingLectureUnit.getCompetencyLinks().stream().noneMatch(existingLink -> existingLink.getCompetency().getId().equals(link.getCompetency().getId()))) + .toList()); + } } diff --git a/src/main/java/de/tum/cit/aet/artemis/lecture/web/OnlineUnitResource.java b/src/main/java/de/tum/cit/aet/artemis/lecture/web/OnlineUnitResource.java index 473d0201c294..b731caa04070 100644 --- a/src/main/java/de/tum/cit/aet/artemis/lecture/web/OnlineUnitResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/lecture/web/OnlineUnitResource.java @@ -6,7 +6,9 @@ import java.net.URI; import java.net.URISyntaxException; import java.net.URL; +import java.util.HashSet; import java.util.Optional; +import java.util.Set; import jakarta.ws.rs.BadRequestException; @@ -28,6 +30,7 @@ import com.google.common.net.InternetDomainName; +import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyLectureUnitLink; import de.tum.cit.aet.artemis.atlas.service.competency.CompetencyProgressService; import de.tum.cit.aet.artemis.core.dto.OnlineResourceDTO; import de.tum.cit.aet.artemis.core.exception.BadRequestAlertException; @@ -108,6 +111,7 @@ public ResponseEntity updateOnlineUnit(@PathVariable Long lectureId, authorizationCheckService.checkHasAtLeastRoleInCourseElseThrow(Role.EDITOR, onlineUnit.getLecture().getCourse(), null); + lectureUnitService.reconnectCompetencyLectureUnitLinks(onlineUnit); OnlineUnit result = onlineUnitRepository.save(onlineUnit); competencyProgressService.updateProgressForUpdatedLearningObjectAsync(existingOnlineUnit, Optional.of(onlineUnit)); @@ -141,8 +145,15 @@ public ResponseEntity createOnlineUnit(@PathVariable Long lectureId, // persist lecture unit before lecture to prevent "null index column for collection" error onlineUnit.setLecture(null); + // persist lecture unit before competency links to prevent error + Set links = onlineUnit.getCompetencyLinks(); + onlineUnit.setCompetencyLinks(new HashSet<>()); + OnlineUnit persistedOnlineUnit = onlineUnitRepository.saveAndFlush(onlineUnit); + persistedOnlineUnit.setCompetencyLinks(links); + lectureUnitService.reconnectCompetencyLectureUnitLinks(persistedOnlineUnit); + persistedOnlineUnit.setLecture(lecture); lecture.addLectureUnit(persistedOnlineUnit); lectureRepository.save(lecture); diff --git a/src/main/java/de/tum/cit/aet/artemis/lecture/web/TextUnitResource.java b/src/main/java/de/tum/cit/aet/artemis/lecture/web/TextUnitResource.java index 02bfaf149839..9263efb5324e 100644 --- a/src/main/java/de/tum/cit/aet/artemis/lecture/web/TextUnitResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/lecture/web/TextUnitResource.java @@ -4,7 +4,9 @@ import java.net.URI; import java.net.URISyntaxException; +import java.util.HashSet; import java.util.Optional; +import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -18,6 +20,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyLectureUnitLink; import de.tum.cit.aet.artemis.atlas.service.competency.CompetencyProgressService; import de.tum.cit.aet.artemis.core.exception.BadRequestAlertException; import de.tum.cit.aet.artemis.core.exception.EntityNotFoundException; @@ -28,6 +31,7 @@ import de.tum.cit.aet.artemis.lecture.domain.TextUnit; import de.tum.cit.aet.artemis.lecture.repository.LectureRepository; import de.tum.cit.aet.artemis.lecture.repository.TextUnitRepository; +import de.tum.cit.aet.artemis.lecture.service.LectureUnitService; @Profile(PROFILE_CORE) @RestController @@ -46,12 +50,15 @@ public class TextUnitResource { private final CompetencyProgressService competencyProgressService; + private final LectureUnitService lectureUnitService; + public TextUnitResource(LectureRepository lectureRepository, TextUnitRepository textUnitRepository, AuthorizationCheckService authorizationCheckService, - CompetencyProgressService competencyProgressService) { + CompetencyProgressService competencyProgressService, LectureUnitService lectureUnitService) { this.lectureRepository = lectureRepository; this.textUnitRepository = textUnitRepository; this.authorizationCheckService = authorizationCheckService; this.competencyProgressService = competencyProgressService; + this.lectureUnitService = lectureUnitService; } /** @@ -101,6 +108,7 @@ public ResponseEntity updateTextUnit(@PathVariable Long lectureId, @Re textUnitForm.setId(existingTextUnit.getId()); textUnitForm.setLecture(existingTextUnit.getLecture()); + lectureUnitService.reconnectCompetencyLectureUnitLinks(textUnitForm); TextUnit result = textUnitRepository.save(textUnitForm); competencyProgressService.updateProgressForUpdatedLearningObjectAsync(existingTextUnit, Optional.of(textUnitForm)); @@ -133,7 +141,15 @@ public ResponseEntity createTextUnit(@PathVariable Long lectureId, @Re // persist lecture unit before lecture to prevent "null index column for collection" error textUnit.setLecture(null); + // persist lecture unit before competency links to prevent error + Set links = textUnit.getCompetencyLinks(); + textUnit.setCompetencyLinks(new HashSet<>()); + textUnit = textUnitRepository.saveAndFlush(textUnit); + + textUnit.setCompetencyLinks(links); + lectureUnitService.reconnectCompetencyLectureUnitLinks(textUnit); + textUnit.setLecture(lecture); lecture.addLectureUnit(textUnit); Lecture updatedLecture = lectureRepository.save(lecture); diff --git a/src/main/java/de/tum/cit/aet/artemis/lecture/web/VideoUnitResource.java b/src/main/java/de/tum/cit/aet/artemis/lecture/web/VideoUnitResource.java index ae67b82c02e1..422eb4c9baa7 100644 --- a/src/main/java/de/tum/cit/aet/artemis/lecture/web/VideoUnitResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/lecture/web/VideoUnitResource.java @@ -4,7 +4,9 @@ import java.net.URI; import java.net.URISyntaxException; +import java.util.HashSet; import java.util.Optional; +import java.util.Set; import jakarta.ws.rs.BadRequestException; @@ -20,6 +22,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyLectureUnitLink; import de.tum.cit.aet.artemis.atlas.service.competency.CompetencyProgressService; import de.tum.cit.aet.artemis.core.exception.BadRequestAlertException; import de.tum.cit.aet.artemis.core.security.Role; @@ -97,6 +100,7 @@ public ResponseEntity updateVideoUnit(@PathVariable Long lectureId, @ lectureUnitService.validateUrlStringAndReturnUrl(videoUnit.getSource()); authorizationCheckService.checkHasAtLeastRoleInCourseElseThrow(Role.EDITOR, videoUnit.getLecture().getCourse(), null); + lectureUnitService.reconnectCompetencyLectureUnitLinks(videoUnit); VideoUnit result = videoUnitRepository.save(videoUnit); competencyProgressService.updateProgressForUpdatedLearningObjectAsync(existingVideoUnit, Optional.of(videoUnit)); @@ -131,7 +135,14 @@ public ResponseEntity createVideoUnit(@PathVariable Long lectureId, @ // persist lecture unit before lecture to prevent "null index column for collection" error videoUnit.setLecture(null); + // persist lecture unit before competency links to prevent error + Set links = videoUnit.getCompetencyLinks(); + videoUnit.setCompetencyLinks(new HashSet<>()); + videoUnit = videoUnitRepository.saveAndFlush(videoUnit); + videoUnit.setCompetencyLinks(links); + lectureUnitService.reconnectCompetencyLectureUnitLinks(videoUnit); + videoUnit.setLecture(lecture); lecture.addLectureUnit(videoUnit); Lecture updatedLecture = lectureRepository.save(lecture); diff --git a/src/test/java/de/tum/cit/aet/artemis/atlas/AbstractAtlasIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/atlas/AbstractAtlasIntegrationTest.java index 24bb9f9bf0d8..020d68eae4f2 100644 --- a/src/test/java/de/tum/cit/aet/artemis/atlas/AbstractAtlasIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/atlas/AbstractAtlasIntegrationTest.java @@ -9,9 +9,7 @@ import de.tum.cit.aet.artemis.atlas.competency.util.PrerequisiteUtilService; import de.tum.cit.aet.artemis.atlas.competency.util.StandardizedCompetencyUtilService; import de.tum.cit.aet.artemis.atlas.learningpath.util.LearningPathUtilService; -import de.tum.cit.aet.artemis.atlas.repository.CompetencyExerciseLinkRepository; import de.tum.cit.aet.artemis.atlas.repository.CompetencyJolRepository; -import de.tum.cit.aet.artemis.atlas.repository.CompetencyLectureUnitLinkRepository; import de.tum.cit.aet.artemis.atlas.repository.CompetencyRelationRepository; import de.tum.cit.aet.artemis.atlas.repository.CompetencyRepository; import de.tum.cit.aet.artemis.atlas.repository.CourseCompetencyRepository; @@ -21,6 +19,8 @@ import de.tum.cit.aet.artemis.atlas.repository.SourceRepository; import de.tum.cit.aet.artemis.atlas.repository.StandardizedCompetencyRepository; import de.tum.cit.aet.artemis.atlas.service.competency.CompetencyProgressService; +import de.tum.cit.aet.artemis.atlas.test_repository.CompetencyExerciseLinkTestRepository; +import de.tum.cit.aet.artemis.atlas.test_repository.CompetencyLectureUnitLinkTestRepository; import de.tum.cit.aet.artemis.atlas.test_repository.CompetencyProgressTestRepository; import de.tum.cit.aet.artemis.atlas.test_repository.LearningPathTestRepository; import de.tum.cit.aet.artemis.atlas.test_repository.ScienceEventTestRepository; @@ -82,10 +82,10 @@ public abstract class AbstractAtlasIntegrationTest extends AbstractSpringIntegra protected CompetencyJolRepository competencyJolRepository; @Autowired - protected CompetencyExerciseLinkRepository competencyExerciseLinkRepository; + protected CompetencyExerciseLinkTestRepository competencyExerciseLinkRepository; @Autowired - protected CompetencyLectureUnitLinkRepository competencyLectureUnitLinkRepository; + protected CompetencyLectureUnitLinkTestRepository competencyLectureUnitLinkRepository; // External Repositories @Autowired diff --git a/src/test/java/de/tum/cit/aet/artemis/atlas/competency/CourseCompetencyIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/atlas/competency/CourseCompetencyIntegrationTest.java index 924f05a14a56..aec786434586 100644 --- a/src/test/java/de/tum/cit/aet/artemis/atlas/competency/CourseCompetencyIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/atlas/competency/CourseCompetencyIntegrationTest.java @@ -7,7 +7,6 @@ import java.time.ZonedDateTime; import java.util.List; import java.util.Set; -import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; @@ -92,16 +91,17 @@ private Result createTextExerciseParticipationSubmissionAndResult(TextExercise e return createExerciseParticipationSubmissionAndResult(exercise, studentParticipation, pointsOfExercise, bonusPointsOfExercise, scoreAwarded, rated, TextSubmission::new, 1); } - private ProgrammingExercise createProgrammingExercise(int i, Set competencies) { + private ProgrammingExercise createProgrammingExercise(int i, CourseCompetency competency) { ProgrammingExercise programmingExercise = ProgrammingExerciseFactory.generateProgrammingExercise(null, null, course); programmingExercise.setMaxPoints(i * 10.0); - Set competencyExerciseLinks = competencies.stream().map(competency -> new CompetencyExerciseLink(competency, programmingExercise, 1)) - .collect(Collectors.toSet()); - programmingExercise.setCompetencyLinks(competencyExerciseLinks); programmingExercise.setDifficulty(i == 1 ? DifficultyLevel.EASY : i == 2 ? DifficultyLevel.MEDIUM : DifficultyLevel.HARD); programmingExerciseBuildConfigRepository.save(programmingExercise.getBuildConfig()); - return programmingExerciseRepository.save(programmingExercise); + programmingExercise = programmingExerciseRepository.save(programmingExercise); + + CompetencyExerciseLink link = new CompetencyExerciseLink(competency, programmingExercise, 1); + programmingExercise = (ProgrammingExercise) competencyExerciseLinkRepository.save(link).getExercise(); + return programmingExercise; } private Result createProgrammingExerciseParticipationSubmissionAndResult(ProgrammingExercise exercise, Participant participant, long scoreAwarded, boolean rated, @@ -278,7 +278,7 @@ class GetCompetencyStudentProgress { @BeforeEach void setupTestScenario() { - programmingExercises = IntStream.range(1, 4).mapToObj(i -> createProgrammingExercise(i, Set.of(courseCompetency))).toArray(ProgrammingExercise[]::new); + programmingExercises = IntStream.range(1, 4).mapToObj(i -> createProgrammingExercise(i, courseCompetency)).toArray(ProgrammingExercise[]::new); } @Test diff --git a/src/test/java/de/tum/cit/aet/artemis/atlas/competency/util/CompetencyUtilService.java b/src/test/java/de/tum/cit/aet/artemis/atlas/competency/util/CompetencyUtilService.java index 0b7cbc5e08d3..e76ef8505ba8 100644 --- a/src/test/java/de/tum/cit/aet/artemis/atlas/competency/util/CompetencyUtilService.java +++ b/src/test/java/de/tum/cit/aet/artemis/atlas/competency/util/CompetencyUtilService.java @@ -14,17 +14,15 @@ import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyRelation; import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyTaxonomy; import de.tum.cit.aet.artemis.atlas.domain.competency.RelationType; -import de.tum.cit.aet.artemis.atlas.repository.CompetencyExerciseLinkRepository; import de.tum.cit.aet.artemis.atlas.repository.CompetencyJolRepository; -import de.tum.cit.aet.artemis.atlas.repository.CompetencyLectureUnitLinkRepository; import de.tum.cit.aet.artemis.atlas.repository.CompetencyRelationRepository; import de.tum.cit.aet.artemis.atlas.repository.CompetencyRepository; +import de.tum.cit.aet.artemis.atlas.test_repository.CompetencyExerciseLinkTestRepository; +import de.tum.cit.aet.artemis.atlas.test_repository.CompetencyLectureUnitLinkTestRepository; import de.tum.cit.aet.artemis.core.domain.Course; import de.tum.cit.aet.artemis.core.domain.User; import de.tum.cit.aet.artemis.exercise.domain.Exercise; -import de.tum.cit.aet.artemis.exercise.repository.ExerciseTestRepository; import de.tum.cit.aet.artemis.lecture.domain.LectureUnit; -import de.tum.cit.aet.artemis.lecture.repository.LectureUnitRepository; /** * Service responsible for initializing the database with specific test data related to competencies for use in integration tests. @@ -35,12 +33,6 @@ public class CompetencyUtilService { @Autowired private CompetencyRepository competencyRepo; - @Autowired - private LectureUnitRepository lectureUnitRepository; - - @Autowired - private ExerciseTestRepository exerciseRepository; - @Autowired private CompetencyRelationRepository competencyRelationRepository; @@ -48,10 +40,10 @@ public class CompetencyUtilService { private CompetencyJolRepository competencyJOLRepository; @Autowired - private CompetencyExerciseLinkRepository competencyExerciseLinkRepository; + private CompetencyExerciseLinkTestRepository competencyExerciseLinkRepository; @Autowired - private CompetencyLectureUnitLinkRepository competencyLectureUnitLinkRepository; + private CompetencyLectureUnitLinkTestRepository competencyLectureUnitLinkRepository; /** * Creates and saves a Competency for the given Course. diff --git a/src/test/java/de/tum/cit/aet/artemis/atlas/test_repository/CompetencyExerciseLinkTestRepository.java b/src/test/java/de/tum/cit/aet/artemis/atlas/test_repository/CompetencyExerciseLinkTestRepository.java new file mode 100644 index 000000000000..4da793bd2392 --- /dev/null +++ b/src/test/java/de/tum/cit/aet/artemis/atlas/test_repository/CompetencyExerciseLinkTestRepository.java @@ -0,0 +1,17 @@ +package de.tum.cit.aet.artemis.atlas.test_repository; + +import org.springframework.context.annotation.Primary; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; + +import de.tum.cit.aet.artemis.atlas.repository.CompetencyExerciseLinkRepository; + +@Repository +@Primary +public interface CompetencyExerciseLinkTestRepository extends CompetencyExerciseLinkRepository { + + @Modifying + @Transactional + void deleteAllByExerciseId(long exerciseId); +} diff --git a/src/test/java/de/tum/cit/aet/artemis/atlas/test_repository/CompetencyLectureUnitLinkTestRepository.java b/src/test/java/de/tum/cit/aet/artemis/atlas/test_repository/CompetencyLectureUnitLinkTestRepository.java new file mode 100644 index 000000000000..02ce40401332 --- /dev/null +++ b/src/test/java/de/tum/cit/aet/artemis/atlas/test_repository/CompetencyLectureUnitLinkTestRepository.java @@ -0,0 +1,18 @@ +package de.tum.cit.aet.artemis.atlas.test_repository; + +import org.springframework.context.annotation.Primary; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; + +import de.tum.cit.aet.artemis.atlas.repository.CompetencyLectureUnitLinkRepository; + +@Repository +@Primary +public interface CompetencyLectureUnitLinkTestRepository extends CompetencyLectureUnitLinkRepository { + + @Modifying + @Transactional + void deleteAllByLectureUnitId(long lectureUnitId); + +} diff --git a/src/test/java/de/tum/cit/aet/artemis/core/MetricsIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/core/MetricsIntegrationTest.java index 0a85177005eb..deec30122c03 100644 --- a/src/test/java/de/tum/cit/aet/artemis/core/MetricsIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/core/MetricsIntegrationTest.java @@ -312,8 +312,7 @@ void shouldReturnLectureUnitInformation() throws Exception { final var lectureUnit = lectureUtilService.createTextUnit(); Competency competency = competencyUtilService.createCompetency(course); - lectureUnitService.linkLectureUnitsToCompetency(competencyUtilService.createCompetency(course), Set.of(new CompetencyLectureUnitLink(competency, lectureUnit, 1)), - Set.of()); + lectureUnitService.linkLectureUnitsToCompetency(competencyUtilService.createCompetency(course), Set.of(new CompetencyLectureUnitLink(competency, lectureUnit, 1))); final var testLecture = lectureUtilService.createLecture(course, null); lectureUtilService.addLectureUnitsToLecture(testLecture, List.of(lectureUnit)); diff --git a/src/test/java/de/tum/cit/aet/artemis/fileupload/AbstractFileUploadIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/fileupload/AbstractFileUploadIntegrationTest.java index 6c6ff7719640..b77a7fadb82e 100644 --- a/src/test/java/de/tum/cit/aet/artemis/fileupload/AbstractFileUploadIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/fileupload/AbstractFileUploadIntegrationTest.java @@ -7,6 +7,7 @@ import de.tum.cit.aet.artemis.assessment.repository.GradingCriterionRepository; import de.tum.cit.aet.artemis.assessment.util.ComplaintUtilService; import de.tum.cit.aet.artemis.atlas.competency.util.CompetencyUtilService; +import de.tum.cit.aet.artemis.atlas.test_repository.CompetencyExerciseLinkTestRepository; import de.tum.cit.aet.artemis.communication.repository.conversation.ChannelRepository; import de.tum.cit.aet.artemis.core.util.PageableSearchUtilService; import de.tum.cit.aet.artemis.exam.repository.ExamRepository; @@ -52,6 +53,9 @@ public class AbstractFileUploadIntegrationTest extends AbstractSpringIntegration @Autowired protected ParticipationTestRepository participationRepository; + @Autowired + protected CompetencyExerciseLinkTestRepository competencyExerciseLinkRepository; + // Services @Autowired protected FileUploadSubmissionRepository fileUploadSubmissionRepository; diff --git a/src/test/java/de/tum/cit/aet/artemis/fileupload/FileUploadExerciseIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/fileupload/FileUploadExerciseIntegrationTest.java index d68555e2e5da..0f3131b3aa84 100644 --- a/src/test/java/de/tum/cit/aet/artemis/fileupload/FileUploadExerciseIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/fileupload/FileUploadExerciseIntegrationTest.java @@ -328,8 +328,8 @@ void testDeleteFileUploadExerciseWithChannel() throws Exception { @Test @WithMockUser(username = TEST_PREFIX + "instructor1", roles = "INSTRUCTOR") void testDeleteFileUploadExerciseWithCompetency() throws Exception { - fileUploadExercise.setCompetencyLinks(Set.of(new CompetencyExerciseLink(competency, fileUploadExercise, 1))); fileUploadExercise = fileUploadExerciseRepository.save(fileUploadExercise); + competencyExerciseLinkRepository.save(new CompetencyExerciseLink(competency, fileUploadExercise, 1)); request.delete("/api/file-upload-exercises/" + fileUploadExercise.getId(), HttpStatus.OK); verify(competencyProgressService).updateProgressByCompetencyAsync(eq(competency)); diff --git a/src/test/java/de/tum/cit/aet/artemis/lecture/AttachmentUnitIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/lecture/AttachmentUnitIntegrationTest.java index 9806b26b8737..74e7754cdec8 100644 --- a/src/test/java/de/tum/cit/aet/artemis/lecture/AttachmentUnitIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/lecture/AttachmentUnitIntegrationTest.java @@ -226,11 +226,12 @@ void createAttachmentUnit_asInstructor_shouldCreateAttachmentUnit() throws Excep assertThat(persistedAttachmentUnit.getId()).isNotNull(); var persistedAttachment = persistedAttachmentUnit.getAttachment(); assertThat(persistedAttachment.getId()).isNotNull(); - var updatedAttachmentUnit = attachmentUnitRepository.findById(persistedAttachmentUnit.getId()).orElseThrow(); + var updatedAttachmentUnit = attachmentUnitRepository.findOneWithCompetencyLinksById(persistedAttachmentUnit.getId()); // Wait for async operation to complete (after attachment unit is saved, the file gets split into slides) await().untilAsserted(() -> assertThat(slideRepository.findAllByAttachmentUnitId(persistedAttachmentUnit.getId())).hasSize(SLIDE_COUNT)); assertThat(updatedAttachmentUnit.getAttachment()).isEqualTo(persistedAttachment); assertThat(updatedAttachmentUnit.getAttachment().getName()).isEqualTo("LoremIpsum"); + assertThat(updatedAttachmentUnit.getCompetencyLinks()).anyMatch(link -> link.getCompetency().getId().equals(competency.getId())); verify(competencyProgressService).updateProgressByLearningObjectAsync(eq(updatedAttachmentUnit)); } diff --git a/src/test/java/de/tum/cit/aet/artemis/lecture/OnlineUnitIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/lecture/OnlineUnitIntegrationTest.java index bcc97f02037a..53f7c139f6d9 100644 --- a/src/test/java/de/tum/cit/aet/artemis/lecture/OnlineUnitIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/lecture/OnlineUnitIntegrationTest.java @@ -171,10 +171,14 @@ void updateOnlineUnit_asInstructor_shouldKeepOrdering() throws Exception { } private void persistOnlineUnitWithLecture() { - this.onlineUnit = onlineUnitRepository.save(this.onlineUnit); - lecture1.addLectureUnit(this.onlineUnit); + Set links = onlineUnit.getCompetencyLinks(); + onlineUnit.setCompetencyLinks(null); + + onlineUnit = onlineUnitRepository.save(onlineUnit); + onlineUnit.setCompetencyLinks(links); + lecture1.addLectureUnit(onlineUnit); lecture1 = lectureRepository.save(lecture1); - this.onlineUnit = (OnlineUnit) lectureRepository.findByIdWithLectureUnitsAndAttachmentsElseThrow(lecture1.getId()).getLectureUnits().stream().findFirst().orElseThrow(); + onlineUnit = (OnlineUnit) lectureRepository.findByIdWithLectureUnitsAndAttachmentsElseThrow(lecture1.getId()).getLectureUnits().stream().findFirst().orElseThrow(); } @Test diff --git a/src/test/java/de/tum/cit/aet/artemis/lecture/TextUnitIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/lecture/TextUnitIntegrationTest.java index b906b3a00e94..0493cd108136 100644 --- a/src/test/java/de/tum/cit/aet/artemis/lecture/TextUnitIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/lecture/TextUnitIntegrationTest.java @@ -109,9 +109,8 @@ void createTextUnit_EditorNotInCourse_shouldReturnForbidden() throws Exception { void updateTextUnit_asEditor_shouldUpdateTextUnit() throws Exception { textUnit.setCompetencyLinks(Set.of(new CompetencyLectureUnitLink(competency, textUnit, 1))); persistTextUnitWithLecture(); - TextUnit textUnitFromRequest = request.get("/api/lectures/" + lecture.getId() + "/text-units/" + this.textUnit.getId(), HttpStatus.OK, TextUnit.class); - textUnitFromRequest.setContent("Changed"); - TextUnit updatedTextUnit = request.putWithResponseBody("/api/lectures/" + lecture.getId() + "/text-units", textUnitFromRequest, TextUnit.class, HttpStatus.OK); + textUnit.setContent("Changed"); + TextUnit updatedTextUnit = request.putWithResponseBody("/api/lectures/" + lecture.getId() + "/text-units", textUnit, TextUnit.class, HttpStatus.OK); assertThat(updatedTextUnit.getContent()).isEqualTo("Changed"); verify(competencyProgressService, timeout(1000).times(1)).updateProgressForUpdatedLearningObjectAsync(eq(textUnit), eq(Optional.of(textUnit))); } @@ -166,10 +165,14 @@ void deleteTextUnit_correctId_shouldDeleteTextUnit() throws Exception { } private void persistTextUnitWithLecture() { - this.textUnit = textUnitRepository.save(this.textUnit); + Set link = textUnit.getCompetencyLinks(); + textUnit.setCompetencyLinks(null); + + textUnit = textUnitRepository.save(textUnit); + textUnit.setCompetencyLinks(link); lecture = lectureRepository.findByIdWithLectureUnitsAndAttachments(lecture.getId()).orElseThrow(); - lecture.addLectureUnit(this.textUnit); + lecture.addLectureUnit(textUnit); lecture = lectureRepository.save(lecture); - this.textUnit = (TextUnit) lectureRepository.findByIdWithLectureUnitsAndAttachments(lecture.getId()).orElseThrow().getLectureUnits().stream().findFirst().orElseThrow(); + textUnit = (TextUnit) lectureRepository.findByIdWithLectureUnitsAndAttachments(lecture.getId()).orElseThrow().getLectureUnits().stream().findFirst().orElseThrow(); } } diff --git a/src/test/java/de/tum/cit/aet/artemis/lecture/VideoUnitIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/lecture/VideoUnitIntegrationTest.java index 02313677828d..2c43ed5c8106 100644 --- a/src/test/java/de/tum/cit/aet/artemis/lecture/VideoUnitIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/lecture/VideoUnitIntegrationTest.java @@ -158,10 +158,14 @@ void updateVideoUnit_asInstructor_shouldKeepOrdering() throws Exception { } private void persistVideoUnitWithLecture() { - this.videoUnit = videoUnitRepository.save(this.videoUnit); - lecture1.addLectureUnit(this.videoUnit); + Set link = videoUnit.getCompetencyLinks(); + videoUnit.setCompetencyLinks(null); + + videoUnit = videoUnitRepository.save(videoUnit); + videoUnit.setCompetencyLinks(link); + lecture1.addLectureUnit(videoUnit); lecture1 = lectureRepository.save(lecture1); - this.videoUnit = (VideoUnit) lectureRepository.findByIdWithLectureUnitsAndAttachments(lecture1.getId()).orElseThrow().getLectureUnits().stream().findFirst().orElseThrow(); + videoUnit = (VideoUnit) lectureRepository.findByIdWithLectureUnitsAndAttachments(lecture1.getId()).orElseThrow().getLectureUnits().stream().findFirst().orElseThrow(); } @Test diff --git a/src/test/java/de/tum/cit/aet/artemis/modeling/ModelingExerciseIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/modeling/ModelingExerciseIntegrationTest.java index c1beaadf0278..f1a8f13d81a0 100644 --- a/src/test/java/de/tum/cit/aet/artemis/modeling/ModelingExerciseIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/modeling/ModelingExerciseIntegrationTest.java @@ -474,7 +474,7 @@ void importModelingExerciseFromCourseToCourse() throws Exception { modelingExerciseToImport.setCourse(course2); String uniqueChannelName = "channel-" + UUID.randomUUID().toString().substring(0, 8); modelingExerciseToImport.setChannelName(uniqueChannelName); - modelingExerciseToImport.setCompetencyLinks(Set.of(new CompetencyExerciseLink(competency, modelingExerciseToImport, 1))); + modelingExerciseToImport.getCompetencyLinks().add(new CompetencyExerciseLink(competency, modelingExerciseToImport, 1)); var importedExercise = request.postWithResponseBody("/api/modeling-exercises/import/" + modelingExerciseToImport.getId(), modelingExerciseToImport, ModelingExercise.class, HttpStatus.CREATED); diff --git a/src/test/javascript/spec/component/shared/competency-selection.component.spec.ts b/src/test/javascript/spec/component/shared/competency-selection.component.spec.ts index d90a9cf4c747..a58b665e45cc 100644 --- a/src/test/javascript/spec/component/shared/competency-selection.component.spec.ts +++ b/src/test/javascript/spec/component/shared/competency-selection.component.spec.ts @@ -146,12 +146,12 @@ describe('CompetencySelection', () => { component.toggleCompetency(new CompetencyLearningObjectLink(competency3, 1)); expect(component.selectedCompetencyLinks).toHaveLength(3); - expect(component.selectedCompetencyLinks).toContain(competency3); + expect(component.selectedCompetencyLinks).toContainEqual(new CompetencyLearningObjectLink(competency3, 1)); component.toggleCompetency(new CompetencyLearningObjectLink(competency2, 1)); expect(component.selectedCompetencyLinks).toHaveLength(2); - expect(component.selectedCompetencyLinks).not.toContain(competency2); + expect(component.selectedCompetencyLinks).not.toContainEqual(new CompetencyLearningObjectLink(competency2, 1)); component.toggleCompetency(new CompetencyLearningObjectLink(competency1, 1)); component.toggleCompetency(new CompetencyLearningObjectLink(competency3, 1)); From 31458509570931d4efd71ff14493545a704d23db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20St=C3=B6hr?= Date: Mon, 21 Oct 2024 15:34:37 +0200 Subject: [PATCH 07/25] Fix more tests --- .../competency/CompetencyExerciseLink.java | 4 ---- .../competency/CompetencyLectureUnitLink.java | 4 ---- .../CompetencyExerciseLinkRepository.java | 4 ++++ .../CompetencyLectureUnitLinkRepository.java | 4 ++++ .../repository/AttachmentUnitRepository.java | 5 ----- .../atlas/service/LearningPathServiceTest.java | 6 ++++-- .../lecture/AttachmentUnitIntegrationTest.java | 4 ++-- .../AttachmentUnitsIntegrationTest.java | 4 ++-- .../AttachmentUnitTestRepository.java | 18 ++++++++++++++++++ .../lecture/util/LectureUtilService.java | 4 ++-- 10 files changed, 36 insertions(+), 21 deletions(-) create mode 100644 src/test/java/de/tum/cit/aet/artemis/lecture/test_repository/AttachmentUnitTestRepository.java diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyExerciseLink.java b/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyExerciseLink.java index 4a8104e7e891..4fd40c80734a 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyExerciseLink.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyExerciseLink.java @@ -53,10 +53,6 @@ public CompetencyExerciseId getId() { return id; } - public void setId(CompetencyExerciseId id) { - this.id = id; - } - @Override public boolean equals(Object o) { if (this == o) { diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLectureUnitLink.java b/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLectureUnitLink.java index 49541489835f..9a422fe002ca 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLectureUnitLink.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLectureUnitLink.java @@ -53,10 +53,6 @@ public CompetencyLectureUnitId getId() { return id; } - public void setId(CompetencyLectureUnitId id) { - this.id = id; - } - @Override public boolean equals(Object o) { if (this == o) { diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyExerciseLinkRepository.java b/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyExerciseLinkRepository.java index bf60360f9022..d19675ca6412 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyExerciseLinkRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyExerciseLinkRepository.java @@ -1,10 +1,14 @@ package de.tum.cit.aet.artemis.atlas.repository; +import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE; + +import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Repository; import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyExerciseLink; import de.tum.cit.aet.artemis.core.repository.base.ArtemisJpaRepository; +@Profile(PROFILE_CORE) @Repository public interface CompetencyExerciseLinkRepository extends ArtemisJpaRepository { diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyLectureUnitLinkRepository.java b/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyLectureUnitLinkRepository.java index 5086fe5eb3a8..19377a3cdfd8 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyLectureUnitLinkRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyLectureUnitLinkRepository.java @@ -1,5 +1,8 @@ package de.tum.cit.aet.artemis.atlas.repository; +import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE; + +import org.springframework.context.annotation.Profile; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; @@ -9,6 +12,7 @@ import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyLectureUnitLink; import de.tum.cit.aet.artemis.core.repository.base.ArtemisJpaRepository; +@Profile(PROFILE_CORE) @Repository public interface CompetencyLectureUnitLinkRepository extends ArtemisJpaRepository { diff --git a/src/main/java/de/tum/cit/aet/artemis/lecture/repository/AttachmentUnitRepository.java b/src/main/java/de/tum/cit/aet/artemis/lecture/repository/AttachmentUnitRepository.java index 56363cb84e03..61f268d75b95 100644 --- a/src/main/java/de/tum/cit/aet/artemis/lecture/repository/AttachmentUnitRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/lecture/repository/AttachmentUnitRepository.java @@ -1,14 +1,12 @@ package de.tum.cit.aet.artemis.lecture.repository; import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE; -import static org.springframework.data.jpa.repository.EntityGraph.EntityGraphType.LOAD; import java.util.List; import jakarta.validation.constraints.NotNull; import org.springframework.context.annotation.Profile; -import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; @@ -65,7 +63,4 @@ default List findAllByLectureIdAndAttachmentTypeElseThrow(Long l WHERE attachmentUnit.id = :attachmentUnitId """) AttachmentUnit findOneWithSlidesAndCompetencies(@Param("attachmentUnitId") long attachmentUnitId); - - @EntityGraph(type = LOAD, attributePaths = "competencyLinks") - AttachmentUnit findOneWithCompetencyLinksById(long attachmentUnitId); } diff --git a/src/test/java/de/tum/cit/aet/artemis/atlas/service/LearningPathServiceTest.java b/src/test/java/de/tum/cit/aet/artemis/atlas/service/LearningPathServiceTest.java index 471d3752c728..e69d27b7e80a 100644 --- a/src/test/java/de/tum/cit/aet/artemis/atlas/service/LearningPathServiceTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/atlas/service/LearningPathServiceTest.java @@ -168,8 +168,9 @@ void testCompetencyWithLectureUnitAndExercise() { final var lectureUnit = lectureUtilService.createTextUnit(); lectureUtilService.addLectureUnitsToLecture(lecture, List.of(lectureUnit)); competencyUtilService.linkLectureUnitToCompetency(competency, lectureUnit); - final var exercise = programmingExerciseUtilService.addProgrammingExerciseToCourse(course); + var exercise = programmingExerciseUtilService.addProgrammingExerciseToCourse(course); exercise.setReleaseDate(null); + exercise = exerciseRepository.save(exercise); competencyUtilService.linkExerciseToCompetency(competency, exercise); final var startNodeId = LearningPathNgxService.getCompetencyStartNodeId(competency.getId()); final var endNodeId = LearningPathNgxService.getCompetencyEndNodeId(competency.getId()); @@ -504,6 +505,7 @@ void testCompetencyWithLectureUnitAndExercise() { generateLectureUnits(1); generateExercises(1); + exerciseRepository.save(exercises[0]); addNodes(lectureUnits); addNodes(exercises); @@ -600,8 +602,8 @@ 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]); + exercises[i].setReleaseDate(null); } } diff --git a/src/test/java/de/tum/cit/aet/artemis/lecture/AttachmentUnitIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/lecture/AttachmentUnitIntegrationTest.java index 74e7754cdec8..d127e1d40db4 100644 --- a/src/test/java/de/tum/cit/aet/artemis/lecture/AttachmentUnitIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/lecture/AttachmentUnitIntegrationTest.java @@ -45,8 +45,8 @@ import de.tum.cit.aet.artemis.lecture.domain.LectureUnit; import de.tum.cit.aet.artemis.lecture.domain.Slide; import de.tum.cit.aet.artemis.lecture.repository.AttachmentRepository; -import de.tum.cit.aet.artemis.lecture.repository.AttachmentUnitRepository; import de.tum.cit.aet.artemis.lecture.repository.LectureRepository; +import de.tum.cit.aet.artemis.lecture.test_repository.AttachmentUnitTestRepository; import de.tum.cit.aet.artemis.lecture.test_repository.SlideTestRepository; import de.tum.cit.aet.artemis.lecture.util.LectureFactory; import de.tum.cit.aet.artemis.lecture.util.LectureUtilService; @@ -62,7 +62,7 @@ class AttachmentUnitIntegrationTest extends AbstractSpringIntegrationIndependent private AttachmentRepository attachmentRepository; @Autowired - private AttachmentUnitRepository attachmentUnitRepository; + private AttachmentUnitTestRepository attachmentUnitRepository; @Autowired private LectureRepository lectureRepository; diff --git a/src/test/java/de/tum/cit/aet/artemis/lecture/AttachmentUnitsIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/lecture/AttachmentUnitsIntegrationTest.java index 664b1b3d5bc6..07ba794e77bc 100644 --- a/src/test/java/de/tum/cit/aet/artemis/lecture/AttachmentUnitsIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/lecture/AttachmentUnitsIntegrationTest.java @@ -32,8 +32,8 @@ import de.tum.cit.aet.artemis.lecture.domain.Lecture; import de.tum.cit.aet.artemis.lecture.dto.LectureUnitSplitDTO; import de.tum.cit.aet.artemis.lecture.dto.LectureUnitSplitInformationDTO; -import de.tum.cit.aet.artemis.lecture.repository.AttachmentUnitRepository; import de.tum.cit.aet.artemis.lecture.service.LectureUnitProcessingService; +import de.tum.cit.aet.artemis.lecture.test_repository.AttachmentUnitTestRepository; import de.tum.cit.aet.artemis.lecture.test_repository.SlideTestRepository; import de.tum.cit.aet.artemis.lecture.util.LectureUtilService; import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationIndependentTest; @@ -43,7 +43,7 @@ class AttachmentUnitsIntegrationTest extends AbstractSpringIntegrationIndependen private static final String TEST_PREFIX = "attachmentunitsintegrationtest"; @Autowired - private AttachmentUnitRepository attachmentUnitRepository; + private AttachmentUnitTestRepository attachmentUnitRepository; @Autowired private SlideTestRepository slideRepository; diff --git a/src/test/java/de/tum/cit/aet/artemis/lecture/test_repository/AttachmentUnitTestRepository.java b/src/test/java/de/tum/cit/aet/artemis/lecture/test_repository/AttachmentUnitTestRepository.java new file mode 100644 index 000000000000..776e889c6942 --- /dev/null +++ b/src/test/java/de/tum/cit/aet/artemis/lecture/test_repository/AttachmentUnitTestRepository.java @@ -0,0 +1,18 @@ +package de.tum.cit.aet.artemis.lecture.test_repository; + +import static org.springframework.data.jpa.repository.EntityGraph.EntityGraphType.LOAD; + +import org.springframework.context.annotation.Primary; +import org.springframework.data.jpa.repository.EntityGraph; +import org.springframework.stereotype.Repository; + +import de.tum.cit.aet.artemis.lecture.domain.AttachmentUnit; +import de.tum.cit.aet.artemis.lecture.repository.AttachmentUnitRepository; + +@Repository +@Primary +public interface AttachmentUnitTestRepository extends AttachmentUnitRepository { + + @EntityGraph(type = LOAD, attributePaths = "competencyLinks") + AttachmentUnit findOneWithCompetencyLinksById(long attachmentUnitId); +} diff --git a/src/test/java/de/tum/cit/aet/artemis/lecture/util/LectureUtilService.java b/src/test/java/de/tum/cit/aet/artemis/lecture/util/LectureUtilService.java index ccbd304b6099..541593bfc3a8 100644 --- a/src/test/java/de/tum/cit/aet/artemis/lecture/util/LectureUtilService.java +++ b/src/test/java/de/tum/cit/aet/artemis/lecture/util/LectureUtilService.java @@ -38,7 +38,6 @@ import de.tum.cit.aet.artemis.lecture.domain.TextUnit; import de.tum.cit.aet.artemis.lecture.domain.VideoUnit; import de.tum.cit.aet.artemis.lecture.repository.AttachmentRepository; -import de.tum.cit.aet.artemis.lecture.repository.AttachmentUnitRepository; import de.tum.cit.aet.artemis.lecture.repository.ExerciseUnitRepository; import de.tum.cit.aet.artemis.lecture.repository.LectureRepository; import de.tum.cit.aet.artemis.lecture.repository.LectureUnitCompletionRepository; @@ -46,6 +45,7 @@ import de.tum.cit.aet.artemis.lecture.repository.OnlineUnitRepository; import de.tum.cit.aet.artemis.lecture.repository.TextUnitRepository; import de.tum.cit.aet.artemis.lecture.repository.VideoUnitRepository; +import de.tum.cit.aet.artemis.lecture.test_repository.AttachmentUnitTestRepository; import de.tum.cit.aet.artemis.lecture.test_repository.SlideTestRepository; import de.tum.cit.aet.artemis.text.domain.TextExercise; import de.tum.cit.aet.artemis.text.repository.TextExerciseRepository; @@ -76,7 +76,7 @@ public class LectureUtilService { private ExerciseUnitRepository exerciseUnitRepository; @Autowired - private AttachmentUnitRepository attachmentUnitRepository; + private AttachmentUnitTestRepository attachmentUnitRepository; @Autowired private AttachmentRepository attachmentRepository; From ff64ccc72044d1d672b8af6125245371e2f3ec0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20St=C3=B6hr?= Date: Mon, 21 Oct 2024 16:17:24 +0200 Subject: [PATCH 08/25] Fix more tests --- .../modeling/web/ModelingExerciseResource.java | 2 ++ .../service/ProgrammingExerciseService.java | 3 +++ .../quiz/service/QuizExerciseService.java | 17 ++++++++++++++++- .../artemis/quiz/web/QuizExerciseResource.java | 2 ++ .../artemis/text/web/TextExerciseResource.java | 2 ++ .../atlas/AbstractAtlasIntegrationTest.java | 4 ++-- .../aet/artemis/core/FileIntegrationTest.java | 4 ++-- 7 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/modeling/web/ModelingExerciseResource.java b/src/main/java/de/tum/cit/aet/artemis/modeling/web/ModelingExerciseResource.java index da9195b214d5..608ac3ca9ef5 100644 --- a/src/main/java/de/tum/cit/aet/artemis/modeling/web/ModelingExerciseResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/modeling/web/ModelingExerciseResource.java @@ -242,6 +242,8 @@ public ResponseEntity updateModelingExercise(@RequestBody Mode channelService.updateExerciseChannel(modelingExerciseBeforeUpdate, modelingExercise); + exerciseService.reconnectCompetencyExerciseLinks(modelingExercise); + ModelingExercise updatedModelingExercise = modelingExerciseRepository.save(modelingExercise); exerciseService.logUpdate(modelingExercise, modelingExercise.getCourseViaExerciseGroupOrCourseMember(), user); exerciseService.updatePointsInRelatedParticipantScores(modelingExerciseBeforeUpdate, updatedModelingExercise); diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseService.java index d4e7de493f52..e3e305fb20bb 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseService.java @@ -604,6 +604,9 @@ public ProgrammingExercise updateProgrammingExercise(ProgrammingExercise program String problemStatementWithTestNames = updatedProgrammingExercise.getProblemStatement(); programmingExerciseTaskService.replaceTestNamesWithIds(updatedProgrammingExercise); programmingExerciseBuildConfigRepository.save(updatedProgrammingExercise.getBuildConfig()); + + exerciseService.reconnectCompetencyExerciseLinks(updatedProgrammingExercise); + ProgrammingExercise savedProgrammingExercise = programmingExerciseRepository.save(updatedProgrammingExercise); // The returned value should use test case names since it gets send back to the client savedProgrammingExercise.setProblemStatement(problemStatementWithTestNames); diff --git a/src/main/java/de/tum/cit/aet/artemis/quiz/service/QuizExerciseService.java b/src/main/java/de/tum/cit/aet/artemis/quiz/service/QuizExerciseService.java index 8071c2c1eeb8..46651ea7a789 100644 --- a/src/main/java/de/tum/cit/aet/artemis/quiz/service/QuizExerciseService.java +++ b/src/main/java/de/tum/cit/aet/artemis/quiz/service/QuizExerciseService.java @@ -33,6 +33,7 @@ import de.tum.cit.aet.artemis.assessment.domain.Result; import de.tum.cit.aet.artemis.assessment.repository.ResultRepository; +import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyExerciseLink; import de.tum.cit.aet.artemis.core.config.Constants; import de.tum.cit.aet.artemis.core.domain.User; import de.tum.cit.aet.artemis.core.dto.SearchResultPageDTO; @@ -44,6 +45,7 @@ import de.tum.cit.aet.artemis.core.service.messaging.InstanceMessageSendService; import de.tum.cit.aet.artemis.core.util.PageUtil; import de.tum.cit.aet.artemis.exercise.domain.participation.StudentParticipation; +import de.tum.cit.aet.artemis.exercise.service.ExerciseService; import de.tum.cit.aet.artemis.exercise.service.ExerciseSpecificationService; import de.tum.cit.aet.artemis.quiz.domain.DragAndDropQuestion; import de.tum.cit.aet.artemis.quiz.domain.DragItem; @@ -83,10 +85,12 @@ public class QuizExerciseService extends QuizService { private final FileService fileService; + private final ExerciseService exerciseService; + public QuizExerciseService(QuizExerciseRepository quizExerciseRepository, ResultRepository resultRepository, QuizSubmissionRepository quizSubmissionRepository, InstanceMessageSendService instanceMessageSendService, QuizStatisticService quizStatisticService, QuizBatchService quizBatchService, ExerciseSpecificationService exerciseSpecificationService, FileService fileService, DragAndDropMappingRepository dragAndDropMappingRepository, - ShortAnswerMappingRepository shortAnswerMappingRepository) { + ShortAnswerMappingRepository shortAnswerMappingRepository, ExerciseService exerciseService) { super(dragAndDropMappingRepository, shortAnswerMappingRepository); this.quizExerciseRepository = quizExerciseRepository; this.resultRepository = resultRepository; @@ -96,6 +100,7 @@ public QuizExerciseService(QuizExerciseRepository quizExerciseRepository, Result this.quizBatchService = quizBatchService; this.exerciseSpecificationService = exerciseSpecificationService; this.fileService = fileService; + this.exerciseService = exerciseService; } /** @@ -429,8 +434,18 @@ public QuizExercise save(QuizExercise quizExercise) { // make sure the pointers in the statistics are correct quizExercise.recalculatePointCounters(); + // persist lecture unit before competency links to prevent error + Set links = quizExercise.getCompetencyLinks(); + quizExercise.setCompetencyLinks(new HashSet<>()); + QuizExercise savedQuizExercise = super.save(quizExercise); + if (!links.isEmpty()) { + savedQuizExercise.setCompetencyLinks(links); + exerciseService.reconnectCompetencyExerciseLinks(savedQuizExercise); + savedQuizExercise = quizExerciseRepository.saveAndFlush(savedQuizExercise); + } + if (savedQuizExercise.isCourseExercise()) { // only schedule quizzes for course exercises, not for exam exercises instanceMessageSendService.sendQuizExerciseStartSchedule(savedQuizExercise.getId()); diff --git a/src/main/java/de/tum/cit/aet/artemis/quiz/web/QuizExerciseResource.java b/src/main/java/de/tum/cit/aet/artemis/quiz/web/QuizExerciseResource.java index 118c5554a1f9..d9d276bb3bd4 100644 --- a/src/main/java/de/tum/cit/aet/artemis/quiz/web/QuizExerciseResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/quiz/web/QuizExerciseResource.java @@ -300,6 +300,8 @@ public ResponseEntity updateQuizExercise(@PathVariable Long exerci Channel updatedChannel = channelService.updateExerciseChannel(originalQuiz, quizExercise); + exerciseService.reconnectCompetencyExerciseLinks(quizExercise); + quizExercise = quizExerciseService.save(quizExercise); exerciseService.logUpdate(quizExercise, quizExercise.getCourseViaExerciseGroupOrCourseMember(), user); groupNotificationScheduleService.checkAndCreateAppropriateNotificationsWhenUpdatingExercise(originalQuiz, quizExercise, notificationText); diff --git a/src/main/java/de/tum/cit/aet/artemis/text/web/TextExerciseResource.java b/src/main/java/de/tum/cit/aet/artemis/text/web/TextExerciseResource.java index 8f98cd911c51..f59713f005eb 100644 --- a/src/main/java/de/tum/cit/aet/artemis/text/web/TextExerciseResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/text/web/TextExerciseResource.java @@ -278,6 +278,8 @@ public ResponseEntity updateTextExercise(@RequestBody TextExercise channelService.updateExerciseChannel(textExerciseBeforeUpdate, textExercise); + exerciseService.reconnectCompetencyExerciseLinks(textExercise); + TextExercise updatedTextExercise = textExerciseRepository.save(textExercise); exerciseService.logUpdate(updatedTextExercise, updatedTextExercise.getCourseViaExerciseGroupOrCourseMember(), user); exerciseService.updatePointsInRelatedParticipantScores(textExerciseBeforeUpdate, updatedTextExercise); diff --git a/src/test/java/de/tum/cit/aet/artemis/atlas/AbstractAtlasIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/atlas/AbstractAtlasIntegrationTest.java index 020d68eae4f2..c5a5e7fb07d1 100644 --- a/src/test/java/de/tum/cit/aet/artemis/atlas/AbstractAtlasIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/atlas/AbstractAtlasIntegrationTest.java @@ -30,12 +30,12 @@ import de.tum.cit.aet.artemis.exercise.service.ParticipationService; import de.tum.cit.aet.artemis.exercise.team.TeamUtilService; import de.tum.cit.aet.artemis.exercise.test_repository.SubmissionTestRepository; -import de.tum.cit.aet.artemis.lecture.repository.AttachmentUnitRepository; import de.tum.cit.aet.artemis.lecture.repository.ExerciseUnitRepository; import de.tum.cit.aet.artemis.lecture.repository.LectureRepository; import de.tum.cit.aet.artemis.lecture.repository.LectureUnitRepository; import de.tum.cit.aet.artemis.lecture.repository.TextUnitRepository; import de.tum.cit.aet.artemis.lecture.service.LectureUnitService; +import de.tum.cit.aet.artemis.lecture.test_repository.AttachmentUnitTestRepository; import de.tum.cit.aet.artemis.lecture.util.LectureUtilService; import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseBuildConfigRepository; import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestRepository; @@ -101,7 +101,7 @@ public abstract class AbstractAtlasIntegrationTest extends AbstractSpringIntegra protected TextUnitRepository textUnitRepository; @Autowired - protected AttachmentUnitRepository attachmentUnitRepository; + protected AttachmentUnitTestRepository attachmentUnitRepository; @Autowired protected ExerciseUnitRepository exerciseUnitRepository; diff --git a/src/test/java/de/tum/cit/aet/artemis/core/FileIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/core/FileIntegrationTest.java index 97b4923c2aef..8a8eb8c92ecb 100644 --- a/src/test/java/de/tum/cit/aet/artemis/core/FileIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/core/FileIntegrationTest.java @@ -35,9 +35,9 @@ import de.tum.cit.aet.artemis.lecture.domain.Lecture; import de.tum.cit.aet.artemis.lecture.domain.LectureUnit; import de.tum.cit.aet.artemis.lecture.repository.AttachmentRepository; -import de.tum.cit.aet.artemis.lecture.repository.AttachmentUnitRepository; import de.tum.cit.aet.artemis.lecture.repository.LectureRepository; import de.tum.cit.aet.artemis.lecture.repository.LectureUnitCompletionRepository; +import de.tum.cit.aet.artemis.lecture.test_repository.AttachmentUnitTestRepository; import de.tum.cit.aet.artemis.lecture.util.LectureFactory; import de.tum.cit.aet.artemis.lecture.util.LectureUtilService; import de.tum.cit.aet.artemis.shared.base.AbstractSpringIntegrationIndependentTest; @@ -50,7 +50,7 @@ class FileIntegrationTest extends AbstractSpringIntegrationIndependentTest { private AttachmentRepository attachmentRepo; @Autowired - private AttachmentUnitRepository attachmentUnitRepo; + private AttachmentUnitTestRepository attachmentUnitRepo; @Autowired private LectureUnitCompletionRepository lectureUnitCompletionRepository; From fe7318ad820788032d08b148fcf458eb318a0cc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20St=C3=B6hr?= Date: Mon, 21 Oct 2024 16:39:17 +0200 Subject: [PATCH 09/25] Fix exercise creation --- .../exercise/service/ExerciseService.java | 17 +++++++++++++++++ .../web/FileUploadExerciseResource.java | 2 +- .../web/ModelingExerciseResource.java | 2 +- .../service/ProgrammingExerciseService.java | 4 +++- .../quiz/service/QuizExerciseService.java | 19 ++++++------------- .../text/web/TextExerciseResource.java | 2 +- 6 files changed, 29 insertions(+), 17 deletions(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/exercise/service/ExerciseService.java b/src/main/java/de/tum/cit/aet/artemis/exercise/service/ExerciseService.java index c7afead50288..5d59ff05727a 100644 --- a/src/main/java/de/tum/cit/aet/artemis/exercise/service/ExerciseService.java +++ b/src/main/java/de/tum/cit/aet/artemis/exercise/service/ExerciseService.java @@ -14,6 +14,7 @@ import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.function.Function; import java.util.stream.Collectors; import jakarta.validation.constraints.NotNull; @@ -795,6 +796,22 @@ else if (now().plusMinutes(EXAM_START_WAIT_TIME_MINUTES).isAfter(originalExercis } } + public T createWithCompetencyLinks(T exercise, Function saveFunction) { + // persist exercise before linking it to the competency + Set links = exercise.getCompetencyLinks(); + exercise.setCompetencyLinks(new HashSet<>()); + + T savedExercise = saveFunction.apply(exercise); + + if (!links.isEmpty()) { + savedExercise.setCompetencyLinks(links); + reconnectCompetencyExerciseLinks(savedExercise); + savedExercise.setCompetencyLinks(new HashSet<>(competencyExerciseLinkRepository.saveAll(links))); + } + + return savedExercise; + } + /** * Reconnects the competency exercise links to the exercise after the cycle was broken by the deserialization. * diff --git a/src/main/java/de/tum/cit/aet/artemis/fileupload/web/FileUploadExerciseResource.java b/src/main/java/de/tum/cit/aet/artemis/fileupload/web/FileUploadExerciseResource.java index 724cb3820a6e..02f27274f007 100644 --- a/src/main/java/de/tum/cit/aet/artemis/fileupload/web/FileUploadExerciseResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/fileupload/web/FileUploadExerciseResource.java @@ -156,7 +156,7 @@ public ResponseEntity createFileUploadExercise(@RequestBody // Check that the user is authorized to create the exercise authCheckService.checkHasAtLeastRoleInCourseElseThrow(Role.EDITOR, course, null); - FileUploadExercise result = fileUploadExerciseRepository.save(fileUploadExercise); + FileUploadExercise result = exerciseService.createWithCompetencyLinks(fileUploadExercise, fileUploadExerciseRepository::save); channelService.createExerciseChannel(result, Optional.ofNullable(fileUploadExercise.getChannelName())); groupNotificationScheduleService.checkNotificationsForNewExerciseAsync(fileUploadExercise); diff --git a/src/main/java/de/tum/cit/aet/artemis/modeling/web/ModelingExerciseResource.java b/src/main/java/de/tum/cit/aet/artemis/modeling/web/ModelingExerciseResource.java index 608ac3ca9ef5..d593f97df07b 100644 --- a/src/main/java/de/tum/cit/aet/artemis/modeling/web/ModelingExerciseResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/modeling/web/ModelingExerciseResource.java @@ -177,7 +177,7 @@ public ResponseEntity createModelingExercise(@RequestBody Mode // Check that the user is authorized to create the exercise authCheckService.checkHasAtLeastRoleInCourseElseThrow(Role.EDITOR, course, null); - ModelingExercise result = modelingExerciseRepository.save(modelingExercise); + ModelingExercise result = exerciseService.createWithCompetencyLinks(modelingExercise, modelingExerciseRepository::save); channelService.createExerciseChannel(result, Optional.ofNullable(modelingExercise.getChannelName())); modelingExerciseService.scheduleOperations(result.getId()); diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseService.java index e3e305fb20bb..5d8ca9ad8e85 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseService.java @@ -272,7 +272,9 @@ public ProgrammingExercise createProgrammingExercise(ProgrammingExercise program // We save once in order to generate an id for the programming exercise var savedBuildConfig = programmingExerciseBuildConfigRepository.saveAndFlush(programmingExercise.getBuildConfig()); programmingExercise.setBuildConfig(savedBuildConfig); - var savedProgrammingExercise = programmingExerciseRepository.saveForCreation(programmingExercise); + + var savedProgrammingExercise = exerciseService.createWithCompetencyLinks(programmingExercise, programmingExerciseRepository::saveForCreation); + savedProgrammingExercise.getBuildConfig().setProgrammingExercise(savedProgrammingExercise); programmingExerciseBuildConfigRepository.save(savedProgrammingExercise.getBuildConfig()); // Step 1: Setting constant facts for a programming exercise diff --git a/src/main/java/de/tum/cit/aet/artemis/quiz/service/QuizExerciseService.java b/src/main/java/de/tum/cit/aet/artemis/quiz/service/QuizExerciseService.java index 46651ea7a789..8ff7a3e00426 100644 --- a/src/main/java/de/tum/cit/aet/artemis/quiz/service/QuizExerciseService.java +++ b/src/main/java/de/tum/cit/aet/artemis/quiz/service/QuizExerciseService.java @@ -33,7 +33,7 @@ import de.tum.cit.aet.artemis.assessment.domain.Result; import de.tum.cit.aet.artemis.assessment.repository.ResultRepository; -import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyExerciseLink; +import de.tum.cit.aet.artemis.atlas.repository.CompetencyExerciseLinkRepository; import de.tum.cit.aet.artemis.core.config.Constants; import de.tum.cit.aet.artemis.core.domain.User; import de.tum.cit.aet.artemis.core.dto.SearchResultPageDTO; @@ -87,10 +87,12 @@ public class QuizExerciseService extends QuizService { private final ExerciseService exerciseService; + private final CompetencyExerciseLinkRepository competencyExerciseLinkRepository; + public QuizExerciseService(QuizExerciseRepository quizExerciseRepository, ResultRepository resultRepository, QuizSubmissionRepository quizSubmissionRepository, InstanceMessageSendService instanceMessageSendService, QuizStatisticService quizStatisticService, QuizBatchService quizBatchService, ExerciseSpecificationService exerciseSpecificationService, FileService fileService, DragAndDropMappingRepository dragAndDropMappingRepository, - ShortAnswerMappingRepository shortAnswerMappingRepository, ExerciseService exerciseService) { + ShortAnswerMappingRepository shortAnswerMappingRepository, ExerciseService exerciseService, CompetencyExerciseLinkRepository competencyExerciseLinkRepository) { super(dragAndDropMappingRepository, shortAnswerMappingRepository); this.quizExerciseRepository = quizExerciseRepository; this.resultRepository = resultRepository; @@ -101,6 +103,7 @@ public QuizExerciseService(QuizExerciseRepository quizExerciseRepository, Result this.exerciseSpecificationService = exerciseSpecificationService; this.fileService = fileService; this.exerciseService = exerciseService; + this.competencyExerciseLinkRepository = competencyExerciseLinkRepository; } /** @@ -434,17 +437,7 @@ public QuizExercise save(QuizExercise quizExercise) { // make sure the pointers in the statistics are correct quizExercise.recalculatePointCounters(); - // persist lecture unit before competency links to prevent error - Set links = quizExercise.getCompetencyLinks(); - quizExercise.setCompetencyLinks(new HashSet<>()); - - QuizExercise savedQuizExercise = super.save(quizExercise); - - if (!links.isEmpty()) { - savedQuizExercise.setCompetencyLinks(links); - exerciseService.reconnectCompetencyExerciseLinks(savedQuizExercise); - savedQuizExercise = quizExerciseRepository.saveAndFlush(savedQuizExercise); - } + QuizExercise savedQuizExercise = exerciseService.createWithCompetencyLinks(quizExercise, super::save); if (savedQuizExercise.isCourseExercise()) { // only schedule quizzes for course exercises, not for exam exercises diff --git a/src/main/java/de/tum/cit/aet/artemis/text/web/TextExerciseResource.java b/src/main/java/de/tum/cit/aet/artemis/text/web/TextExerciseResource.java index f59713f005eb..773fb3b0f324 100644 --- a/src/main/java/de/tum/cit/aet/artemis/text/web/TextExerciseResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/text/web/TextExerciseResource.java @@ -222,7 +222,7 @@ public ResponseEntity createTextExercise(@RequestBody TextExercise // Check that only allowed athena modules are used athenaModuleService.ifPresentOrElse(ams -> ams.checkHasAccessToAthenaModule(textExercise, course, ENTITY_NAME), () -> textExercise.setFeedbackSuggestionModule(null)); - TextExercise result = textExerciseRepository.save(textExercise); + TextExercise result = exerciseService.createWithCompetencyLinks(textExercise, textExerciseRepository::save); channelService.createExerciseChannel(result, Optional.ofNullable(textExercise.getChannelName())); instanceMessageSendService.sendTextExerciseSchedule(result.getId()); From 4458e4755c7e760c1ebdd83a64054d1085cc9eed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20St=C3=B6hr?= Date: Mon, 21 Oct 2024 18:14:17 +0200 Subject: [PATCH 10/25] Fix exercise update server --- .../artemis/exercise/service/ExerciseService.java | 13 +++++++++++-- .../fileupload/web/FileUploadExerciseResource.java | 6 ++---- .../modeling/web/ModelingExerciseResource.java | 5 ++--- .../service/ProgrammingExerciseService.java | 5 ++--- .../artemis/quiz/service/QuizExerciseService.java | 2 +- .../aet/artemis/text/web/TextExerciseResource.java | 5 ++--- 6 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/exercise/service/ExerciseService.java b/src/main/java/de/tum/cit/aet/artemis/exercise/service/ExerciseService.java index 5d59ff05727a..0a355a870620 100644 --- a/src/main/java/de/tum/cit/aet/artemis/exercise/service/ExerciseService.java +++ b/src/main/java/de/tum/cit/aet/artemis/exercise/service/ExerciseService.java @@ -20,6 +20,7 @@ import jakarta.validation.constraints.NotNull; import org.apache.commons.lang3.StringUtils; +import org.hibernate.Hibernate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.actuate.audit.AuditEvent; @@ -796,14 +797,22 @@ else if (now().plusMinutes(EXAM_START_WAIT_TIME_MINUTES).isAfter(originalExercis } } - public T createWithCompetencyLinks(T exercise, Function saveFunction) { + /** + * Saves the exercise and links it to the competencies. + * + * @param exercise exercise to save + * @param saveFunction function to save the exercise + * @param type of the exercise + * @return saved exercise + */ + public T saveWithCompetencyLinks(T exercise, Function saveFunction) { // persist exercise before linking it to the competency Set links = exercise.getCompetencyLinks(); exercise.setCompetencyLinks(new HashSet<>()); T savedExercise = saveFunction.apply(exercise); - if (!links.isEmpty()) { + if (Hibernate.isInitialized(links) && !links.isEmpty()) { savedExercise.setCompetencyLinks(links); reconnectCompetencyExerciseLinks(savedExercise); savedExercise.setCompetencyLinks(new HashSet<>(competencyExerciseLinkRepository.saveAll(links))); diff --git a/src/main/java/de/tum/cit/aet/artemis/fileupload/web/FileUploadExerciseResource.java b/src/main/java/de/tum/cit/aet/artemis/fileupload/web/FileUploadExerciseResource.java index 02f27274f007..02e060f7c931 100644 --- a/src/main/java/de/tum/cit/aet/artemis/fileupload/web/FileUploadExerciseResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/fileupload/web/FileUploadExerciseResource.java @@ -156,7 +156,7 @@ public ResponseEntity createFileUploadExercise(@RequestBody // Check that the user is authorized to create the exercise authCheckService.checkHasAtLeastRoleInCourseElseThrow(Role.EDITOR, course, null); - FileUploadExercise result = exerciseService.createWithCompetencyLinks(fileUploadExercise, fileUploadExerciseRepository::save); + FileUploadExercise result = exerciseService.saveWithCompetencyLinks(fileUploadExercise, fileUploadExerciseRepository::save); channelService.createExerciseChannel(result, Optional.ofNullable(fileUploadExercise.getChannelName())); groupNotificationScheduleService.checkNotificationsForNewExerciseAsync(fileUploadExercise); @@ -281,9 +281,7 @@ public ResponseEntity updateFileUploadExercise(@RequestBody channelService.updateExerciseChannel(fileUploadExerciseBeforeUpdate, fileUploadExercise); - exerciseService.reconnectCompetencyExerciseLinks(fileUploadExercise); - - var updatedExercise = fileUploadExerciseRepository.save(fileUploadExercise); + var updatedExercise = exerciseService.saveWithCompetencyLinks(fileUploadExercise, fileUploadExerciseRepository::save); exerciseService.logUpdate(updatedExercise, updatedExercise.getCourseViaExerciseGroupOrCourseMember(), user); exerciseService.updatePointsInRelatedParticipantScores(fileUploadExerciseBeforeUpdate, updatedExercise); participationRepository.removeIndividualDueDatesIfBeforeDueDate(updatedExercise, fileUploadExerciseBeforeUpdate.getDueDate()); diff --git a/src/main/java/de/tum/cit/aet/artemis/modeling/web/ModelingExerciseResource.java b/src/main/java/de/tum/cit/aet/artemis/modeling/web/ModelingExerciseResource.java index d593f97df07b..a2739ed28c76 100644 --- a/src/main/java/de/tum/cit/aet/artemis/modeling/web/ModelingExerciseResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/modeling/web/ModelingExerciseResource.java @@ -177,7 +177,7 @@ public ResponseEntity createModelingExercise(@RequestBody Mode // Check that the user is authorized to create the exercise authCheckService.checkHasAtLeastRoleInCourseElseThrow(Role.EDITOR, course, null); - ModelingExercise result = exerciseService.createWithCompetencyLinks(modelingExercise, modelingExerciseRepository::save); + ModelingExercise result = exerciseService.saveWithCompetencyLinks(modelingExercise, modelingExerciseRepository::save); channelService.createExerciseChannel(result, Optional.ofNullable(modelingExercise.getChannelName())); modelingExerciseService.scheduleOperations(result.getId()); @@ -242,9 +242,8 @@ public ResponseEntity updateModelingExercise(@RequestBody Mode channelService.updateExerciseChannel(modelingExerciseBeforeUpdate, modelingExercise); - exerciseService.reconnectCompetencyExerciseLinks(modelingExercise); + ModelingExercise updatedModelingExercise = exerciseService.saveWithCompetencyLinks(modelingExercise, modelingExerciseRepository::save); - ModelingExercise updatedModelingExercise = modelingExerciseRepository.save(modelingExercise); exerciseService.logUpdate(modelingExercise, modelingExercise.getCourseViaExerciseGroupOrCourseMember(), user); exerciseService.updatePointsInRelatedParticipantScores(modelingExerciseBeforeUpdate, updatedModelingExercise); diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseService.java index 5d8ca9ad8e85..93a57b8557d6 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseService.java @@ -273,7 +273,7 @@ public ProgrammingExercise createProgrammingExercise(ProgrammingExercise program var savedBuildConfig = programmingExerciseBuildConfigRepository.saveAndFlush(programmingExercise.getBuildConfig()); programmingExercise.setBuildConfig(savedBuildConfig); - var savedProgrammingExercise = exerciseService.createWithCompetencyLinks(programmingExercise, programmingExerciseRepository::saveForCreation); + var savedProgrammingExercise = exerciseService.saveWithCompetencyLinks(programmingExercise, programmingExerciseRepository::saveForCreation); savedProgrammingExercise.getBuildConfig().setProgrammingExercise(savedProgrammingExercise); programmingExerciseBuildConfigRepository.save(savedProgrammingExercise.getBuildConfig()); @@ -607,9 +607,8 @@ public ProgrammingExercise updateProgrammingExercise(ProgrammingExercise program programmingExerciseTaskService.replaceTestNamesWithIds(updatedProgrammingExercise); programmingExerciseBuildConfigRepository.save(updatedProgrammingExercise.getBuildConfig()); - exerciseService.reconnectCompetencyExerciseLinks(updatedProgrammingExercise); + ProgrammingExercise savedProgrammingExercise = exerciseService.saveWithCompetencyLinks(updatedProgrammingExercise, programmingExerciseRepository::save); - ProgrammingExercise savedProgrammingExercise = programmingExerciseRepository.save(updatedProgrammingExercise); // The returned value should use test case names since it gets send back to the client savedProgrammingExercise.setProblemStatement(problemStatementWithTestNames); diff --git a/src/main/java/de/tum/cit/aet/artemis/quiz/service/QuizExerciseService.java b/src/main/java/de/tum/cit/aet/artemis/quiz/service/QuizExerciseService.java index 8ff7a3e00426..274718a9b382 100644 --- a/src/main/java/de/tum/cit/aet/artemis/quiz/service/QuizExerciseService.java +++ b/src/main/java/de/tum/cit/aet/artemis/quiz/service/QuizExerciseService.java @@ -437,7 +437,7 @@ public QuizExercise save(QuizExercise quizExercise) { // make sure the pointers in the statistics are correct quizExercise.recalculatePointCounters(); - QuizExercise savedQuizExercise = exerciseService.createWithCompetencyLinks(quizExercise, super::save); + QuizExercise savedQuizExercise = exerciseService.saveWithCompetencyLinks(quizExercise, super::save); if (savedQuizExercise.isCourseExercise()) { // only schedule quizzes for course exercises, not for exam exercises diff --git a/src/main/java/de/tum/cit/aet/artemis/text/web/TextExerciseResource.java b/src/main/java/de/tum/cit/aet/artemis/text/web/TextExerciseResource.java index 773fb3b0f324..11bb4c15721e 100644 --- a/src/main/java/de/tum/cit/aet/artemis/text/web/TextExerciseResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/text/web/TextExerciseResource.java @@ -222,7 +222,7 @@ public ResponseEntity createTextExercise(@RequestBody TextExercise // Check that only allowed athena modules are used athenaModuleService.ifPresentOrElse(ams -> ams.checkHasAccessToAthenaModule(textExercise, course, ENTITY_NAME), () -> textExercise.setFeedbackSuggestionModule(null)); - TextExercise result = exerciseService.createWithCompetencyLinks(textExercise, textExerciseRepository::save); + TextExercise result = exerciseService.saveWithCompetencyLinks(textExercise, textExerciseRepository::save); channelService.createExerciseChannel(result, Optional.ofNullable(textExercise.getChannelName())); instanceMessageSendService.sendTextExerciseSchedule(result.getId()); @@ -278,9 +278,8 @@ public ResponseEntity updateTextExercise(@RequestBody TextExercise channelService.updateExerciseChannel(textExerciseBeforeUpdate, textExercise); - exerciseService.reconnectCompetencyExerciseLinks(textExercise); + TextExercise updatedTextExercise = exerciseService.saveWithCompetencyLinks(textExercise, textExerciseRepository::save); - TextExercise updatedTextExercise = textExerciseRepository.save(textExercise); exerciseService.logUpdate(updatedTextExercise, updatedTextExercise.getCourseViaExerciseGroupOrCourseMember(), user); exerciseService.updatePointsInRelatedParticipantScores(textExerciseBeforeUpdate, updatedTextExercise); participationRepository.removeIndividualDueDatesIfBeforeDueDate(updatedTextExercise, textExerciseBeforeUpdate.getDueDate()); From bfa4f289548892ee4710c2c2770e7cacbe08d95a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20St=C3=B6hr?= Date: Mon, 21 Oct 2024 18:50:51 +0200 Subject: [PATCH 11/25] Fix exercise import --- .../service/FileUploadExerciseImportService.java | 8 ++++++-- .../service/ModelingExerciseImportService.java | 8 ++++++-- .../ProgrammingExerciseImportBasicService.java | 10 ++++++++-- .../text/service/TextExerciseImportService.java | 10 +++++++--- .../manage/programming-exercise.component.ts | 12 ++++++------ .../FileUploadExerciseIntegrationTest.java | 2 +- .../modeling/ModelingExerciseIntegrationTest.java | 4 +++- ...rammingExerciseLocalVCLocalCIIntegrationTest.java | 4 ++++ .../artemis/text/TextExerciseIntegrationTest.java | 3 +++ 9 files changed, 44 insertions(+), 17 deletions(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/fileupload/service/FileUploadExerciseImportService.java b/src/main/java/de/tum/cit/aet/artemis/fileupload/service/FileUploadExerciseImportService.java index a6e1a51ad71e..f997bf8d30e0 100644 --- a/src/main/java/de/tum/cit/aet/artemis/fileupload/service/FileUploadExerciseImportService.java +++ b/src/main/java/de/tum/cit/aet/artemis/fileupload/service/FileUploadExerciseImportService.java @@ -19,6 +19,7 @@ import de.tum.cit.aet.artemis.communication.service.conversation.ChannelService; import de.tum.cit.aet.artemis.exercise.repository.SubmissionRepository; import de.tum.cit.aet.artemis.exercise.service.ExerciseImportService; +import de.tum.cit.aet.artemis.exercise.service.ExerciseService; import de.tum.cit.aet.artemis.fileupload.domain.FileUploadExercise; import de.tum.cit.aet.artemis.fileupload.repository.FileUploadExerciseRepository; @@ -34,13 +35,16 @@ public class FileUploadExerciseImportService extends ExerciseImportService { private final CompetencyProgressService competencyProgressService; + private final ExerciseService exerciseService; + public FileUploadExerciseImportService(ExampleSubmissionRepository exampleSubmissionRepository, SubmissionRepository submissionRepository, ResultRepository resultRepository, FileUploadExerciseRepository fileUploadExerciseRepository, ChannelService channelService, FeedbackService feedbackService, - CompetencyProgressService competencyProgressService) { + CompetencyProgressService competencyProgressService, ExerciseService exerciseService) { super(exampleSubmissionRepository, submissionRepository, resultRepository, feedbackService); this.fileUploadExerciseRepository = fileUploadExerciseRepository; this.channelService = channelService; this.competencyProgressService = competencyProgressService; + this.exerciseService = exerciseService; } /** @@ -57,7 +61,7 @@ public FileUploadExercise importFileUploadExercise(final FileUploadExercise temp log.debug("Creating a new Exercise based on exercise {}", templateExercise); FileUploadExercise newExercise = copyFileUploadExerciseBasis(importedExercise); - FileUploadExercise newFileUploadExercise = fileUploadExerciseRepository.save(newExercise); + FileUploadExercise newFileUploadExercise = exerciseService.saveWithCompetencyLinks(newExercise, fileUploadExerciseRepository::save); channelService.createExerciseChannel(newFileUploadExercise, Optional.ofNullable(importedExercise.getChannelName())); diff --git a/src/main/java/de/tum/cit/aet/artemis/modeling/service/ModelingExerciseImportService.java b/src/main/java/de/tum/cit/aet/artemis/modeling/service/ModelingExerciseImportService.java index 4c9a8b01bf09..3c877ce69465 100644 --- a/src/main/java/de/tum/cit/aet/artemis/modeling/service/ModelingExerciseImportService.java +++ b/src/main/java/de/tum/cit/aet/artemis/modeling/service/ModelingExerciseImportService.java @@ -27,6 +27,7 @@ import de.tum.cit.aet.artemis.exercise.domain.Submission; import de.tum.cit.aet.artemis.exercise.repository.SubmissionRepository; import de.tum.cit.aet.artemis.exercise.service.ExerciseImportService; +import de.tum.cit.aet.artemis.exercise.service.ExerciseService; import de.tum.cit.aet.artemis.modeling.domain.ModelingExercise; import de.tum.cit.aet.artemis.modeling.domain.ModelingSubmission; import de.tum.cit.aet.artemis.modeling.repository.ModelingExerciseRepository; @@ -43,13 +44,16 @@ public class ModelingExerciseImportService extends ExerciseImportService { private final CompetencyProgressService competencyProgressService; + private final ExerciseService exerciseService; + public ModelingExerciseImportService(ModelingExerciseRepository modelingExerciseRepository, ExampleSubmissionRepository exampleSubmissionRepository, SubmissionRepository submissionRepository, ResultRepository resultRepository, ChannelService channelService, FeedbackService feedbackService, - CompetencyProgressService competencyProgressService) { + CompetencyProgressService competencyProgressService, ExerciseService exerciseService) { super(exampleSubmissionRepository, submissionRepository, resultRepository, feedbackService); this.modelingExerciseRepository = modelingExerciseRepository; this.channelService = channelService; this.competencyProgressService = competencyProgressService; + this.exerciseService = exerciseService; } /** @@ -68,7 +72,7 @@ public ModelingExercise importModelingExercise(ModelingExercise templateExercise Map gradingInstructionCopyTracker = new HashMap<>(); ModelingExercise newExercise = copyModelingExerciseBasis(importedExercise, gradingInstructionCopyTracker); - ModelingExercise newModelingExercise = modelingExerciseRepository.save(newExercise); + ModelingExercise newModelingExercise = exerciseService.saveWithCompetencyLinks(newExercise, modelingExerciseRepository::save); channelService.createExerciseChannel(newModelingExercise, Optional.ofNullable(importedExercise.getChannelName())); newModelingExercise.setExampleSubmissions(copyExampleSubmission(templateExercise, newExercise, gradingInstructionCopyTracker)); diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseImportBasicService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseImportBasicService.java index bcd23f9ddda3..9bb4403d6246 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseImportBasicService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseImportBasicService.java @@ -17,6 +17,7 @@ import de.tum.cit.aet.artemis.communication.service.conversation.ChannelService; import de.tum.cit.aet.artemis.exercise.domain.ExerciseMode; +import de.tum.cit.aet.artemis.exercise.service.ExerciseService; import de.tum.cit.aet.artemis.plagiarism.domain.PlagiarismDetectionConfig; import de.tum.cit.aet.artemis.programming.domain.AuxiliaryRepository; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; @@ -77,6 +78,8 @@ public class ProgrammingExerciseImportBasicService { private final ChannelService channelService; + private final ExerciseService exerciseService; + public ProgrammingExerciseImportBasicService(ExerciseHintService exerciseHintService, ExerciseHintRepository exerciseHintRepository, Optional versionControlService, ProgrammingExerciseParticipationService programmingExerciseParticipationService, ProgrammingExerciseTestCaseRepository programmingExerciseTestCaseRepository, StaticCodeAnalysisCategoryRepository staticCodeAnalysisCategoryRepository, @@ -84,7 +87,7 @@ public ProgrammingExerciseImportBasicService(ExerciseHintService exerciseHintSer AuxiliaryRepositoryRepository auxiliaryRepositoryRepository, SubmissionPolicyRepository submissionPolicyRepository, ProgrammingExerciseTaskRepository programmingExerciseTaskRepository, ProgrammingExerciseTaskService programmingExerciseTaskService, ProgrammingExerciseSolutionEntryRepository solutionEntryRepository, ChannelService channelService, - ProgrammingExerciseBuildConfigRepository programmingExerciseBuildConfigRepository) { + ProgrammingExerciseBuildConfigRepository programmingExerciseBuildConfigRepository, ExerciseService exerciseService) { this.exerciseHintService = exerciseHintService; this.exerciseHintRepository = exerciseHintRepository; this.versionControlService = versionControlService; @@ -101,6 +104,7 @@ public ProgrammingExerciseImportBasicService(ExerciseHintService exerciseHintSer this.solutionEntryRepository = solutionEntryRepository; this.channelService = channelService; this.programmingExerciseBuildConfigRepository = programmingExerciseBuildConfigRepository; + this.exerciseService = exerciseService; } /** @@ -142,7 +146,9 @@ public ProgrammingExercise importProgrammingExerciseBasis(final ProgrammingExerc final Map newHintIdByOldId = exerciseHintService.copyExerciseHints(originalProgrammingExercise, newProgrammingExercise); newProgrammingExercise.setBuildConfig(programmingExerciseBuildConfigRepository.save(newProgrammingExercise.getBuildConfig())); - final ProgrammingExercise importedExercise = programmingExerciseRepository.save(newProgrammingExercise); + + final ProgrammingExercise importedExercise = exerciseService.saveWithCompetencyLinks(newProgrammingExercise, programmingExerciseRepository::save); + ; final Map newTestCaseIdByOldId = importTestCases(originalProgrammingExercise, importedExercise); final Map newTaskIdByOldId = importTasks(originalProgrammingExercise, importedExercise, newTestCaseIdByOldId); diff --git a/src/main/java/de/tum/cit/aet/artemis/text/service/TextExerciseImportService.java b/src/main/java/de/tum/cit/aet/artemis/text/service/TextExerciseImportService.java index 249d395c295b..161c6426589b 100644 --- a/src/main/java/de/tum/cit/aet/artemis/text/service/TextExerciseImportService.java +++ b/src/main/java/de/tum/cit/aet/artemis/text/service/TextExerciseImportService.java @@ -33,6 +33,7 @@ import de.tum.cit.aet.artemis.exercise.domain.Submission; import de.tum.cit.aet.artemis.exercise.repository.SubmissionRepository; import de.tum.cit.aet.artemis.exercise.service.ExerciseImportService; +import de.tum.cit.aet.artemis.exercise.service.ExerciseService; import de.tum.cit.aet.artemis.text.domain.TextBlock; import de.tum.cit.aet.artemis.text.domain.TextBlockType; import de.tum.cit.aet.artemis.text.domain.TextExercise; @@ -58,10 +59,12 @@ public class TextExerciseImportService extends ExerciseImportService { private final CompetencyProgressService competencyProgressService; + private final ExerciseService exerciseService; + public TextExerciseImportService(TextExerciseRepository textExerciseRepository, ExampleSubmissionRepository exampleSubmissionRepository, SubmissionRepository submissionRepository, ResultRepository resultRepository, TextBlockRepository textBlockRepository, FeedbackRepository feedbackRepository, - TextSubmissionRepository textSubmissionRepository, ChannelService channelService, FeedbackService feedbackService, - CompetencyProgressService competencyProgressService) { + TextSubmissionRepository textSubmissionRepository, ChannelService channelService, FeedbackService feedbackService, CompetencyProgressService competencyProgressService, + ExerciseService exerciseService) { super(exampleSubmissionRepository, submissionRepository, resultRepository, feedbackService); this.textBlockRepository = textBlockRepository; this.textExerciseRepository = textExerciseRepository; @@ -69,6 +72,7 @@ public TextExerciseImportService(TextExerciseRepository textExerciseRepository, this.textSubmissionRepository = textSubmissionRepository; this.channelService = channelService; this.competencyProgressService = competencyProgressService; + this.exerciseService = exerciseService; } /** @@ -91,7 +95,7 @@ public TextExercise importTextExercise(final TextExercise templateExercise, Text newExercise.setFeedbackSuggestionModule(null); } - TextExercise newTextExercise = textExerciseRepository.save(newExercise); + TextExercise newTextExercise = exerciseService.saveWithCompetencyLinks(newExercise, textExerciseRepository::save); channelService.createExerciseChannel(newTextExercise, Optional.ofNullable(importedExercise.getChannelName())); newExercise.setExampleSubmissions(copyExampleSubmission(templateExercise, newExercise, gradingInstructionCopyTracker)); diff --git a/src/main/webapp/app/exercises/programming/manage/programming-exercise.component.ts b/src/main/webapp/app/exercises/programming/manage/programming-exercise.component.ts index bcc83f36839e..e1f659c87483 100644 --- a/src/main/webapp/app/exercises/programming/manage/programming-exercise.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/programming-exercise.component.ts @@ -119,18 +119,18 @@ export class ProgrammingExerciseComponent extends ExerciseComponent implements O exercise.course = this.course; this.accountService.setAccessRightsForExercise(exercise); if (exercise.projectKey) { - if (exercise.solutionParticipation!.buildPlanId) { - exercise.solutionParticipation!.buildPlanUrl = createBuildPlanUrl( + if (exercise.solutionParticipation?.buildPlanId) { + exercise.solutionParticipation.buildPlanUrl = createBuildPlanUrl( this.buildPlanLinkTemplate, exercise.projectKey, - exercise.solutionParticipation!.buildPlanId, + exercise.solutionParticipation.buildPlanId, ); } - if (exercise.templateParticipation!.buildPlanId) { - exercise.templateParticipation!.buildPlanUrl = createBuildPlanUrl( + if (exercise.templateParticipation?.buildPlanId) { + exercise.templateParticipation.buildPlanUrl = createBuildPlanUrl( this.buildPlanLinkTemplate, exercise.projectKey, - exercise.templateParticipation!.buildPlanId, + exercise.templateParticipation.buildPlanId, ); } } diff --git a/src/test/java/de/tum/cit/aet/artemis/fileupload/FileUploadExerciseIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/fileupload/FileUploadExerciseIntegrationTest.java index 0f3131b3aa84..1c6476a486e4 100644 --- a/src/test/java/de/tum/cit/aet/artemis/fileupload/FileUploadExerciseIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/fileupload/FileUploadExerciseIntegrationTest.java @@ -687,7 +687,7 @@ void testImportFileUploadExerciseFromCourseToCourseAsEditorSuccess() throws Exce var importedFileUploadExercise = request.postWithResponseBody("/api/file-upload-exercises/import/" + sourceExerciseId, expectedFileUploadExercise, FileUploadExercise.class, HttpStatus.CREATED); assertThat(importedFileUploadExercise).usingRecursiveComparison().ignoringFields("id", "course", "shortName", "releaseDate", "dueDate", "assessmentDueDate", - "exampleSolutionPublicationDate", "channelNameTransient", "competencies").isEqualTo(expectedFileUploadExercise); + "exampleSolutionPublicationDate", "channelNameTransient", "competencyLinks").isEqualTo(expectedFileUploadExercise); Channel channelFromDB = channelRepository.findChannelByExerciseId(importedFileUploadExercise.getId()); assertThat(channelFromDB).isNotNull(); assertThat(channelFromDB.getName()).isEqualTo(uniqueChannelName); diff --git a/src/test/java/de/tum/cit/aet/artemis/modeling/ModelingExerciseIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/modeling/ModelingExerciseIntegrationTest.java index f1a8f13d81a0..6732ee7c6a2d 100644 --- a/src/test/java/de/tum/cit/aet/artemis/modeling/ModelingExerciseIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/modeling/ModelingExerciseIntegrationTest.java @@ -228,6 +228,7 @@ void testUpdateModelingExercise_asInstructor() throws Exception { courseUtilService.enableMessagingForCourse(modelingExercise.getCourseViaExerciseGroupOrCourseMember()); modelingExercise.setChannelName("testchannel-" + UUID.randomUUID().toString().substring(0, 8)); modelingExercise.setCompetencyLinks(Set.of(new CompetencyExerciseLink(competency, modelingExercise, 1))); + modelingExercise.getCompetencyLinks().forEach(link -> link.getCompetency().setCourse(null)); ModelingExercise createdModelingExercise = request.postWithResponseBody("/api/modeling-exercises", modelingExercise, ModelingExercise.class, HttpStatus.CREATED); gradingCriteria = exerciseUtilService.addGradingInstructionsToExercise(modelingExercise); @@ -475,11 +476,12 @@ void importModelingExerciseFromCourseToCourse() throws Exception { String uniqueChannelName = "channel-" + UUID.randomUUID().toString().substring(0, 8); modelingExerciseToImport.setChannelName(uniqueChannelName); modelingExerciseToImport.getCompetencyLinks().add(new CompetencyExerciseLink(competency, modelingExerciseToImport, 1)); + modelingExerciseToImport.getCompetencyLinks().forEach(link -> link.getCompetency().setCourse(null)); var importedExercise = request.postWithResponseBody("/api/modeling-exercises/import/" + modelingExerciseToImport.getId(), modelingExerciseToImport, ModelingExercise.class, HttpStatus.CREATED); assertThat(importedExercise).usingRecursiveComparison().ignoringFields("id", "course", "shortName", "releaseDate", "dueDate", "assessmentDueDate", - "exampleSolutionPublicationDate", "channelNameTransient", "plagiarismDetectionConfig.id", "competencies").isEqualTo(modelingExerciseToImport); + "exampleSolutionPublicationDate", "channelNameTransient", "plagiarismDetectionConfig.id", "competencyLinks").isEqualTo(modelingExerciseToImport); Channel channelFromDB = channelRepository.findChannelByExerciseId(importedExercise.getId()); assertThat(channelFromDB).isNotNull(); assertThat(channelFromDB.getName()).isEqualTo(uniqueChannelName); diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/icl/ProgrammingExerciseLocalVCLocalCIIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/icl/ProgrammingExerciseLocalVCLocalCIIntegrationTest.java index ed98eded276c..16690c1aba19 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/icl/ProgrammingExerciseLocalVCLocalCIIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/icl/ProgrammingExerciseLocalVCLocalCIIntegrationTest.java @@ -168,6 +168,7 @@ void testCreateProgrammingExercise() throws Exception { ProgrammingExercise newExercise = ProgrammingExerciseFactory.generateProgrammingExercise(ZonedDateTime.now().minusDays(1), ZonedDateTime.now().plusDays(7), course); newExercise.setProjectType(ProjectType.PLAIN_GRADLE); newExercise.setCompetencyLinks(Set.of(new CompetencyExerciseLink(competency, newExercise, 1))); + newExercise.getCompetencyLinks().forEach(link -> link.getCompetency().setCourse(null)); // Mock dockerClient.copyArchiveFromContainerCmd() such that it returns a dummy commitHash for both the assignment and the test repository. // Note: The stub needs to receive the same object twice because there are two requests to the same method (one for the template participation and one for the solution @@ -220,6 +221,7 @@ void testCreateProgrammingExercise_Invalid_CheckoutPaths() throws Exception { void testUpdateProgrammingExercise() throws Exception { programmingExercise.setReleaseDate(ZonedDateTime.now().plusHours(1)); programmingExercise.setCompetencyLinks(Set.of(new CompetencyExerciseLink(competency, programmingExercise, 1))); + programmingExercise.getCompetencyLinks().forEach(link -> link.getCompetency().setCourse(null)); ProgrammingExercise updatedExercise = request.putWithResponseBody("/api/programming-exercises", programmingExercise, ProgrammingExercise.class, HttpStatus.OK); @@ -293,6 +295,8 @@ void testImportProgrammingExercise() throws Exception { params.add("recreateBuildPlans", "true"); exerciseToBeImported.setChannelName("testchannel-pe-imported"); exerciseToBeImported.setCompetencyLinks(Set.of(new CompetencyExerciseLink(competency, exerciseToBeImported, 1))); + exerciseToBeImported.getCompetencyLinks().forEach(link -> link.getCompetency().setCourse(null)); + var importedExercise = request.postWithResponseBody("/api/programming-exercises/import/" + programmingExercise.getId(), exerciseToBeImported, ProgrammingExercise.class, params, HttpStatus.OK); diff --git a/src/test/java/de/tum/cit/aet/artemis/text/TextExerciseIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/text/TextExerciseIntegrationTest.java index e0edf1621d57..c635d94e9c6b 100644 --- a/src/test/java/de/tum/cit/aet/artemis/text/TextExerciseIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/text/TextExerciseIntegrationTest.java @@ -420,6 +420,7 @@ void updateTextExercise() throws Exception { exampleSubmissionRepo.save(exampleSubmission); textExercise.addExampleSubmission(exampleSubmission); textExercise.setCompetencyLinks(Set.of(new CompetencyExerciseLink(competency, textExercise, 1))); + textExercise.getCompetencyLinks().forEach(link -> link.getCompetency().setCourse(null)); TextExercise updatedTextExercise = request.putWithResponseBody("/api/text-exercises", textExercise, TextExercise.class, HttpStatus.OK); @@ -590,6 +591,8 @@ void importTextExerciseFromCourseToCourse() throws Exception { textExercise.setCourse(course2); textExercise.setChannelName("testchannel" + textExercise.getId()); textExercise.setCompetencyLinks(Set.of(new CompetencyExerciseLink(competency, textExercise, 1))); + textExercise.getCompetencyLinks().forEach(link -> link.getCompetency().setCourse(null)); + var newTextExercise = request.postWithResponseBody("/api/text-exercises/import/" + textExercise.getId(), textExercise, TextExercise.class, HttpStatus.CREATED); Channel channel = channelRepository.findChannelByExerciseId(newTextExercise.getId()); assertThat(channel).isNotNull(); From 9f070acb3f5ab0ec6508457c77a6f3994a26f1b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20St=C3=B6hr?= Date: Tue, 22 Oct 2024 15:23:21 +0200 Subject: [PATCH 12/25] Done --- .../competency/CompetencyExerciseLink.java | 16 ------ .../competency/CompetencyLectureUnitLink.java | 16 ------ .../competency/CourseCompetencyService.java | 5 -- .../service/AttachmentUnitService.java | 5 +- .../lecture/service/LectureUnitService.java | 31 +++++++---- .../lecture/web/OnlineUnitResource.java | 3 +- .../artemis/lecture/web/TextUnitResource.java | 4 +- .../lecture/web/VideoUnitResource.java | 3 +- .../course/competencies/competency.service.ts | 12 ----- .../create/create-competency.component.ts | 3 +- .../create/create-prerequisite.component.ts | 3 +- .../edit/edit-competency.component.ts | 8 +-- .../edit/edit-prerequisite.component.ts | 8 +-- ...mmon-course-competency-form.component.html | 51 ------------------- ...common-course-competency-form.component.ts | 41 +-------------- .../competency/competency-form.component.html | 1 - .../competency/competency-form.component.ts | 2 - .../forms/course-competency-form.component.ts | 12 +---- .../prerequisite-form.component.html | 1 - .../prerequisite-form.component.ts | 2 - .../competencies/prerequisite.service.ts | 12 ----- 21 files changed, 33 insertions(+), 206 deletions(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyExerciseLink.java b/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyExerciseLink.java index 4fd40c80734a..b6764a924893 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyExerciseLink.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyExerciseLink.java @@ -53,22 +53,6 @@ public CompetencyExerciseId getId() { return id; } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof CompetencyExerciseLink that)) { - return false; - } - return Objects.equals(id, that.id); - } - - @Override - public int hashCode() { - return Objects.hashCode(id); - } - @Override public String toString() { return "CompetencyExerciseLink{" + "exercise=" + exercise + ", id=" + id + ", competency=" + competency + ", weight=" + weight + '}'; diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLectureUnitLink.java b/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLectureUnitLink.java index 9a422fe002ca..ce1e45183c02 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLectureUnitLink.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLectureUnitLink.java @@ -53,22 +53,6 @@ public CompetencyLectureUnitId getId() { return id; } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof CompetencyLectureUnitLink that)) { - return false; - } - return Objects.equals(id, that.id); - } - - @Override - public int hashCode() { - return Objects.hashCode(id); - } - @Override public String toString() { return "CompetencyLectureUnitLink{" + "lectureUnit=" + lectureUnit + ", id=" + id + ", competency=" + competency + ", weight=" + weight + '}'; diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CourseCompetencyService.java b/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CourseCompetencyService.java index 786857d2d860..3ce28572c089 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CourseCompetencyService.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CourseCompetencyService.java @@ -291,11 +291,8 @@ public List importStandardizedCompetencies(List competen */ public C createCourseCompetency(C competencyToCreate, Course course) { competencyToCreate.setCourse(course); - Set lectureUnitLinks = competencyToCreate.getLectureUnitLinks(); - competencyToCreate.setLectureUnitLinks(new HashSet<>()); var persistedCompetency = courseCompetencyRepository.save(competencyToCreate); - persistedCompetency.setLectureUnitLinks(lectureUnitLinks); updateLectureUnits(competencyToCreate, persistedCompetency); if (course.getLearningPathsEnabled()) { @@ -349,8 +346,6 @@ public C updateCourseCompetency(C competencyToUpdat competencyToUpdate.setMasteryThreshold(competency.getMasteryThreshold()); competencyToUpdate.setTaxonomy(competency.getTaxonomy()); competencyToUpdate.setOptional(competency.isOptional()); - competencyToUpdate.getLectureUnitLinks().forEach(link -> link.setCompetency(competency)); - competencyToUpdate.setLectureUnitLinks(competency.getLectureUnitLinks()); final var persistedCompetency = courseCompetencyRepository.save(competencyToUpdate); // update competency progress if necessary diff --git a/src/main/java/de/tum/cit/aet/artemis/lecture/service/AttachmentUnitService.java b/src/main/java/de/tum/cit/aet/artemis/lecture/service/AttachmentUnitService.java index 2e0b51a1fdab..8f6dc87ab88e 100644 --- a/src/main/java/de/tum/cit/aet/artemis/lecture/service/AttachmentUnitService.java +++ b/src/main/java/de/tum/cit/aet/artemis/lecture/service/AttachmentUnitService.java @@ -122,10 +122,7 @@ public AttachmentUnit updateAttachmentUnit(AttachmentUnit existingAttachmentUnit existingAttachmentUnit.setName(updateUnit.getName()); existingAttachmentUnit.setReleaseDate(updateUnit.getReleaseDate()); - lectureUnitService.updateCompetencyLectureUnitLinks(existingAttachmentUnit, updateUnit); - lectureUnitService.reconnectCompetencyLectureUnitLinks(existingAttachmentUnit); - - AttachmentUnit savedAttachmentUnit = attachmentUnitRepository.saveAndFlush(existingAttachmentUnit); + AttachmentUnit savedAttachmentUnit = lectureUnitService.saveWithCompetencyLinks(existingAttachmentUnit, attachmentUnitRepository::saveAndFlush); Attachment existingAttachment = existingAttachmentUnit.getAttachment(); if (existingAttachment == null) { diff --git a/src/main/java/de/tum/cit/aet/artemis/lecture/service/LectureUnitService.java b/src/main/java/de/tum/cit/aet/artemis/lecture/service/LectureUnitService.java index fa6b1184804f..5683045a208d 100644 --- a/src/main/java/de/tum/cit/aet/artemis/lecture/service/LectureUnitService.java +++ b/src/main/java/de/tum/cit/aet/artemis/lecture/service/LectureUnitService.java @@ -8,14 +8,17 @@ import java.net.URL; import java.time.ZonedDateTime; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.function.Function; import java.util.stream.Collectors; import jakarta.validation.constraints.NotNull; import jakarta.ws.rs.BadRequestException; +import org.hibernate.Hibernate; import org.springframework.context.annotation.Profile; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.stereotype.Service; @@ -227,16 +230,26 @@ public void reconnectCompetencyLectureUnitLinks(LectureUnit lectureUnit) { } /** - * Updates the competency lecture unit links of the existing lecture unit with the updated lecture unit. + * Saves the exercise and links it to the competencies. * - * @param existingLectureUnit The existing lecture unit - * @param updatedLectureUnit The updated lecture unit + * @param lectureUnit the lecture unit to save + * @param saveFunction function to save the exercise + * @param type of the lecture unit + * @return saved exercise */ - public void updateCompetencyLectureUnitLinks(LectureUnit existingLectureUnit, LectureUnit updatedLectureUnit) { - existingLectureUnit.getCompetencyLinks().removeIf( - link -> updatedLectureUnit.getCompetencyLinks().stream().noneMatch(updateLink -> updateLink.getCompetency().getId().equals(link.getCompetency().getId()))); - existingLectureUnit.getCompetencyLinks().addAll(updatedLectureUnit.getCompetencyLinks().stream().filter( - link -> existingLectureUnit.getCompetencyLinks().stream().noneMatch(existingLink -> existingLink.getCompetency().getId().equals(link.getCompetency().getId()))) - .toList()); + public T saveWithCompetencyLinks(T lectureUnit, Function saveFunction) { + // persist lecture Unit before linking it to the competency + Set links = lectureUnit.getCompetencyLinks(); + lectureUnit.setCompetencyLinks(new HashSet<>()); + + T savedLectureUnit = saveFunction.apply(lectureUnit); + + if (Hibernate.isInitialized(links) && !links.isEmpty()) { + savedLectureUnit.setCompetencyLinks(links); + reconnectCompetencyLectureUnitLinks(savedLectureUnit); + savedLectureUnit.setCompetencyLinks(new HashSet<>(competencyLectureUnitLinkRepository.saveAll(links))); + } + + return savedLectureUnit; } } diff --git a/src/main/java/de/tum/cit/aet/artemis/lecture/web/OnlineUnitResource.java b/src/main/java/de/tum/cit/aet/artemis/lecture/web/OnlineUnitResource.java index b731caa04070..5fcb6bba8c85 100644 --- a/src/main/java/de/tum/cit/aet/artemis/lecture/web/OnlineUnitResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/lecture/web/OnlineUnitResource.java @@ -111,8 +111,7 @@ public ResponseEntity updateOnlineUnit(@PathVariable Long lectureId, authorizationCheckService.checkHasAtLeastRoleInCourseElseThrow(Role.EDITOR, onlineUnit.getLecture().getCourse(), null); - lectureUnitService.reconnectCompetencyLectureUnitLinks(onlineUnit); - OnlineUnit result = onlineUnitRepository.save(onlineUnit); + OnlineUnit result = lectureUnitService.saveWithCompetencyLinks(onlineUnit, onlineUnitRepository::save); competencyProgressService.updateProgressForUpdatedLearningObjectAsync(existingOnlineUnit, Optional.of(onlineUnit)); diff --git a/src/main/java/de/tum/cit/aet/artemis/lecture/web/TextUnitResource.java b/src/main/java/de/tum/cit/aet/artemis/lecture/web/TextUnitResource.java index 9263efb5324e..35b844cd9b4c 100644 --- a/src/main/java/de/tum/cit/aet/artemis/lecture/web/TextUnitResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/lecture/web/TextUnitResource.java @@ -108,8 +108,8 @@ public ResponseEntity updateTextUnit(@PathVariable Long lectureId, @Re textUnitForm.setId(existingTextUnit.getId()); textUnitForm.setLecture(existingTextUnit.getLecture()); - lectureUnitService.reconnectCompetencyLectureUnitLinks(textUnitForm); - TextUnit result = textUnitRepository.save(textUnitForm); + + TextUnit result = lectureUnitService.saveWithCompetencyLinks(textUnitForm, textUnitRepository::save); competencyProgressService.updateProgressForUpdatedLearningObjectAsync(existingTextUnit, Optional.of(textUnitForm)); diff --git a/src/main/java/de/tum/cit/aet/artemis/lecture/web/VideoUnitResource.java b/src/main/java/de/tum/cit/aet/artemis/lecture/web/VideoUnitResource.java index 422eb4c9baa7..07703940bd9e 100644 --- a/src/main/java/de/tum/cit/aet/artemis/lecture/web/VideoUnitResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/lecture/web/VideoUnitResource.java @@ -100,8 +100,7 @@ public ResponseEntity updateVideoUnit(@PathVariable Long lectureId, @ lectureUnitService.validateUrlStringAndReturnUrl(videoUnit.getSource()); authorizationCheckService.checkHasAtLeastRoleInCourseElseThrow(Role.EDITOR, videoUnit.getLecture().getCourse(), null); - lectureUnitService.reconnectCompetencyLectureUnitLinks(videoUnit); - VideoUnit result = videoUnitRepository.save(videoUnit); + VideoUnit result = lectureUnitService.saveWithCompetencyLinks(videoUnit, videoUnitRepository::save); competencyProgressService.updateProgressForUpdatedLearningObjectAsync(existingVideoUnit, Optional.of(videoUnit)); diff --git a/src/main/webapp/app/course/competencies/competency.service.ts b/src/main/webapp/app/course/competencies/competency.service.ts index ccac5b6318f3..1cba4d5410c4 100644 --- a/src/main/webapp/app/course/competencies/competency.service.ts +++ b/src/main/webapp/app/course/competencies/competency.service.ts @@ -37,12 +37,6 @@ export class CompetencyService extends CourseCompetencyService { } create(competency: Competency, courseId: number): Observable { - competency.lectureUnitLinks?.forEach((link) => { - link.lectureUnit = undefined; - if (link.competency?.lectureUnitLinks) { - link.competency.lectureUnitLinks = undefined; - } - }); const copy = this.convertCompetencyFromClient(competency); return this.httpClient.post(`${this.resourceURL}/courses/${courseId}/competencies`, copy, { observe: 'response' }); } @@ -97,12 +91,6 @@ export class CompetencyService extends CourseCompetencyService { } update(competency: Competency, courseId: number): Observable { - competency.lectureUnitLinks?.forEach((link) => { - link.lectureUnit = undefined; - if (link.competency?.lectureUnitLinks) { - link.competency.lectureUnitLinks = undefined; - } - }); const copy = this.convertCompetencyFromClient(competency); return this.httpClient.put(`${this.resourceURL}/courses/${courseId}/competencies`, copy, { observe: 'response' }); } diff --git a/src/main/webapp/app/course/competencies/create/create-competency.component.ts b/src/main/webapp/app/course/competencies/create/create-competency.component.ts index aa30635759ef..267d9016631c 100644 --- a/src/main/webapp/app/course/competencies/create/create-competency.component.ts +++ b/src/main/webapp/app/course/competencies/create/create-competency.component.ts @@ -38,7 +38,7 @@ export class CreateCompetencyComponent extends CreateCourseCompetencyComponent { return; } - const { title, description, softDueDate, taxonomy, masteryThreshold, optional, lectureUnitLinks } = formData; + const { title, description, softDueDate, taxonomy, masteryThreshold, optional } = formData; this.competencyToCreate.title = title; this.competencyToCreate.description = description; @@ -46,7 +46,6 @@ export class CreateCompetencyComponent extends CreateCourseCompetencyComponent { this.competencyToCreate.taxonomy = taxonomy; this.competencyToCreate.masteryThreshold = masteryThreshold; this.competencyToCreate.optional = optional; - this.competencyToCreate.lectureUnitLinks = lectureUnitLinks; this.isLoading = true; diff --git a/src/main/webapp/app/course/competencies/create/create-prerequisite.component.ts b/src/main/webapp/app/course/competencies/create/create-prerequisite.component.ts index c5ecdf011ac4..befbdfb3478e 100644 --- a/src/main/webapp/app/course/competencies/create/create-prerequisite.component.ts +++ b/src/main/webapp/app/course/competencies/create/create-prerequisite.component.ts @@ -37,7 +37,7 @@ export class CreatePrerequisiteComponent extends CreateCourseCompetencyComponent return; } - const { title, description, softDueDate, taxonomy, masteryThreshold, optional, lectureUnitLinks } = formData; + const { title, description, softDueDate, taxonomy, masteryThreshold, optional } = formData; this.prerequisiteToCreate.title = title; this.prerequisiteToCreate.description = description; @@ -45,7 +45,6 @@ export class CreatePrerequisiteComponent extends CreateCourseCompetencyComponent this.prerequisiteToCreate.taxonomy = taxonomy; this.prerequisiteToCreate.masteryThreshold = masteryThreshold; this.prerequisiteToCreate.optional = optional; - this.prerequisiteToCreate.lectureUnitLinks = lectureUnitLinks; this.isLoading = true; diff --git a/src/main/webapp/app/course/competencies/edit/edit-competency.component.ts b/src/main/webapp/app/course/competencies/edit/edit-competency.component.ts index b5e24d7941c1..7ea6f4b22b76 100644 --- a/src/main/webapp/app/course/competencies/edit/edit-competency.component.ts +++ b/src/main/webapp/app/course/competencies/edit/edit-competency.component.ts @@ -56,10 +56,6 @@ export class EditCompetencyComponent extends EditCourseCompetencyComponent imple if (courseProgressResult.body) { this.competency.courseProgress = courseProgressResult.body; } - // server will send undefined instead of empty array, therefore we set it here as it is easier to handle - if (!this.competency.lectureUnitLinks) { - this.competency.lectureUnitLinks = []; - } } this.formData = { @@ -67,7 +63,6 @@ export class EditCompetencyComponent extends EditCourseCompetencyComponent imple title: this.competency.title, description: this.competency.description, softDueDate: this.competency.softDueDate, - lectureUnitLinks: this.competency.lectureUnitLinks, taxonomy: this.competency.taxonomy, masteryThreshold: this.competency.masteryThreshold, optional: this.competency.optional, @@ -78,7 +73,7 @@ export class EditCompetencyComponent extends EditCourseCompetencyComponent imple } updateCompetency(formData: CourseCompetencyFormData) { - const { title, description, softDueDate, taxonomy, masteryThreshold, optional, lectureUnitLinks } = formData; + const { title, description, softDueDate, taxonomy, masteryThreshold, optional } = formData; this.competency.title = title; this.competency.description = description; @@ -86,7 +81,6 @@ export class EditCompetencyComponent extends EditCourseCompetencyComponent imple this.competency.taxonomy = taxonomy; this.competency.masteryThreshold = masteryThreshold; this.competency.optional = optional; - this.competency.lectureUnitLinks = lectureUnitLinks; this.isLoading = true; diff --git a/src/main/webapp/app/course/competencies/edit/edit-prerequisite.component.ts b/src/main/webapp/app/course/competencies/edit/edit-prerequisite.component.ts index 9dcf0b1490d1..d11ecd71c7fb 100644 --- a/src/main/webapp/app/course/competencies/edit/edit-prerequisite.component.ts +++ b/src/main/webapp/app/course/competencies/edit/edit-prerequisite.component.ts @@ -57,10 +57,6 @@ export class EditPrerequisiteComponent extends EditCourseCompetencyComponent imp if (courseProgressResult.body) { this.prerequisite.courseProgress = courseProgressResult.body; } - // server will send undefined instead of empty array, therefore we set it here as it is easier to handle - if (!this.prerequisite.lectureUnitLinks) { - this.prerequisite.lectureUnitLinks = []; - } } this.formData = { @@ -68,7 +64,6 @@ export class EditPrerequisiteComponent extends EditCourseCompetencyComponent imp title: this.prerequisite.title, description: this.prerequisite.description, softDueDate: this.prerequisite.softDueDate, - lectureUnitLinks: this.prerequisite.lectureUnitLinks, taxonomy: this.prerequisite.taxonomy, masteryThreshold: this.prerequisite.masteryThreshold, optional: this.prerequisite.optional, @@ -79,7 +74,7 @@ export class EditPrerequisiteComponent extends EditCourseCompetencyComponent imp } updateCompetency(formData: CourseCompetencyFormData) { - const { title, description, softDueDate, taxonomy, masteryThreshold, optional, lectureUnitLinks } = formData; + const { title, description, softDueDate, taxonomy, masteryThreshold, optional } = formData; this.prerequisite.title = title; this.prerequisite.description = description; @@ -87,7 +82,6 @@ export class EditPrerequisiteComponent extends EditCourseCompetencyComponent imp this.prerequisite.taxonomy = taxonomy; this.prerequisite.masteryThreshold = masteryThreshold; this.prerequisite.optional = optional; - this.prerequisite.lectureUnitLinks = lectureUnitLinks; this.isLoading = true; diff --git a/src/main/webapp/app/course/competencies/forms/common-course-competency-form.component.html b/src/main/webapp/app/course/competencies/forms/common-course-competency-form.component.html index 6eda4fbaad0d..afd6368b570d 100644 --- a/src/main/webapp/app/course/competencies/forms/common-course-competency-form.component.html +++ b/src/main/webapp/app/course/competencies/forms/common-course-competency-form.component.html @@ -84,55 +84,4 @@
-
- - @if (lecturesOfCourseWithLectureUnits && lecturesOfCourseWithLectureUnits.length > 0) { -
- -
- @for (lecture of lecturesOfCourseWithLectureUnits; track lecture) { - - } -
-
- } @else { -
- } - @if (selectedLectureInDropdown) { -
- - - - - - - - - - - @for (lectureUnit of selectedLectureInDropdown.lectureUnits; track lectureUnit) { - - - - - - - } - -
id
{{ lectureUnit.id ? lectureUnit.id : '' }}{{ lectureUnit.type ? lectureUnit.type : '' }}{{ lectureUnitService.getLectureUnitName(lectureUnit) ? lectureUnitService.getLectureUnitName(lectureUnit) : '' }} - {{ - lectureUnitService.getLectureUnitReleaseDate(lectureUnit) - ? lectureUnitService.getLectureUnitReleaseDate(lectureUnit)!.format('MMM DD YYYY, HH:mm:ss') - : '' - }} -
-
- } -
diff --git a/src/main/webapp/app/course/competencies/forms/common-course-competency-form.component.ts b/src/main/webapp/app/course/competencies/forms/common-course-competency-form.component.ts index a29d855c865a..cd512a1d12f6 100644 --- a/src/main/webapp/app/course/competencies/forms/common-course-competency-form.component.ts +++ b/src/main/webapp/app/course/competencies/forms/common-course-competency-form.component.ts @@ -1,11 +1,9 @@ import { Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core'; import { FormControl, FormGroup } from '@angular/forms'; import { Lecture } from 'app/entities/lecture.model'; -import { LectureUnit } from 'app/entities/lecture-unit/lectureUnit.model'; import { TranslateService } from '@ngx-translate/core'; import { LectureUnitService } from 'app/lecture/lecture-unit/lecture-unit-management/lectureUnit.service'; -import { intersection } from 'lodash-es'; -import { CompetencyLectureUnitLink, CompetencyTaxonomy, CourseCompetency, CourseCompetencyValidators, DEFAULT_MASTERY_THRESHOLD } from 'app/entities/competency.model'; +import { CompetencyTaxonomy, CourseCompetency, CourseCompetencyValidators, DEFAULT_MASTERY_THRESHOLD } from 'app/entities/competency.model'; import { faQuestionCircle, faTimes } from '@fortawesome/free-solid-svg-icons'; import { CourseCompetencyFormData } from 'app/course/competencies/forms/course-competency-form.component'; import { ArtemisSharedModule } from 'app/shared/shared.module'; @@ -52,15 +50,11 @@ export class CommonCourseCompetencyFormComponent implements OnInit, OnChanges { @Input() courseCompetency: CourseCompetency; - @Output() - onLectureUnitSelectionChange = new EventEmitter(); @Output() onTitleOrDescriptionChange = new EventEmitter(); protected readonly competencyValidators = CourseCompetencyValidators; - selectedLectureInDropdown: Lecture; - selectedLectureUnitLinksInTable: CompetencyLectureUnitLink[] = []; suggestedTaxonomies: string[] = []; // Icons @@ -120,40 +114,7 @@ export class CommonCourseCompetencyFormComponent implements OnInit, OnChanges { private setFormValues(formData: CourseCompetencyFormData) { this.form.patchValue(formData); - if (formData.lectureUnitLinks) { - this.selectedLectureUnitLinksInTable = formData.lectureUnitLinks; - this.onLectureUnitSelectionChange.next(this.selectedLectureUnitLinksInTable); - } - } - - selectLectureInDropdown(lecture: Lecture) { - this.selectedLectureInDropdown = lecture; - } - - selectLectureUnitInTable(lectureUnit: LectureUnit) { - if (this.isLectureUnitAlreadySelectedInTable(lectureUnit)) { - this.selectedLectureUnitLinksInTable = this.selectedLectureUnitLinksInTable.filter((lectureUnitLink) => lectureUnitLink.lectureUnit?.id !== lectureUnit.id); - } else { - this.selectedLectureUnitLinksInTable.push(new CompetencyLectureUnitLink(this.courseCompetency, lectureUnit, 1)); - } - this.onLectureUnitSelectionChange.next(this.selectedLectureUnitLinksInTable); - } - - isLectureUnitAlreadySelectedInTable(lectureUnit: LectureUnit) { - return this.selectedLectureUnitLinksInTable.map((selectedLectureUnitLink) => selectedLectureUnitLink.lectureUnit?.id).includes(lectureUnit.id); } - - getLectureTitleForDropdown(lecture: Lecture) { - const noOfSelectedUnitsInLecture = intersection( - this.selectedLectureUnitLinksInTable.map((unitLink) => unitLink.lectureUnit?.id), - lecture.lectureUnits?.map((unit) => unit.id), - ).length; - return this.translateService.instant('artemisApp.courseCompetency.create.dropdown', { - lectureTitle: lecture.title, - noOfConnectedUnits: noOfSelectedUnitsInLecture, - }); - } - /** * Suggest some taxonomies based on keywords used in the title or description. * Triggered after the user changes the title or description input field. diff --git a/src/main/webapp/app/course/competencies/forms/competency/competency-form.component.html b/src/main/webapp/app/course/competencies/forms/competency/competency-form.component.html index 3e9358604917..3c6066444220 100644 --- a/src/main/webapp/app/course/competencies/forms/competency/competency-form.component.html +++ b/src/main/webapp/app/course/competencies/forms/competency/competency-form.component.html @@ -11,7 +11,6 @@ [averageStudentScore]="averageStudentScore" [form]="form" [courseCompetency]="competency" - (onLectureUnitSelectionChange)="onLectureUnitSelectionChange($event)" />
} } diff --git a/src/main/webapp/app/shared/competency-selection/competency-selection.component.ts b/src/main/webapp/app/shared/competency-selection/competency-selection.component.ts index 295d2b009f93..3da5e91c1990 100644 --- a/src/main/webapp/app/shared/competency-selection/competency-selection.component.ts +++ b/src/main/webapp/app/shared/competency-selection/competency-selection.component.ts @@ -122,7 +122,19 @@ export class CompetencySelectionComponent implements OnInit, ControlValueAccesso } } + updateLinkWeight(link: CompetencyLearningObjectLink, value: number) { + link.weight = value; + + this._onChange(this.selectedCompetencyLinks); + this.valueChange.emit(this.selectedCompetencyLinks); + } + writeValue(value?: CompetencyLearningObjectLink[]): void { + this.competencyLinks?.forEach((link) => { + const selectedLink = value?.find((value) => value.competency?.id === link.competency?.id); + link.weight = selectedLink?.weight ?? 0.5; + }); + if (value && this.competencyLinks) { // Compare the ids of the competencies instead of the whole objects const ids = value.map((el) => el.competency?.id); diff --git a/src/main/webapp/i18n/de/competency.json b/src/main/webapp/i18n/de/competency.json index aaef36b9f34a..f06f02951cda 100644 --- a/src/main/webapp/i18n/de/competency.json +++ b/src/main/webapp/i18n/de/competency.json @@ -117,7 +117,13 @@ "link": { "title": "Verbundene Kompetenzen", "lectureUnit": "Verbinde diese Vorlesungseinheit wahlweise mit einer oder mehreren Kompetenzen.", - "exercise": "Verbinde diese Übung wahlweise mit einer oder mehreren Kompetenzen." + "exercise": "Verbinde diese Übung wahlweise mit einer oder mehreren Kompetenzen.", + "weightTooltip": "Die Relevanz dieses Lernobjektes für die Kompetenz.", + "weightLabels": { + "low": "Niedrig", + "medium": "Mittel", + "high": "Hoch" + } }, "competencyCard": { "connectedLectureUnits": "Verbundene Vorlesungseinheiten", diff --git a/src/main/webapp/i18n/en/competency.json b/src/main/webapp/i18n/en/competency.json index e4aedbe82c77..6cac8ddf0256 100644 --- a/src/main/webapp/i18n/en/competency.json +++ b/src/main/webapp/i18n/en/competency.json @@ -117,7 +117,13 @@ "link": { "title": "Linked Competencies", "lectureUnit": "Optionally link this lecture unit to one or more competencies.", - "exercise": "Optionally link this exercise to one or more competencies." + "exercise": "Optionally link this exercise to one or more competencies.", + "weightTooltip": "The relevance of this learning object for the competency.", + "weightLabels": { + "low": "Low", + "medium": "Medium", + "high": "High" + } }, "competencyCard": { "connectedLectureUnits": "Linked Lecture Units", diff --git a/src/test/javascript/spec/component/shared/competency-selection.component.spec.ts b/src/test/javascript/spec/component/shared/competency-selection.component.spec.ts index a58b665e45cc..13cdf686b9c9 100644 --- a/src/test/javascript/spec/component/shared/competency-selection.component.spec.ts +++ b/src/test/javascript/spec/component/shared/competency-selection.component.spec.ts @@ -14,6 +14,7 @@ import { By } from '@angular/platform-browser'; import { CourseStorageService } from 'app/course/manage/course-storage.service'; import { ChangeDetectorRef } from '@angular/core'; import { CourseCompetencyService } from 'app/course/competencies/course-competency.service'; +import { Prerequisite } from 'app/entities/prerequisite.model'; describe('CompetencySelection', () => { let fixture: ComponentFixture; @@ -122,6 +123,22 @@ describe('CompetencySelection', () => { expect(component.selectedCompetencyLinks?.first()?.competency?.title).toBe('test'); }); + it('should update link weight when value is written', () => { + jest.spyOn(courseStorageService, 'getCourse').mockReturnValue({ + competencies: [{ id: 1, title: 'test' } as Competency, { id: 2, title: 'testAgain' } as Prerequisite, { id: 3, title: 'testMore' } as Competency], + }); + + fixture.detectChanges(); + + component.writeValue([ + new CompetencyLearningObjectLink({ id: 1, title: 'other' } as Competency, 0.5), + new CompetencyLearningObjectLink({ id: 3, title: 'otherMore' } as Competency, 1), + ]); + expect(component.selectedCompetencyLinks).toBeArrayOfSize(2); + expect(component.selectedCompetencyLinks?.first()?.weight).toBe(0.5); + expect(component.selectedCompetencyLinks?.last()?.weight).toBe(1); + }); + it('should trigger change detection after loading competencies', () => { jest.spyOn(courseStorageService, 'getCourse').mockReturnValue({ competencies: undefined }); const changeDetector = fixture.debugElement.injector.get(ChangeDetectorRef); From f285a0de1085fbc7e3a69a5339e021540918d0d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20St=C3=B6hr?= Date: Tue, 22 Oct 2024 22:26:09 +0200 Subject: [PATCH 17/25] Patrik --- .../atlas/repository/CompetencyLectureUnitLinkRepository.java | 2 +- .../modeling/repository/ModelingExerciseRepository.java | 3 ++- .../service/ProgrammingExerciseImportBasicService.java | 1 - .../aet/artemis/quiz/repository/QuizExerciseRepository.java | 3 ++- .../aet/artemis/text/repository/TextExerciseRepository.java | 3 ++- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyLectureUnitLinkRepository.java b/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyLectureUnitLinkRepository.java index 19377a3cdfd8..d245ee76c6b2 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyLectureUnitLinkRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CompetencyLectureUnitLinkRepository.java @@ -17,7 +17,7 @@ public interface CompetencyLectureUnitLinkRepository extends ArtemisJpaRepository { @Modifying - @Transactional + @Transactional // ok because of delete @Query(""" DELETE FROM CompetencyLectureUnitLink clul WHERE clul.lectureUnit.lecture.id = :lectureId diff --git a/src/main/java/de/tum/cit/aet/artemis/modeling/repository/ModelingExerciseRepository.java b/src/main/java/de/tum/cit/aet/artemis/modeling/repository/ModelingExerciseRepository.java index e2861d984506..5b25bfc46938 100644 --- a/src/main/java/de/tum/cit/aet/artemis/modeling/repository/ModelingExerciseRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/modeling/repository/ModelingExerciseRepository.java @@ -101,7 +101,8 @@ public interface ModelingExerciseRepository extends ArtemisJpaRepository newTestCaseIdByOldId = importTestCases(originalProgrammingExercise, importedExercise); final Map newTaskIdByOldId = importTasks(originalProgrammingExercise, importedExercise, newTestCaseIdByOldId); diff --git a/src/main/java/de/tum/cit/aet/artemis/quiz/repository/QuizExerciseRepository.java b/src/main/java/de/tum/cit/aet/artemis/quiz/repository/QuizExerciseRepository.java index fc0537611937..3d695cd8af56 100644 --- a/src/main/java/de/tum/cit/aet/artemis/quiz/repository/QuizExerciseRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/quiz/repository/QuizExerciseRepository.java @@ -78,7 +78,8 @@ public interface QuizExerciseRepository extends ArtemisJpaRepository Date: Thu, 24 Oct 2024 14:19:05 +0200 Subject: [PATCH 18/25] Use variables --- .../competency-selection.component.html | 14 ++++++++++---- .../competency-selection.component.ts | 8 +++++++- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/main/webapp/app/shared/competency-selection/competency-selection.component.html b/src/main/webapp/app/shared/competency-selection/competency-selection.component.html index bb55d0f1c42c..2bbf0a985611 100644 --- a/src/main/webapp/app/shared/competency-selection/competency-selection.component.html +++ b/src/main/webapp/app/shared/competency-selection/competency-selection.component.html @@ -34,13 +34,19 @@ @if (checkboxStates[competencyLink.competency.id]) { }

diff --git a/src/main/webapp/app/shared/competency-selection/competency-selection.component.ts b/src/main/webapp/app/shared/competency-selection/competency-selection.component.ts index 3da5e91c1990..464434a0ddb4 100644 --- a/src/main/webapp/app/shared/competency-selection/competency-selection.component.ts +++ b/src/main/webapp/app/shared/competency-selection/competency-selection.component.ts @@ -40,6 +40,12 @@ export class CompetencySelectionComponent implements OnInit, ControlValueAccesso // eslint-disable-next-line @typescript-eslint/no-unused-vars _onChange = (value: any) => {}; + protected readonly highLinkWeight = 1; + protected readonly mediumLinkWeight = 0.5; + protected readonly lowLinkWeight = 0.25; + protected readonly lowLinkWeightCutOff = 0.375; // halfway between low and medium + protected readonly mediumLinkWeightCutOff = 0.75; // halfway between medium and high + constructor( private route: ActivatedRoute, private courseStorageService: CourseStorageService, @@ -132,7 +138,7 @@ export class CompetencySelectionComponent implements OnInit, ControlValueAccesso writeValue(value?: CompetencyLearningObjectLink[]): void { this.competencyLinks?.forEach((link) => { const selectedLink = value?.find((value) => value.competency?.id === link.competency?.id); - link.weight = selectedLink?.weight ?? 0.5; + link.weight = selectedLink?.weight ?? this.mediumLinkWeight; }); if (value && this.competencyLinks) { From b4f1334b4cd00b89d31c5b976d4b8399cda4b819 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20St=C3=B6hr?= Date: Thu, 24 Oct 2024 14:34:13 +0200 Subject: [PATCH 19/25] Use SCREAMING_SNAKE_CASE --- .../competency-selection.component.html | 16 ++++++++-------- .../competency-selection.component.ts | 12 ++++++------ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/main/webapp/app/shared/competency-selection/competency-selection.component.html b/src/main/webapp/app/shared/competency-selection/competency-selection.component.html index 2bbf0a985611..149a07f27923 100644 --- a/src/main/webapp/app/shared/competency-selection/competency-selection.component.html +++ b/src/main/webapp/app/shared/competency-selection/competency-selection.component.html @@ -35,18 +35,18 @@ @if (checkboxStates[competencyLink.competency.id]) { }
diff --git a/src/main/webapp/app/shared/competency-selection/competency-selection.component.ts b/src/main/webapp/app/shared/competency-selection/competency-selection.component.ts index 464434a0ddb4..978b43c2d4f3 100644 --- a/src/main/webapp/app/shared/competency-selection/competency-selection.component.ts +++ b/src/main/webapp/app/shared/competency-selection/competency-selection.component.ts @@ -40,11 +40,11 @@ export class CompetencySelectionComponent implements OnInit, ControlValueAccesso // eslint-disable-next-line @typescript-eslint/no-unused-vars _onChange = (value: any) => {}; - protected readonly highLinkWeight = 1; - protected readonly mediumLinkWeight = 0.5; - protected readonly lowLinkWeight = 0.25; - protected readonly lowLinkWeightCutOff = 0.375; // halfway between low and medium - protected readonly mediumLinkWeightCutOff = 0.75; // halfway between medium and high + protected readonly HIGH_LINK_WEIGHT = 1; + protected readonly MEDIUM_LINK_WEIGHT = 0.5; + protected readonly LOW_LINK_WEIGHT = 0.25; + protected readonly LOW_LINK_WEIGHT_CUT_OFF = 0.375; // halfway between low and medium + protected readonly MEDIUM_LINK_WEIGHT_CUT_OFF = 0.75; // halfway between medium and high constructor( private route: ActivatedRoute, @@ -138,7 +138,7 @@ export class CompetencySelectionComponent implements OnInit, ControlValueAccesso writeValue(value?: CompetencyLearningObjectLink[]): void { this.competencyLinks?.forEach((link) => { const selectedLink = value?.find((value) => value.competency?.id === link.competency?.id); - link.weight = selectedLink?.weight ?? this.mediumLinkWeight; + link.weight = selectedLink?.weight ?? this.MEDIUM_LINK_WEIGHT; }); if (value && this.competencyLinks) { From 30f9583ffc6933ec3f956e9f979da9ab6d86201c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20St=C3=B6hr?= Date: Fri, 25 Oct 2024 11:40:20 +0200 Subject: [PATCH 20/25] Make Flo happy --- .../competencies-popover/competencies-popover.component.html | 4 +++- src/main/webapp/app/entities/competency.model.ts | 2 ++ .../course-competencies-details.component.ts | 3 ++- .../competency-selection/competency-selection.component.ts | 4 ++-- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/main/webapp/app/course/competencies/competencies-popover/competencies-popover.component.html b/src/main/webapp/app/course/competencies/competencies-popover/competencies-popover.component.html index 4d1c26d8b07a..21ad25ca8389 100644 --- a/src/main/webapp/app/course/competencies/competencies-popover/competencies-popover.component.html +++ b/src/main/webapp/app/course/competencies/competencies-popover/competencies-popover.component.html @@ -5,7 +5,9 @@ diff --git a/src/main/webapp/app/entities/competency.model.ts b/src/main/webapp/app/entities/competency.model.ts index 11def16ea085..245cfd71553c 100644 --- a/src/main/webapp/app/entities/competency.model.ts +++ b/src/main/webapp/app/entities/competency.model.ts @@ -46,6 +46,8 @@ export enum CourseCompetencyType { export const DEFAULT_MASTERY_THRESHOLD = 100; +export const MEDIUM_COMPETENCY_LINK_WEIGHT = 0.5; + export abstract class BaseCompetency implements BaseEntity { id?: number; title?: string; diff --git a/src/main/webapp/app/overview/course-competencies/course-competencies-details.component.ts b/src/main/webapp/app/overview/course-competencies/course-competencies-details.component.ts index 602f50364f34..9dde7549efad 100644 --- a/src/main/webapp/app/overview/course-competencies/course-competencies-details.component.ts +++ b/src/main/webapp/app/overview/course-competencies/course-competencies-details.component.ts @@ -7,6 +7,7 @@ import { CompetencyLectureUnitLink, CompetencyProgress, ConfidenceReason, + MEDIUM_COMPETENCY_LINK_WEIGHT, getConfidence, getIcon, getMastery, @@ -125,7 +126,7 @@ export class CourseCompetenciesDetailsComponent implements OnInit, OnDestroy { const exerciseUnit = new ExerciseUnit(); exerciseUnit.id = exerciseLink.exercise?.id; exerciseUnit.exercise = exerciseLink.exercise; - return new CompetencyLectureUnitLink(this.competency, exerciseUnit as LectureUnit, 0.5); + return new CompetencyLectureUnitLink(this.competency, exerciseUnit as LectureUnit, MEDIUM_COMPETENCY_LINK_WEIGHT); }), ); } diff --git a/src/main/webapp/app/shared/competency-selection/competency-selection.component.ts b/src/main/webapp/app/shared/competency-selection/competency-selection.component.ts index 295d2b009f93..224abeb3cabe 100644 --- a/src/main/webapp/app/shared/competency-selection/competency-selection.component.ts +++ b/src/main/webapp/app/shared/competency-selection/competency-selection.component.ts @@ -1,7 +1,7 @@ import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output, forwardRef } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { faQuestionCircle } from '@fortawesome/free-solid-svg-icons'; -import { CompetencyLearningObjectLink, CourseCompetency, getIcon } from 'app/entities/competency.model'; +import { CompetencyLearningObjectLink, CourseCompetency, MEDIUM_COMPETENCY_LINK_WEIGHT, getIcon } from 'app/entities/competency.model'; import { ActivatedRoute } from '@angular/router'; import { CourseStorageService } from 'app/course/manage/course-storage.service'; import { finalize } from 'rxjs'; @@ -89,7 +89,7 @@ export class CompetencySelectionComponent implements OnInit, ControlValueAccesso // Remove unnecessary properties competency.course = undefined; competency.userProgress = undefined; - return new CompetencyLearningObjectLink(competency, 0.5); + return new CompetencyLearningObjectLink(competency, MEDIUM_COMPETENCY_LINK_WEIGHT); }); this.checkboxStates = this.competencyLinks.reduce( (states, competencyLink) => { From 7c0d345364f51ff71aa6c88a8436ccf2a2ed8881 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20St=C3=B6hr?= Date: Fri, 25 Oct 2024 11:47:37 +0200 Subject: [PATCH 21/25] Make Flo happy transitively --- .../webapp/app/entities/competency.model.ts | 4 ++++ .../competency-selection.component.html | 16 ++++++------- .../competency-selection.component.ts | 23 +++++++++++++------ 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src/main/webapp/app/entities/competency.model.ts b/src/main/webapp/app/entities/competency.model.ts index 245cfd71553c..51afaa1b18a3 100644 --- a/src/main/webapp/app/entities/competency.model.ts +++ b/src/main/webapp/app/entities/competency.model.ts @@ -46,7 +46,11 @@ export enum CourseCompetencyType { export const DEFAULT_MASTERY_THRESHOLD = 100; +export const HIGH_COMPETENCY_LINK_WEIGHT = 1; export const MEDIUM_COMPETENCY_LINK_WEIGHT = 0.5; +export const LOW_COMPETENCY_LINK_WEIGHT = 0.25; +export const LOW_COMPETENCY_LINK_WEIGHT_CUT_OFF = 0.375; // halfway between low and medium +export const MEDIUM_COMPETENCY_LINK_WEIGHT_CUT_OFF = 0.75; // halfway between medium and high export abstract class BaseCompetency implements BaseEntity { id?: number; diff --git a/src/main/webapp/app/shared/competency-selection/competency-selection.component.html b/src/main/webapp/app/shared/competency-selection/competency-selection.component.html index 149a07f27923..7fba47389226 100644 --- a/src/main/webapp/app/shared/competency-selection/competency-selection.component.html +++ b/src/main/webapp/app/shared/competency-selection/competency-selection.component.html @@ -35,18 +35,18 @@ @if (checkboxStates[competencyLink.competency.id]) { } diff --git a/src/main/webapp/app/shared/competency-selection/competency-selection.component.ts b/src/main/webapp/app/shared/competency-selection/competency-selection.component.ts index 621d17e03ad1..41f2dcf0e67a 100644 --- a/src/main/webapp/app/shared/competency-selection/competency-selection.component.ts +++ b/src/main/webapp/app/shared/competency-selection/competency-selection.component.ts @@ -1,7 +1,16 @@ import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output, forwardRef } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { faQuestionCircle } from '@fortawesome/free-solid-svg-icons'; -import { CompetencyLearningObjectLink, CourseCompetency, MEDIUM_COMPETENCY_LINK_WEIGHT, getIcon } from 'app/entities/competency.model'; +import { + CompetencyLearningObjectLink, + CourseCompetency, + HIGH_COMPETENCY_LINK_WEIGHT, + LOW_COMPETENCY_LINK_WEIGHT, + LOW_COMPETENCY_LINK_WEIGHT_CUT_OFF, + MEDIUM_COMPETENCY_LINK_WEIGHT, + MEDIUM_COMPETENCY_LINK_WEIGHT_CUT_OFF, + getIcon, +} from 'app/entities/competency.model'; import { ActivatedRoute } from '@angular/router'; import { CourseStorageService } from 'app/course/manage/course-storage.service'; import { finalize } from 'rxjs'; @@ -40,11 +49,11 @@ export class CompetencySelectionComponent implements OnInit, ControlValueAccesso // eslint-disable-next-line @typescript-eslint/no-unused-vars _onChange = (value: any) => {}; - protected readonly HIGH_LINK_WEIGHT = 1; - protected readonly MEDIUM_LINK_WEIGHT = 0.5; - protected readonly LOW_LINK_WEIGHT = 0.25; - protected readonly LOW_LINK_WEIGHT_CUT_OFF = 0.375; // halfway between low and medium - protected readonly MEDIUM_LINK_WEIGHT_CUT_OFF = 0.75; // halfway between medium and high + protected readonly HIGH_COMPETENCY_LINK_WEIGHT = HIGH_COMPETENCY_LINK_WEIGHT; + protected readonly MEDIUM_COMPETENCY_LINK_WEIGHT = MEDIUM_COMPETENCY_LINK_WEIGHT; + protected readonly LOW_COMPETENCY_LINK_WEIGHT = LOW_COMPETENCY_LINK_WEIGHT; + protected readonly LOW_COMPETENCY_LINK_WEIGHT_CUT_OFF = LOW_COMPETENCY_LINK_WEIGHT_CUT_OFF; // halfway between low and medium + protected readonly MEDIUM_COMPETENCY_LINK_WEIGHT_CUT_OFF = MEDIUM_COMPETENCY_LINK_WEIGHT_CUT_OFF; // halfway between medium and high constructor( private route: ActivatedRoute, @@ -138,7 +147,7 @@ export class CompetencySelectionComponent implements OnInit, ControlValueAccesso writeValue(value?: CompetencyLearningObjectLink[]): void { this.competencyLinks?.forEach((link) => { const selectedLink = value?.find((value) => value.competency?.id === link.competency?.id); - link.weight = selectedLink?.weight ?? this.MEDIUM_LINK_WEIGHT; + link.weight = selectedLink?.weight ?? MEDIUM_COMPETENCY_LINK_WEIGHT; }); if (value && this.competencyLinks) { From 9efb40f206599245a0fb39e14fd4cd538cbec0ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20St=C3=B6hr?= Date: Fri, 25 Oct 2024 12:10:07 +0200 Subject: [PATCH 22/25] Fix and combine test --- .../competency-popover.component.spec.ts | 49 ++++++++----------- 1 file changed, 20 insertions(+), 29 deletions(-) diff --git a/src/test/javascript/spec/component/competencies/competency-popover.component.spec.ts b/src/test/javascript/spec/component/competencies/competency-popover.component.spec.ts index 8834a670b530..dfd4771068e3 100644 --- a/src/test/javascript/spec/component/competencies/competency-popover.component.spec.ts +++ b/src/test/javascript/spec/component/competencies/competency-popover.component.spec.ts @@ -54,33 +54,24 @@ describe('CompetencyPopoverComponent', () => { expect(competencyPopoverComponent).toBeDefined(); }); - it('should navigate to course competencies', fakeAsync(() => { - const location: Location = TestBed.inject(Location); - competencyPopoverComponent.navigateTo = 'courseCompetencies'; - competencyPopoverComponent.competencyLinks = [{ weight: 1 }]; - competencyPopoverComponent.courseId = 1; - competencyPopoverComponentFixture.detectChanges(); - const popoverButton = competencyPopoverComponentFixture.debugElement.nativeElement.querySelector('button'); - popoverButton.click(); - tick(); - const anchor = competencyPopoverComponentFixture.debugElement.query(By.css('a')).nativeElement; - anchor.click(); - tick(); - expect(location.path()).toBe('/courses/1/competencies'); - })); - - it('should navigate to competency management', fakeAsync(() => { - const location: Location = TestBed.inject(Location); - competencyPopoverComponent.navigateTo = 'competencyManagement'; - competencyPopoverComponent.competencyLinks = [{ weight: 1 }]; - competencyPopoverComponent.courseId = 1; - competencyPopoverComponentFixture.detectChanges(); - const popoverButton = competencyPopoverComponentFixture.debugElement.nativeElement.querySelector('button'); - popoverButton.click(); - tick(); - const anchor = competencyPopoverComponentFixture.debugElement.query(By.css('a')).nativeElement; - anchor.click(); - tick(); - expect(location.path()).toBe('/course-management/1/competency-management'); - })); + it.each([ + ['courseCompetencies', '/courses/1/competencies'], + ['competencyManagement', '/course-management/1/competency-management'], + ])( + 'should navigate', + fakeAsync((navigateTo: 'competencyManagement' | 'courseCompetencies', expectedPath: string) => { + const location: Location = TestBed.inject(Location); + competencyPopoverComponent.navigateTo = navigateTo; + competencyPopoverComponent.competencyLinks = [{ competency: { id: 1, title: 'competency' }, weight: 1 }]; + competencyPopoverComponent.courseId = 1; + competencyPopoverComponentFixture.detectChanges(); + const popoverButton = competencyPopoverComponentFixture.debugElement.nativeElement.querySelector('button'); + popoverButton.click(); + tick(); + const anchor = competencyPopoverComponentFixture.debugElement.query(By.css('a')).nativeElement; + anchor.click(); + tick(); + expect(location.path()).toBe(expectedPath); + }), + ); }); From 19c6044b9c93d5d70d2d438c0f03f5c07d82d26c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20St=C3=B6hr?= Date: Mon, 28 Oct 2024 13:04:49 +0100 Subject: [PATCH 23/25] Increase test coverage --- .../competencies/course-competency.service.ts | 31 +++++++++++------- .../course-competencies-details.component.ts | 32 ++++++++++++------- 2 files changed, 40 insertions(+), 23 deletions(-) diff --git a/src/main/webapp/app/course/competencies/course-competency.service.ts b/src/main/webapp/app/course/competencies/course-competency.service.ts index d5d4c07bd458..e7a17d6d49ea 100644 --- a/src/main/webapp/app/course/competencies/course-competency.service.ts +++ b/src/main/webapp/app/course/competencies/course-competency.service.ts @@ -3,6 +3,7 @@ import { HttpClient, HttpResponse } from '@angular/common/http'; import { Observable } from 'rxjs'; import { Competency, + CompetencyExerciseLink, CompetencyJol, CompetencyProgress, CompetencyRelation, @@ -17,7 +18,6 @@ import { convertDateFromClient, convertDateFromServer } from 'app/utils/date.uti import { CompetencyPageableSearch, SearchResult } from 'app/shared/table/pageable-table'; import { HttpParams } from '@angular/common/http'; import { ExerciseService } from 'app/exercises/shared/exercise/exercise.service'; -import { Prerequisite } from 'app/entities/prerequisite.model'; import { LectureUnitService } from 'app/lecture/lecture-unit/lecture-unit-management/lectureUnit.service'; import { AccountService } from 'app/core/auth/account.service'; import { CompetencyRecommendation } from 'app/course/competencies/generate-competencies/generate-competencies.component'; @@ -200,33 +200,42 @@ export class CourseCompetencyService { if (res.body?.course) { this.accountService.setAccessRightsForCourse(res.body.course); } - res.body?.exerciseLinks?.forEach((exerciseLink) => { + this.convertExerciseLinksFromServer(res.body?.exerciseLinks); + + return res; + } + + private convertExerciseLinksFromServer(exerciseLinks: CompetencyExerciseLink[] | undefined) { + exerciseLinks?.forEach((exerciseLink) => { exerciseLink.exercise = ExerciseService.convertExerciseDatesFromServer(exerciseLink.exercise); ExerciseService.parseExerciseCategories(exerciseLink.exercise); if (exerciseLink.exercise) { this.accountService.setAccessRightsForExercise(exerciseLink.exercise); } }); - - return res; } - protected convertCompetencyFromClient(prerequisite: Prerequisite): Prerequisite { - const copy = Object.assign({}, prerequisite, { - softDueDate: convertDateFromClient(prerequisite.softDueDate), + protected convertCompetencyFromClient(courseCompetency: CourseCompetency): CourseCompetency { + const copy = Object.assign({}, courseCompetency, { + softDueDate: convertDateFromClient(courseCompetency.softDueDate), }); - copy.lectureUnitLinks?.forEach((lectureUnitLink) => { + + this.convertCompetencyLinksFromClient(copy); + + return copy; + } + + private convertCompetencyLinksFromClient(courseCompetency: CourseCompetency) { + courseCompetency.lectureUnitLinks?.forEach((lectureUnitLink) => { if (lectureUnitLink.lectureUnit) { lectureUnitLink.lectureUnit = this.lectureUnitService.convertLectureUnitDatesFromClient(lectureUnitLink.lectureUnit); } }); - copy.exerciseLinks?.forEach((exerciseLink) => { + courseCompetency.exerciseLinks?.forEach((exerciseLink) => { if (exerciseLink.exercise) { exerciseLink.exercise = ExerciseService.convertExerciseFromClient(exerciseLink.exercise); } }); - - return copy; } /** diff --git a/src/main/webapp/app/overview/course-competencies/course-competencies-details.component.ts b/src/main/webapp/app/overview/course-competencies/course-competencies-details.component.ts index 9dde7549efad..065a6d802c64 100644 --- a/src/main/webapp/app/overview/course-competencies/course-competencies-details.component.ts +++ b/src/main/webapp/app/overview/course-competencies/course-competencies-details.component.ts @@ -118,24 +118,32 @@ export class CourseCompetenciesDetailsComponent implements OnInit, OnDestroy { } } - if (this.competency.exerciseLinks) { - // Add exercises as lecture units for display - this.competency.lectureUnitLinks = this.competency.lectureUnitLinks ?? []; - this.competency.lectureUnitLinks.push( - ...this.competency.exerciseLinks.map((exerciseLink) => { - const exerciseUnit = new ExerciseUnit(); - exerciseUnit.id = exerciseLink.exercise?.id; - exerciseUnit.exercise = exerciseLink.exercise; - return new CompetencyLectureUnitLink(this.competency, exerciseUnit as LectureUnit, MEDIUM_COMPETENCY_LINK_WEIGHT); - }), - ); - } + this.handleExerciseLinks(); + this.isLoading = false; }, error: (errorResponse: HttpErrorResponse) => onError(this.alertService, errorResponse), }); } + /** + * Add exercises as lecture units for display + * @private + */ + private handleExerciseLinks() { + if (this.competency.exerciseLinks) { + this.competency.lectureUnitLinks = this.competency.lectureUnitLinks ?? []; + this.competency.lectureUnitLinks.push( + ...this.competency.exerciseLinks.map((exerciseLink) => { + const exerciseUnit = new ExerciseUnit(); + exerciseUnit.id = exerciseLink.exercise?.id; + exerciseUnit.exercise = exerciseLink.exercise; + return new CompetencyLectureUnitLink(this.competency, exerciseUnit as LectureUnit, MEDIUM_COMPETENCY_LINK_WEIGHT); + }), + ); + } + } + showFireworksIfMastered() { if (this.mastery >= 100 && !this.showFireworks) { setTimeout(() => (this.showFireworks = true), 1000); From f5430263f454af986fcd81a397a2717e46f00a69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20St=C3=B6hr?= Date: Mon, 28 Oct 2024 13:06:31 +0100 Subject: [PATCH 24/25] Fix constructor order --- .../CompetencyLearningObjectLink.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLearningObjectLink.java b/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLearningObjectLink.java index 7791376aa08a..9d6cef6115ce 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLearningObjectLink.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/domain/competency/CompetencyLearningObjectLink.java @@ -18,6 +18,15 @@ public abstract class CompetencyLearningObjectLink implements Serializable { @Column(name = "link_weight") protected double weight; + public CompetencyLearningObjectLink(CourseCompetency competency, double weight) { + this.competency = competency; + this.weight = weight; + } + + public CompetencyLearningObjectLink() { + // Empty constructor for Spring + } + public CourseCompetency getCompetency() { return competency; } @@ -33,13 +42,4 @@ public double getWeight() { public void setWeight(double weight) { this.weight = weight; } - - public CompetencyLearningObjectLink(CourseCompetency competency, double weight) { - this.competency = competency; - this.weight = weight; - } - - public CompetencyLearningObjectLink() { - // Empty constructor for Spring - } } From e1bb3880149cfaaaad3bb89b3a67462c223d563c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20St=C3=B6hr?= Date: Wed, 30 Oct 2024 11:50:04 +0100 Subject: [PATCH 25/25] Fix merge conflict --- .../problem/programming-exercise-problem.component.html | 4 ++-- .../programming-exercise-information.component.ts | 0 .../programming-exercise-problem.component.html | 0 3 files changed, 2 insertions(+), 2 deletions(-) delete mode 100644 src/main/webapp/app/exercises/programming/manage/update/update-components/programming-exercise-information.component.ts delete mode 100644 src/main/webapp/app/exercises/programming/manage/update/update-components/programming-exercise-problem.component.html diff --git a/src/main/webapp/app/exercises/programming/manage/update/update-components/problem/programming-exercise-problem.component.html b/src/main/webapp/app/exercises/programming/manage/update/update-components/problem/programming-exercise-problem.component.html index f6e3f325ed6c..4e4009b37896 100644 --- a/src/main/webapp/app/exercises/programming/manage/update/update-components/problem/programming-exercise-problem.component.html +++ b/src/main/webapp/app/exercises/programming/manage/update/update-components/problem/programming-exercise-problem.component.html @@ -50,8 +50,8 @@

} diff --git a/src/main/webapp/app/exercises/programming/manage/update/update-components/programming-exercise-information.component.ts b/src/main/webapp/app/exercises/programming/manage/update/update-components/programming-exercise-information.component.ts deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/src/main/webapp/app/exercises/programming/manage/update/update-components/programming-exercise-problem.component.html b/src/main/webapp/app/exercises/programming/manage/update/update-components/programming-exercise-problem.component.html deleted file mode 100644 index e69de29bb2d1..000000000000