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 38651ce86558..a6f371de0da7 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 @@ -56,14 +56,6 @@ public interface CompetencyRepository extends ArtemisJpaRepository findByIdWithLectureUnitsAndExercises(@Param("competencyId") long competencyId); - default Competency findByIdWithLectureUnitsAndExercisesElseThrow(long competencyId) { - return getValueElseThrow(findByIdWithLectureUnitsAndExercises(competencyId), competencyId); - } - - default Competency findByIdWithLectureUnitsElseThrow(long competencyId) { - return getValueElseThrow(findByIdWithLectureUnits(competencyId), competencyId); - } - long countByCourse(Course course); List findByCourseIdOrderById(long courseId); 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 bba103f436d9..051666bfbb34 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 @@ -14,7 +14,6 @@ import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; -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.dto.metrics.CompetencyExerciseMasteryCalculationDTO; import de.tum.cit.aet.artemis.atlas.dto.metrics.CompetencyLectureUnitMasteryCalculationDTO; @@ -80,10 +79,6 @@ public interface CourseCompetencyRepository extends ArtemisJpaRepository findByIdWithExercisesAndLectureUnitsAndLectures(@Param("id") long id); - default CourseCompetency findByIdWithExercisesAndLectureUnitsAndLecturesElseThrow(long id) { - return getValueElseThrow(findByIdWithExercisesAndLectureUnitsAndLectures(id), id); - } - @Query(""" SELECT c FROM CourseCompetency c @@ -259,40 +254,6 @@ Page findForImportAndUserHasAccessToCourse(@Param("partialTitl """) Optional findByIdWithLectureUnitsAndExercises(@Param("competencyId") long competencyId); - default CourseCompetency findByIdWithLectureUnitsElseThrow(long competencyId) { - return getValueElseThrow(findByIdWithLectureUnits(competencyId), competencyId); - } - - default CourseCompetency findByIdWithExercisesAndLectureUnitsElseThrow(long competencyId) { - return getValueElseThrow(findByIdWithExercisesAndLectureUnits(competencyId), competencyId); - } - - default CourseCompetency findByIdWithExercisesAndLectureUnitsBidirectionalElseThrow(long competencyId) { - return getValueElseThrow(findByIdWithExercisesAndLectureUnitsBidirectional(competencyId), competencyId); - } - - /** - * Finds the set of ids of course competencies that are linked to a given learning object - * - * @param learningObject the learning object to find the course competencies for - * @return the set of ids of course competencies linked to the learning object - */ - default Set findAllIdsByLearningObject(LearningObject learningObject) { - return switch (learningObject) { - case LectureUnit lectureUnit -> findAllIdsByLectureUnit(lectureUnit); - case Exercise exercise -> findAllIdsByExercise(exercise); - default -> throw new IllegalArgumentException("Unknown LearningObject type: " + learningObject.getClass()); - }; - } - - default CourseCompetency findByIdWithExercisesElseThrow(long competencyId) { - return getValueElseThrow(findByIdWithExercises(competencyId), competencyId); - } - - default CourseCompetency findByIdWithLectureUnitsAndExercisesElseThrow(long competencyId) { - return getValueElseThrow(findByIdWithLectureUnitsAndExercises(competencyId), competencyId); - } - List findByCourseIdOrderById(long courseId); boolean existsByIdAndCourseId(long competencyId, long courseId); diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/repository/simple/CompetencySimpleService.java b/src/main/java/de/tum/cit/aet/artemis/atlas/repository/simple/CompetencySimpleService.java new file mode 100644 index 000000000000..da854465b54b --- /dev/null +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/repository/simple/CompetencySimpleService.java @@ -0,0 +1,34 @@ +package de.tum.cit.aet.artemis.atlas.repository.simple; + +import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE; + +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Service; + +import de.tum.cit.aet.artemis.atlas.domain.competency.Competency; +import de.tum.cit.aet.artemis.atlas.repository.CompetencyRepository; +import de.tum.cit.aet.artemis.core.repository.base.ISimpleService; + +@Profile(PROFILE_CORE) +@Service +public class CompetencySimpleService implements ISimpleService { + + private final CompetencyRepository competencyRepository; + + @Override + public String getEntityName() { + return "Competency"; + } + + public CompetencySimpleService(CompetencyRepository competencyRepository) { + this.competencyRepository = competencyRepository; + } + + public Competency findByIdWithLectureUnitsAndExercisesElseThrow(long competencyId) { + return getValueElseThrow(competencyRepository.findByIdWithLectureUnitsAndExercises(competencyId)); + } + + public Competency findByIdWithLectureUnitsElseThrow(long competencyId) { + return getValueElseThrow(competencyRepository.findByIdWithLectureUnitsAndExercises(competencyId)); + } +} diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/repository/simple/CourseCompetencySimpleService.java b/src/main/java/de/tum/cit/aet/artemis/atlas/repository/simple/CourseCompetencySimpleService.java new file mode 100644 index 000000000000..0fa4684bd3e2 --- /dev/null +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/repository/simple/CourseCompetencySimpleService.java @@ -0,0 +1,65 @@ +package de.tum.cit.aet.artemis.atlas.repository.simple; + +import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE; + +import java.util.Set; + +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Service; + +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.repository.CourseCompetencyRepository; +import de.tum.cit.aet.artemis.core.repository.base.ISimpleService; +import de.tum.cit.aet.artemis.exercise.domain.Exercise; +import de.tum.cit.aet.artemis.lecture.domain.LectureUnit; + +@Profile(PROFILE_CORE) +@Service +public class CourseCompetencySimpleService implements ISimpleService { + + private final CourseCompetencyRepository courseCompetencyRepository; + + @Override + public String getEntityName() { + return "CourseCompetency"; + } + + public CourseCompetencySimpleService(CourseCompetencyRepository courseCompetencyRepository) { + this.courseCompetencyRepository = courseCompetencyRepository; + } + + public CourseCompetency findByIdWithExercisesAndLectureUnitsAndLecturesElseThrow(long id) { + return getValueElseThrow(courseCompetencyRepository.findByIdWithExercisesAndLectureUnitsAndLectures(id)); + } + + public CourseCompetency findByIdWithExercisesAndLectureUnitsElseThrow(long competencyId) { + return getValueElseThrow(courseCompetencyRepository.findByIdWithExercisesAndLectureUnits(competencyId)); + } + + public CourseCompetency findByIdWithExercisesAndLectureUnitsBidirectionalElseThrow(long competencyId) { + return getValueElseThrow(courseCompetencyRepository.findByIdWithExercisesAndLectureUnitsBidirectional(competencyId)); + } + + /** + * Finds the set of ids of course competencies that are linked to a given learning object + * + * @param learningObject the learning object to find the course competencies for + * @return the set of ids of course competencies linked to the learning object + */ + public Set findAllIdsByLearningObject(LearningObject learningObject) { + return switch (learningObject) { + case LectureUnit lectureUnit -> courseCompetencyRepository.findAllIdsByLectureUnit(lectureUnit); + case Exercise exercise -> courseCompetencyRepository.findAllIdsByExercise(exercise); + default -> throw new IllegalArgumentException("Unknown LearningObject type: " + learningObject.getClass()); + }; + } + + public CourseCompetency findByIdWithExercisesElseThrow(long competencyId) { + return getValueElseThrow(courseCompetencyRepository.findByIdWithExercises(competencyId)); + } + + public CourseCompetency findByIdWithLectureUnitsAndExercisesElseThrow(long competencyId) { + return getValueElseThrow(courseCompetencyRepository.findByIdWithLectureUnitsAndExercises(competencyId)); + } +} 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 813408442238..756f94280905 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 @@ -31,6 +31,7 @@ import de.tum.cit.aet.artemis.atlas.dto.metrics.CompetencyLectureUnitMasteryCalculationDTO; import de.tum.cit.aet.artemis.atlas.repository.CompetencyProgressRepository; import de.tum.cit.aet.artemis.atlas.repository.CourseCompetencyRepository; +import de.tum.cit.aet.artemis.atlas.repository.simple.CourseCompetencySimpleService; import de.tum.cit.aet.artemis.atlas.service.learningpath.LearningPathService; import de.tum.cit.aet.artemis.core.domain.Course; import de.tum.cit.aet.artemis.core.domain.User; @@ -62,6 +63,8 @@ public class CompetencyProgressService { private final CourseCompetencyRepository courseCompetencyRepository; + private final CourseCompetencySimpleService courseCompetencySimpleService; + private static final int MIN_EXERCISES_RECENCY_CONFIDENCE = 3; private static final int MAX_SUBMISSIONS_FOR_QUICK_SOLVE_HEURISTIC = 3; @@ -75,13 +78,14 @@ public class CompetencyProgressService { private static final double CONFIDENCE_REASON_DEADZONE = 0.05; public CompetencyProgressService(CompetencyProgressRepository competencyProgressRepository, LearningPathService learningPathService, - ParticipantScoreService participantScoreService, LectureUnitCompletionRepository lectureUnitCompletionRepository, - CourseCompetencyRepository courseCompetencyRepository) { + ParticipantScoreService participantScoreService, LectureUnitCompletionRepository lectureUnitCompletionRepository, CourseCompetencyRepository courseCompetencyRepository, + CourseCompetencySimpleService courseCompetencySimpleService) { this.competencyProgressRepository = competencyProgressRepository; this.learningPathService = learningPathService; this.participantScoreService = participantScoreService; this.lectureUnitCompletionRepository = lectureUnitCompletionRepository; this.courseCompetencyRepository = courseCompetencyRepository; + this.courseCompetencySimpleService = courseCompetencySimpleService; } /** @@ -104,7 +108,7 @@ public void updateProgressByLearningObjectForParticipantAsync(LearningObject lea @Async public void updateProgressByLearningObjectAsync(LearningObject learningObject) { SecurityUtils.setAuthorizationObject(); // Required for async - Set competencyIds = courseCompetencyRepository.findAllIdsByLearningObject(learningObject); + Set competencyIds = courseCompetencySimpleService.findAllIdsByLearningObject(learningObject); for (long competencyId : competencyIds) { Set users = competencyProgressRepository.findAllByCompetencyId(competencyId).stream().map(CompetencyProgress::getUser).collect(Collectors.toSet()); @@ -183,7 +187,7 @@ private void updateProgressByCompetencyIdsAndLearningObject(Set competency * @param users The users for which to update the progress */ public void updateProgressByLearningObjectSync(LearningObject learningObject, Set users) { - Set competencyIds = courseCompetencyRepository.findAllIdsByLearningObject(learningObject); + Set competencyIds = courseCompetencySimpleService.findAllIdsByLearningObject(learningObject); for (long competencyId : competencyIds) { log.debug("Updating competency progress synchronously for {} users.", users.size()); 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 3b0a4bc083c9..954cb530daff 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 @@ -22,6 +22,8 @@ import de.tum.cit.aet.artemis.atlas.repository.CompetencyRepository; import de.tum.cit.aet.artemis.atlas.repository.CourseCompetencyRepository; import de.tum.cit.aet.artemis.atlas.repository.StandardizedCompetencyRepository; +import de.tum.cit.aet.artemis.atlas.repository.simple.CompetencySimpleService; +import de.tum.cit.aet.artemis.atlas.repository.simple.CourseCompetencySimpleService; import de.tum.cit.aet.artemis.atlas.service.LearningObjectImportService; import de.tum.cit.aet.artemis.atlas.service.learningpath.LearningPathService; import de.tum.cit.aet.artemis.core.domain.Course; @@ -42,17 +44,21 @@ public class CompetencyService extends CourseCompetencyService { private final CompetencyRepository competencyRepository; + private final CompetencySimpleService competencySimpleService; + private final CompetencyExerciseLinkRepository competencyExerciseLinkRepository; public CompetencyService(CompetencyRepository competencyRepository, AuthorizationCheckService authCheckService, CompetencyRelationRepository competencyRelationRepository, - LearningPathService learningPathService, CompetencyProgressService competencyProgressService, LectureUnitService lectureUnitService, - CompetencyProgressRepository competencyProgressRepository, LectureUnitCompletionRepository lectureUnitCompletionRepository, + CourseCompetencySimpleService courseCompetencySimpleService, LearningPathService learningPathService, CompetencyProgressService competencyProgressService, + LectureUnitService lectureUnitService, CompetencyProgressRepository competencyProgressRepository, LectureUnitCompletionRepository lectureUnitCompletionRepository, StandardizedCompetencyRepository standardizedCompetencyRepository, CourseCompetencyRepository courseCompetencyRepository, ExerciseService exerciseService, LearningObjectImportService learningObjectImportService, CompetencyLectureUnitLinkRepository competencyLectureUnitLinkRepository, CourseRepository courseRepository, - CompetencyExerciseLinkRepository competencyExerciseLinkRepository) { - super(competencyProgressRepository, courseCompetencyRepository, competencyRelationRepository, competencyProgressService, exerciseService, lectureUnitService, - learningPathService, authCheckService, standardizedCompetencyRepository, lectureUnitCompletionRepository, learningObjectImportService, courseRepository); + CompetencySimpleService competencySimpleService, CompetencyExerciseLinkRepository competencyExerciseLinkRepository) { + super(competencyProgressRepository, courseCompetencyRepository, courseCompetencySimpleService, competencyRelationRepository, competencyProgressService, exerciseService, + lectureUnitService, learningPathService, authCheckService, standardizedCompetencyRepository, lectureUnitCompletionRepository, learningObjectImportService, + courseRepository); this.competencyRepository = competencyRepository; + this.competencySimpleService = competencySimpleService; this.competencyExerciseLinkRepository = competencyExerciseLinkRepository; } @@ -100,7 +106,7 @@ public List createCompetencies(List competencies, Course * @return The found competency */ public Competency findCompetencyWithExercisesAndLectureUnitsAndProgressForUser(Long competencyId, Long userId) { - Competency competency = competencyRepository.findByIdWithLectureUnitsAndExercisesElseThrow(competencyId); + Competency competency = competencySimpleService.findByIdWithLectureUnitsAndExercisesElseThrow(competencyId); return findProgressAndLectureUnitCompletionsForUser(competency, userId); } 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 88cad15f1000..a4123f63ec52 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 @@ -34,6 +34,7 @@ import de.tum.cit.aet.artemis.atlas.repository.CompetencyRelationRepository; import de.tum.cit.aet.artemis.atlas.repository.CourseCompetencyRepository; import de.tum.cit.aet.artemis.atlas.repository.StandardizedCompetencyRepository; +import de.tum.cit.aet.artemis.atlas.repository.simple.CourseCompetencySimpleService; import de.tum.cit.aet.artemis.atlas.service.LearningObjectImportService; import de.tum.cit.aet.artemis.atlas.service.learningpath.LearningPathService; import de.tum.cit.aet.artemis.core.domain.Course; @@ -64,6 +65,8 @@ public class CourseCompetencyService { protected final CourseCompetencyRepository courseCompetencyRepository; + private final CourseCompetencySimpleService courseCompetencySimpleService; + protected final CompetencyRelationRepository competencyRelationRepository; protected final CompetencyProgressService competencyProgressService; @@ -85,12 +88,13 @@ public class CourseCompetencyService { private final CourseRepository courseRepository; public CourseCompetencyService(CompetencyProgressRepository competencyProgressRepository, CourseCompetencyRepository courseCompetencyRepository, - CompetencyRelationRepository competencyRelationRepository, CompetencyProgressService competencyProgressService, ExerciseService exerciseService, - LectureUnitService lectureUnitService, LearningPathService learningPathService, AuthorizationCheckService authCheckService, - StandardizedCompetencyRepository standardizedCompetencyRepository, LectureUnitCompletionRepository lectureUnitCompletionRepository, - LearningObjectImportService learningObjectImportService, CourseRepository courseRepository) { + CourseCompetencySimpleService courseCompetencySimpleService, CompetencyRelationRepository competencyRelationRepository, + CompetencyProgressService competencyProgressService, ExerciseService exerciseService, LectureUnitService lectureUnitService, LearningPathService learningPathService, + AuthorizationCheckService authCheckService, StandardizedCompetencyRepository standardizedCompetencyRepository, + LectureUnitCompletionRepository lectureUnitCompletionRepository, LearningObjectImportService learningObjectImportService, CourseRepository courseRepository) { this.competencyProgressRepository = competencyProgressRepository; this.courseCompetencyRepository = courseCompetencyRepository; + this.courseCompetencySimpleService = courseCompetencySimpleService; this.competencyRelationRepository = competencyRelationRepository; this.competencyProgressService = competencyProgressService; this.exerciseService = exerciseService; @@ -113,7 +117,7 @@ public CourseCompetencyService(CompetencyProgressRepository competencyProgressRe * @return The found competency */ public CourseCompetency findCompetencyWithExercisesAndLectureUnitsAndProgressForUser(Long competencyId, Long userId) { - CourseCompetency competency = courseCompetencyRepository.findByIdWithLectureUnitsAndExercisesElseThrow(competencyId); + CourseCompetency competency = courseCompetencySimpleService.findByIdWithLectureUnitsAndExercisesElseThrow(competencyId); return findProgressAndLectureUnitCompletionsForUser(competency, userId); } 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 96a68280f334..0d18f373f619 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 @@ -19,6 +19,7 @@ import de.tum.cit.aet.artemis.atlas.repository.CourseCompetencyRepository; import de.tum.cit.aet.artemis.atlas.repository.PrerequisiteRepository; import de.tum.cit.aet.artemis.atlas.repository.StandardizedCompetencyRepository; +import de.tum.cit.aet.artemis.atlas.repository.simple.CourseCompetencySimpleService; import de.tum.cit.aet.artemis.atlas.service.LearningObjectImportService; import de.tum.cit.aet.artemis.atlas.service.learningpath.LearningPathService; import de.tum.cit.aet.artemis.core.domain.Course; @@ -38,12 +39,13 @@ public class PrerequisiteService extends CourseCompetencyService { private final PrerequisiteRepository prerequisiteRepository; public PrerequisiteService(PrerequisiteRepository prerequisiteRepository, AuthorizationCheckService authCheckService, CompetencyRelationRepository competencyRelationRepository, - LearningPathService learningPathService, CompetencyProgressService competencyProgressService, LectureUnitService lectureUnitService, - CompetencyProgressRepository competencyProgressRepository, LectureUnitCompletionRepository lectureUnitCompletionRepository, + CourseCompetencySimpleService courseCompetencySimpleService, LearningPathService learningPathService, CompetencyProgressService competencyProgressService, + LectureUnitService lectureUnitService, CompetencyProgressRepository competencyProgressRepository, LectureUnitCompletionRepository lectureUnitCompletionRepository, StandardizedCompetencyRepository standardizedCompetencyRepository, CourseCompetencyRepository courseCompetencyRepository, ExerciseService exerciseService, LearningObjectImportService learningObjectImportService, CompetencyLectureUnitLinkRepository competencyLectureUnitLinkRepository, CourseRepository courseRepository) { - super(competencyProgressRepository, courseCompetencyRepository, competencyRelationRepository, competencyProgressService, exerciseService, lectureUnitService, - learningPathService, authCheckService, standardizedCompetencyRepository, lectureUnitCompletionRepository, learningObjectImportService, courseRepository); + super(competencyProgressRepository, courseCompetencyRepository, courseCompetencySimpleService, competencyRelationRepository, competencyProgressService, exerciseService, + lectureUnitService, learningPathService, authCheckService, standardizedCompetencyRepository, lectureUnitCompletionRepository, learningObjectImportService, + courseRepository); this.prerequisiteRepository = prerequisiteRepository; } 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 1f3981baa4ce..a5c2161be9b1 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 @@ -33,7 +33,7 @@ import de.tum.cit.aet.artemis.atlas.domain.competency.RelationType; 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; +import de.tum.cit.aet.artemis.atlas.repository.simple.CourseCompetencySimpleService; 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; @@ -57,7 +57,7 @@ public class LearningPathRecommendationService { private final CompetencyProgressRepository competencyProgressRepository; - private final CourseCompetencyRepository courseCompetencyRepository; + private final CourseCompetencySimpleService courseCompetencySimpleService; /** * Base utility that is used to calculate a competencies' utility with respect to the earliest due date of the competency. @@ -99,12 +99,13 @@ public class LearningPathRecommendationService { { 0.50, 0.40, 0.10 }, { 0.39, 0.45, 0.16 }, { 0.28, 0.48, 0.24 }, { 0.20, 0.47, 0.33 }, { 0.13, 0.43, 0.44 }, { 0.08, 0.37, 0.55 }, { 0.04, 0.29, 0.67 }, }; protected LearningPathRecommendationService(CompetencyRelationRepository competencyRelationRepository, LearningObjectService learningObjectService, - ParticipantScoreService participantScoreService, CompetencyProgressRepository competencyProgressRepository, CourseCompetencyRepository courseCompetencyRepository) { + ParticipantScoreService participantScoreService, CompetencyProgressRepository competencyProgressRepository, + CourseCompetencySimpleService courseCompetencySimpleService) { this.competencyRelationRepository = competencyRelationRepository; this.learningObjectService = learningObjectService; this.participantScoreService = participantScoreService; this.competencyProgressRepository = competencyProgressRepository; - this.courseCompetencyRepository = courseCompetencyRepository; + this.courseCompetencySimpleService = courseCompetencySimpleService; } /** @@ -695,7 +696,7 @@ public record RecommendationState(Map competencyIdMap, L * @return the recommended order of learning objects */ public List getOrderOfLearningObjectsForCompetency(long competencyId, User user) { - CourseCompetency competency = courseCompetencyRepository.findByIdWithExercisesAndLectureUnitsElseThrow(competencyId); + CourseCompetency competency = courseCompetencySimpleService.findByIdWithExercisesAndLectureUnitsElseThrow(competencyId); return getOrderOfLearningObjectsForCompetency(competency, user); } 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 1bc5b2202aeb..89405a7e739c 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 @@ -30,6 +30,8 @@ import de.tum.cit.aet.artemis.atlas.dto.CompetencyWithTailRelationDTO; import de.tum.cit.aet.artemis.atlas.repository.CompetencyRepository; import de.tum.cit.aet.artemis.atlas.repository.CourseCompetencyRepository; +import de.tum.cit.aet.artemis.atlas.repository.simple.CompetencySimpleService; +import de.tum.cit.aet.artemis.atlas.repository.simple.CourseCompetencySimpleService; import de.tum.cit.aet.artemis.atlas.service.competency.CompetencyService; import de.tum.cit.aet.artemis.atlas.service.competency.CourseCompetencyService; import de.tum.cit.aet.artemis.core.domain.Course; @@ -64,21 +66,27 @@ public class CompetencyResource { private final CompetencyRepository competencyRepository; + private final CompetencySimpleService competencySimpleService; + private final CompetencyService competencyService; private final CourseCompetencyRepository courseCompetencyRepository; + private final CourseCompetencySimpleService courseCompetencySimpleService; + private final CourseCompetencyService courseCompetencyService; public CompetencyResource(CourseRepository courseRepository, AuthorizationCheckService authorizationCheckService, UserRepository userRepository, - CompetencyRepository competencyRepository, CompetencyService competencyService, CourseCompetencyRepository courseCompetencyRepository, - CourseCompetencyService courseCompetencyService) { + CompetencyRepository competencyRepository, CompetencySimpleService competencySimpleService, CompetencyService competencyService, + CourseCompetencyRepository courseCompetencyRepository, CourseCompetencySimpleService courseCompetencySimpleService, CourseCompetencyService courseCompetencyService) { this.courseRepository = courseRepository; this.authorizationCheckService = authorizationCheckService; this.userRepository = userRepository; this.competencyRepository = competencyRepository; + this.competencySimpleService = competencySimpleService; this.competencyService = competencyService; this.courseCompetencyRepository = courseCompetencyRepository; + this.courseCompetencySimpleService = courseCompetencySimpleService; this.courseCompetencyService = courseCompetencyService; } @@ -181,7 +189,7 @@ public ResponseEntity importCompetency(@PathVariable long courseId, long competencyId = importOptions.competencyIds().iterator().next(); var course = courseRepository.findWithEagerCompetenciesAndPrerequisitesByIdElseThrow(courseId); - var competencyToImport = courseCompetencyRepository.findByIdWithExercisesAndLectureUnitsAndLecturesElseThrow(competencyId); + var competencyToImport = courseCompetencySimpleService.findByIdWithExercisesAndLectureUnitsAndLecturesElseThrow(competencyId); authorizationCheckService.checkHasAtLeastRoleInCourseElseThrow(Role.EDITOR, competencyToImport.getCourse(), null); if (competencyToImport.getCourse().getId().equals(courseId)) { @@ -293,7 +301,7 @@ public ResponseEntity updateCompetency(@PathVariable long courseId, checkCompetencyAttributesForUpdate(competency); var course = courseRepository.findByIdElseThrow(courseId); - var existingCompetency = competencyRepository.findByIdWithLectureUnitsElseThrow(competency.getId()); + var existingCompetency = competencySimpleService.findByIdWithLectureUnitsElseThrow(competency.getId()); checkCourseForCompetency(course, existingCompetency); var persistedCompetency = competencyService.updateCourseCompetency(existingCompetency, competency); @@ -314,7 +322,7 @@ public ResponseEntity deleteCompetency(@PathVariable long competencyId, @P log.info("REST request to delete a Competency : {}", competencyId); var course = courseRepository.findByIdElseThrow(courseId); - var competency = courseCompetencyRepository.findByIdWithExercisesAndLectureUnitsBidirectionalElseThrow(competencyId); + var competency = courseCompetencySimpleService.findByIdWithExercisesAndLectureUnitsBidirectionalElseThrow(competencyId); checkCourseForCompetency(course, competency); courseCompetencyService.deleteCourseCompetency(competency, course); diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/web/CourseCompetencyResource.java b/src/main/java/de/tum/cit/aet/artemis/atlas/web/CourseCompetencyResource.java index 8e93c6c73090..39225fa1011f 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/web/CourseCompetencyResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/web/CourseCompetencyResource.java @@ -38,6 +38,7 @@ 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; +import de.tum.cit.aet.artemis.atlas.repository.simple.CourseCompetencySimpleService; import de.tum.cit.aet.artemis.atlas.service.competency.CompetencyJolService; import de.tum.cit.aet.artemis.atlas.service.competency.CompetencyProgressService; import de.tum.cit.aet.artemis.atlas.service.competency.CompetencyRelationService; @@ -91,13 +92,15 @@ public class CourseCompetencyResource { private final CourseCompetencyRepository courseCompetencyRepository; + private final CourseCompetencySimpleService courseCompetencySimpleService; + private final AuthorizationCheckService authorizationCheckService; public CourseCompetencyResource(UserRepository userRepository, CourseCompetencyService courseCompetencyService, CourseCompetencyRepository courseCompetencyRepository, CourseRepository courseRepository, CompetencyProgressService competencyProgressService, CompetencyProgressRepository competencyProgressRepository, CompetencyRelationRepository competencyRelationRepository, CompetencyRelationService competencyRelationService, Optional irisCompetencyGenerationService, CompetencyJolService competencyJolService, - AuthorizationCheckService authorizationCheckService) { + CourseCompetencySimpleService courseCompetencySimpleService, AuthorizationCheckService authorizationCheckService) { this.userRepository = userRepository; this.courseCompetencyService = courseCompetencyService; this.courseCompetencyRepository = courseCompetencyRepository; @@ -108,6 +111,7 @@ public CourseCompetencyResource(UserRepository userRepository, CourseCompetencyS this.competencyRelationService = competencyRelationService; this.irisCompetencyGenerationService = irisCompetencyGenerationService; this.competencyJolService = competencyJolService; + this.courseCompetencySimpleService = courseCompetencySimpleService; this.authorizationCheckService = authorizationCheckService; } @@ -215,7 +219,7 @@ public ResponseEntity getCompetencyStudentProgress(@PathVari public ResponseEntity getCompetencyCourseProgress(@PathVariable long courseId, @PathVariable long competencyId) { log.debug("REST request to get course progress for competency: {}", competencyId); var course = courseRepository.findByIdElseThrow(courseId); - var competency = courseCompetencyRepository.findByIdWithExercisesElseThrow(competencyId); + var competency = courseCompetencySimpleService.findByIdWithExercisesElseThrow(competencyId); var progress = competencyProgressService.getCompetencyCourseProgress(competency, course); 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 4f75c618282d..14571bd7562f 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 @@ -30,6 +30,7 @@ import de.tum.cit.aet.artemis.atlas.dto.CompetencyWithTailRelationDTO; import de.tum.cit.aet.artemis.atlas.repository.CourseCompetencyRepository; import de.tum.cit.aet.artemis.atlas.repository.PrerequisiteRepository; +import de.tum.cit.aet.artemis.atlas.repository.simple.CourseCompetencySimpleService; import de.tum.cit.aet.artemis.atlas.service.competency.CourseCompetencyService; import de.tum.cit.aet.artemis.atlas.service.competency.PrerequisiteService; import de.tum.cit.aet.artemis.core.domain.Course; @@ -72,17 +73,20 @@ public class PrerequisiteResource { private final CourseCompetencyRepository courseCompetencyRepository; + private final CourseCompetencySimpleService courseCompetencySimpleService; + private final CourseCompetencyService courseCompetencyService; public PrerequisiteResource(CourseRepository courseRepository, AuthorizationCheckService authorizationCheckService, UserRepository userRepository, PrerequisiteRepository prerequisiteRepository, PrerequisiteService prerequisiteService, CourseCompetencyRepository courseCompetencyRepository, - CourseCompetencyService courseCompetencyService) { + CourseCompetencySimpleService courseCompetencySimpleService, CourseCompetencyService courseCompetencyService) { this.courseRepository = courseRepository; this.authorizationCheckService = authorizationCheckService; this.userRepository = userRepository; this.prerequisiteRepository = prerequisiteRepository; this.prerequisiteService = prerequisiteService; this.courseCompetencyRepository = courseCompetencyRepository; + this.courseCompetencySimpleService = courseCompetencySimpleService; this.courseCompetencyService = courseCompetencyService; } @@ -185,7 +189,7 @@ public ResponseEntity importPrerequisite(@PathVariable long course long prerequisiteId = importOptions.competencyIds().iterator().next(); var course = courseRepository.findWithEagerCompetenciesAndPrerequisitesByIdElseThrow(courseId); - var prerequisiteToImport = courseCompetencyRepository.findByIdWithExercisesAndLectureUnitsAndLecturesElseThrow(prerequisiteId); + var prerequisiteToImport = courseCompetencySimpleService.findByIdWithExercisesAndLectureUnitsAndLecturesElseThrow(prerequisiteId); authorizationCheckService.checkHasAtLeastRoleInCourseElseThrow(Role.EDITOR, prerequisiteToImport.getCourse(), null); if (prerequisiteToImport.getCourse().getId().equals(courseId)) { @@ -318,7 +322,7 @@ public ResponseEntity deletePrerequisite(@PathVariable long prerequisiteId log.info("REST request to delete a Prerequisite : {}", prerequisiteId); var course = courseRepository.findByIdElseThrow(courseId); - var prerequisite = courseCompetencyRepository.findByIdWithExercisesAndLectureUnitsBidirectionalElseThrow(prerequisiteId); + var prerequisite = courseCompetencySimpleService.findByIdWithExercisesAndLectureUnitsBidirectionalElseThrow(prerequisiteId); checkCourseForPrerequisite(course, prerequisite); courseCompetencyService.deleteCourseCompetency(prerequisite, course); diff --git a/src/main/java/de/tum/cit/aet/artemis/core/repository/base/ISimpleService.java b/src/main/java/de/tum/cit/aet/artemis/core/repository/base/ISimpleService.java new file mode 100644 index 000000000000..b004fe55370d --- /dev/null +++ b/src/main/java/de/tum/cit/aet/artemis/core/repository/base/ISimpleService.java @@ -0,0 +1,23 @@ +package de.tum.cit.aet.artemis.core.repository.base; + +import java.util.Optional; + +import de.tum.cit.aet.artemis.core.domain.DomainObject; +import de.tum.cit.aet.artemis.core.exception.EntityNotFoundException; + +/** + * Interface for simple (repository) services. These are services on repository + * layer which contain the default methods of the repository layer/data access. + */ +public interface ISimpleService { + + String getEntityName(); + + default U getValueElseThrow(Optional optional) { + return getValueElseThrow(optional, getEntityName()); + } + + default U getValueElseThrow(Optional optional, String entityName) { + return optional.orElseThrow(() -> new EntityNotFoundException(entityName)); + } +} diff --git a/src/test/java/de/tum/cit/aet/artemis/shared/architecture/module/AbstractModuleRepositoryArchitectureTest.java b/src/test/java/de/tum/cit/aet/artemis/shared/architecture/module/AbstractModuleRepositoryArchitectureTest.java index fdc272e7877d..17c09ad2b456 100644 --- a/src/test/java/de/tum/cit/aet/artemis/shared/architecture/module/AbstractModuleRepositoryArchitectureTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/shared/architecture/module/AbstractModuleRepositoryArchitectureTest.java @@ -8,12 +8,14 @@ import static com.tngtech.archunit.lang.SimpleConditionEvent.violated; import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes; +import java.util.List; import java.util.Optional; import java.util.Set; import java.util.function.Supplier; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.context.annotation.Primary; import org.springframework.data.domain.Pageable; @@ -27,8 +29,11 @@ import com.tngtech.archunit.base.DescribedPredicate; import com.tngtech.archunit.core.domain.JavaClass; +import com.tngtech.archunit.core.domain.JavaConstructor; import com.tngtech.archunit.core.domain.JavaField; import com.tngtech.archunit.core.domain.JavaMethod; +import com.tngtech.archunit.core.domain.JavaModifier; +import com.tngtech.archunit.core.domain.JavaParameter; import com.tngtech.archunit.core.domain.JavaType; import com.tngtech.archunit.lang.ArchCondition; import com.tngtech.archunit.lang.ArchRule; @@ -302,4 +307,53 @@ private String replaceLast(String string, String substring, String replacement) sb.replace(lastIndex, lastIndex + substring.length(), replacement); return sb.toString(); } + + /** + * Disabled for now. + * Enforce that no default methods are declared in the JPARepository interfaces. + * Instead, one should use/create a SimpleService in the 'simple'-subpackage. + */ + @Disabled + @Test + void enforceNoDefaultMethodsInRepository() { + methodsOfThisModuleThat().areDeclaredInClassesThat().areAnnotatedWith(Repository.class).should(new ArchCondition<>("not have default methods") { + + @Override + public void check(JavaMethod javaMethod, ConditionEvents events) { + if (!javaMethod.getModifiers().contains(JavaModifier.ABSTRACT)) { + String message = String.format("Method %s has a default modifier", javaMethod.getFullName()); + events.add(SimpleConditionEvent.violated(javaMethod, message)); + } + } + }).because("Default methods should be declared in SimpleServices, not in JPA Repository").allowEmptyShould(true).check(productionClasses); + } + + @Test + void enforceSimpleServiceSingleAutowire() { + classesOfThisModuleThat().resideInAPackage("..repository.simple..").and().areAnnotatedWith(Service.class) + .should(new ArchCondition<>("have a constructor with exactly one parameter of a @Repository type") { + + @Override + public void check(JavaClass javaClass, ConditionEvents events) { + List constructors = javaClass.getConstructors().stream().filter(constructor -> constructor.getParameters().size() == 1).toList(); + + boolean hasValidConstructor = false; + + for (JavaConstructor constructor : constructors) { + JavaParameter parameter = constructor.getParameters().getFirst(); + JavaClass parameterType = parameter.getType().toErasure(); + + if (parameterType.isAnnotatedWith(Repository.class)) { + hasValidConstructor = true; + break; + } + } + + if (!hasValidConstructor) { + String message = String.format("Class %s does not have a constructor with exactly one parameter of a @Repository type.", javaClass.getName()); + events.add(SimpleConditionEvent.violated(javaClass, message)); + } + } + }).because("Simple Services should have exactly one autowired candidate to remain simple").allowEmptyShould(true).check(productionClasses); + } } diff --git a/src/test/java/de/tum/cit/aet/artemis/shared/architecture/module/AbstractModuleServiceArchitectureTest.java b/src/test/java/de/tum/cit/aet/artemis/shared/architecture/module/AbstractModuleServiceArchitectureTest.java index cd43dd981e51..1d5658b55463 100644 --- a/src/test/java/de/tum/cit/aet/artemis/shared/architecture/module/AbstractModuleServiceArchitectureTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/shared/architecture/module/AbstractModuleServiceArchitectureTest.java @@ -37,7 +37,8 @@ void shouldBeNamedService() { @Test void shouldBeInServicePackage() { - ArchRule rule = classesOfThisModuleThat().areAnnotatedWith(Service.class).should().resideInAPackage("..service..").because("services should be in the package 'service'."); + ArchRule rule = classesOfThisModuleThat().areAnnotatedWith(Service.class).should().resideInAPackage("..service..").orShould().resideInAPackage("..repository.simple..") + .because("services should be in the package 'service'."); final var exceptions = new Class[] { MigrationService.class, SecurityMetersService.class, DomainUserDetailsService.class, OAuth2JWKSService.class, JWTCookieService.class, GitDiffReportParserService.class, ResultWebsocketService.class, LocalCIWebsocketMessagingService.class }; final var classes = classesExcept(productionClasses, exceptions);