diff --git a/.idea/runConfigurations/Artemis__Server__LocalVC___Jenkins_.xml b/.idea/runConfigurations/Artemis__Server__LocalVC___Jenkins_.xml
index 217477ace79d..2eae7040817e 100644
--- a/.idea/runConfigurations/Artemis__Server__LocalVC___Jenkins_.xml
+++ b/.idea/runConfigurations/Artemis__Server__LocalVC___Jenkins_.xml
@@ -9,4 +9,4 @@
-
\ No newline at end of file
+
diff --git a/.idea/runConfigurations/Artemis__Server__LocalVC___LocalCI_.xml b/.idea/runConfigurations/Artemis__Server__LocalVC___LocalCI_.xml
index 2323d3d5ca3b..9135ec4a2fd2 100644
--- a/.idea/runConfigurations/Artemis__Server__LocalVC___LocalCI_.xml
+++ b/.idea/runConfigurations/Artemis__Server__LocalVC___LocalCI_.xml
@@ -1,7 +1,7 @@
-
-
+
+
diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/service/AssessmentService.java b/src/main/java/de/tum/cit/aet/artemis/assessment/service/AssessmentService.java
index 6b7b45c8c65b..35596c4a4c15 100644
--- a/src/main/java/de/tum/cit/aet/artemis/assessment/service/AssessmentService.java
+++ b/src/main/java/de/tum/cit/aet/artemis/assessment/service/AssessmentService.java
@@ -17,7 +17,6 @@
import de.tum.cit.aet.artemis.assessment.dto.AssessmentUpdateBaseDTO;
import de.tum.cit.aet.artemis.assessment.repository.ComplaintRepository;
import de.tum.cit.aet.artemis.assessment.repository.FeedbackRepository;
-import de.tum.cit.aet.artemis.assessment.repository.LongFeedbackTextRepository;
import de.tum.cit.aet.artemis.assessment.repository.ResultRepository;
import de.tum.cit.aet.artemis.assessment.web.ResultWebsocketService;
import de.tum.cit.aet.artemis.communication.service.notifications.SingleUserNotificationService;
@@ -68,12 +67,10 @@ public class AssessmentService {
protected final ResultWebsocketService resultWebsocketService;
- private final LongFeedbackTextRepository longFeedbackTextRepository;
-
public AssessmentService(ComplaintResponseService complaintResponseService, ComplaintRepository complaintRepository, FeedbackRepository feedbackRepository,
ResultRepository resultRepository, StudentParticipationRepository studentParticipationRepository, ResultService resultService, SubmissionService submissionService,
SubmissionRepository submissionRepository, ExamDateService examDateService, UserRepository userRepository, Optional ltiNewResultService,
- SingleUserNotificationService singleUserNotificationService, ResultWebsocketService resultWebsocketService, LongFeedbackTextRepository longFeedbackTextRepository) {
+ SingleUserNotificationService singleUserNotificationService, ResultWebsocketService resultWebsocketService) {
this.complaintResponseService = complaintResponseService;
this.complaintRepository = complaintRepository;
this.feedbackRepository = feedbackRepository;
@@ -87,7 +84,6 @@ public AssessmentService(ComplaintResponseService complaintResponseService, Comp
this.ltiNewResultService = ltiNewResultService;
this.singleUserNotificationService = singleUserNotificationService;
this.resultWebsocketService = resultWebsocketService;
- this.longFeedbackTextRepository = longFeedbackTextRepository;
}
/**
diff --git a/src/main/java/de/tum/cit/aet/artemis/assessment/service/ParticipantScoreScheduleService.java b/src/main/java/de/tum/cit/aet/artemis/assessment/service/ParticipantScoreScheduleService.java
index fffd4957b5b2..a3b7521668f4 100644
--- a/src/main/java/de/tum/cit/aet/artemis/assessment/service/ParticipantScoreScheduleService.java
+++ b/src/main/java/de/tum/cit/aet/artemis/assessment/service/ParticipantScoreScheduleService.java
@@ -37,7 +37,7 @@
import de.tum.cit.aet.artemis.assessment.repository.ResultRepository;
import de.tum.cit.aet.artemis.assessment.repository.StudentScoreRepository;
import de.tum.cit.aet.artemis.assessment.repository.TeamScoreRepository;
-import de.tum.cit.aet.artemis.atlas.service.competency.CompetencyProgressService;
+import de.tum.cit.aet.artemis.atlas.api.CompetencyProgressApi;
import de.tum.cit.aet.artemis.core.domain.User;
import de.tum.cit.aet.artemis.core.repository.UserRepository;
import de.tum.cit.aet.artemis.core.security.SecurityUtils;
@@ -74,7 +74,7 @@ public class ParticipantScoreScheduleService {
private Optional lastScheduledRun = Optional.empty();
- private final CompetencyProgressService competencyProgressService;
+ private final CompetencyProgressApi competencyProgressApi;
private final ParticipantScoreRepository participantScoreRepository;
@@ -96,11 +96,11 @@ public class ParticipantScoreScheduleService {
*/
private final AtomicBoolean isRunning = new AtomicBoolean(false);
- public ParticipantScoreScheduleService(@Qualifier("taskScheduler") TaskScheduler scheduler, CompetencyProgressService competencyProgressService,
+ public ParticipantScoreScheduleService(@Qualifier("taskScheduler") TaskScheduler scheduler, CompetencyProgressApi competencyProgressApi,
ParticipantScoreRepository participantScoreRepository, StudentScoreRepository studentScoreRepository, TeamScoreRepository teamScoreRepository,
ExerciseRepository exerciseRepository, ResultRepository resultRepository, UserRepository userRepository, TeamRepository teamRepository) {
this.scheduler = scheduler;
- this.competencyProgressService = competencyProgressService;
+ this.competencyProgressApi = competencyProgressApi;
this.participantScoreRepository = participantScoreRepository;
this.studentScoreRepository = studentScoreRepository;
this.teamScoreRepository = teamScoreRepository;
@@ -336,7 +336,7 @@ private void executeTask(Long exerciseId, Long participantId, Instant resultLast
if (scoreParticipant instanceof Team team && !Hibernate.isInitialized(team.getStudents())) {
scoreParticipant = teamRepository.findWithStudentsByIdElseThrow(team.getId());
}
- competencyProgressService.updateProgressByLearningObjectSync(score.getExercise(), scoreParticipant.getParticipants());
+ competencyProgressApi.updateProgressByLearningObjectSync(score.getExercise(), scoreParticipant.getParticipants());
}
catch (Exception e) {
log.error("Exception while processing participant score for exercise {} and participant {} for participant scores:", exerciseId, participantId, e);
diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/api/AbstractAtlasApi.java b/src/main/java/de/tum/cit/aet/artemis/atlas/api/AbstractAtlasApi.java
new file mode 100644
index 000000000000..41481096ac68
--- /dev/null
+++ b/src/main/java/de/tum/cit/aet/artemis/atlas/api/AbstractAtlasApi.java
@@ -0,0 +1,6 @@
+package de.tum.cit.aet.artemis.atlas.api;
+
+import de.tum.cit.aet.artemis.core.api.AbstractApi;
+
+public abstract class AbstractAtlasApi implements AbstractApi {
+}
diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/api/CompetencyApi.java b/src/main/java/de/tum/cit/aet/artemis/atlas/api/CompetencyApi.java
new file mode 100644
index 000000000000..7c4f349d9215
--- /dev/null
+++ b/src/main/java/de/tum/cit/aet/artemis/atlas/api/CompetencyApi.java
@@ -0,0 +1,24 @@
+package de.tum.cit.aet.artemis.atlas.api;
+
+import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE;
+
+import org.springframework.context.annotation.Profile;
+import org.springframework.stereotype.Controller;
+
+import de.tum.cit.aet.artemis.atlas.service.competency.CompetencyService;
+import de.tum.cit.aet.artemis.lecture.domain.Lecture;
+
+@Controller
+@Profile(PROFILE_CORE)
+public class CompetencyApi extends AbstractAtlasApi {
+
+ private final CompetencyService competencyService;
+
+ public CompetencyApi(CompetencyService competencyService) {
+ this.competencyService = competencyService;
+ }
+
+ public void addCompetencyLinksToExerciseUnits(Lecture lecture) {
+ competencyService.addCompetencyLinksToExerciseUnits(lecture);
+ }
+}
diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/api/CompetencyProgressApi.java b/src/main/java/de/tum/cit/aet/artemis/atlas/api/CompetencyProgressApi.java
new file mode 100644
index 000000000000..eddc384ad8c7
--- /dev/null
+++ b/src/main/java/de/tum/cit/aet/artemis/atlas/api/CompetencyProgressApi.java
@@ -0,0 +1,74 @@
+package de.tum.cit.aet.artemis.atlas.api;
+
+import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+
+import org.springframework.context.annotation.Profile;
+import org.springframework.stereotype.Controller;
+
+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.CourseCompetency;
+import de.tum.cit.aet.artemis.atlas.repository.CompetencyRepository;
+import de.tum.cit.aet.artemis.atlas.service.competency.CompetencyProgressService;
+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.Participant;
+
+@Controller
+@Profile(PROFILE_CORE)
+public class CompetencyProgressApi extends AbstractAtlasApi {
+
+ private final CompetencyProgressService competencyProgressService;
+
+ private final CompetencyRepository competencyRepository;
+
+ public CompetencyProgressApi(CompetencyProgressService competencyProgressService, CompetencyRepository competencyRepository) {
+ this.competencyProgressService = competencyProgressService;
+ this.competencyRepository = competencyRepository;
+ }
+
+ public void updateProgressByLearningObjectForParticipantAsync(LearningObject learningObject, Participant participant) {
+ competencyProgressService.updateProgressByLearningObjectForParticipantAsync(learningObject, participant);
+ }
+
+ public void updateProgressByLearningObjectAsync(LearningObject learningObject) {
+ competencyProgressService.updateProgressByLearningObjectAsync(learningObject);
+ }
+
+ public void updateProgressByCompetencyAsync(CourseCompetency competency) {
+ competencyProgressService.updateProgressByCompetencyAsync(competency);
+ }
+
+ public void updateProgressForUpdatedLearningObjectAsync(LearningObject originalLearningObject, Optional updatedLearningObject) {
+ competencyProgressService.updateProgressForUpdatedLearningObjectAsync(originalLearningObject, updatedLearningObject);
+ }
+
+ public void updateProgressByLearningObjectSync(LearningObject learningObject, Set users) {
+ competencyProgressService.updateProgressByLearningObjectSync(learningObject, users);
+ }
+
+ public long countByCourse(Course course) {
+ return competencyRepository.countByCourse(course);
+ }
+
+ public void deleteAll(Set competencies) {
+ competencyRepository.deleteAll(competencies);
+ }
+
+ /**
+ * Updates the progress for all competencies of the given courses.
+ *
+ * @param activeCourses the active courses
+ */
+ public void updateProgressForCoursesAsync(List activeCourses) {
+ activeCourses.forEach(course -> {
+ List competencies = competencyRepository.findByCourseIdOrderById(course.getId());
+ // Asynchronously update the progress for each competency
+ competencies.forEach(competencyProgressService::updateProgressByCompetencyAsync);
+ });
+ }
+}
diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/api/CompetencyRelationApi.java b/src/main/java/de/tum/cit/aet/artemis/atlas/api/CompetencyRelationApi.java
new file mode 100644
index 000000000000..d7857bdcf509
--- /dev/null
+++ b/src/main/java/de/tum/cit/aet/artemis/atlas/api/CompetencyRelationApi.java
@@ -0,0 +1,56 @@
+package de.tum.cit.aet.artemis.atlas.api;
+
+import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE;
+
+import java.util.List;
+
+import org.springframework.context.annotation.Profile;
+import org.springframework.stereotype.Controller;
+
+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.repository.CompetencyExerciseLinkRepository;
+import de.tum.cit.aet.artemis.atlas.repository.CompetencyLectureUnitLinkRepository;
+import de.tum.cit.aet.artemis.atlas.repository.CompetencyRelationRepository;
+
+@Controller
+@Profile(PROFILE_CORE)
+public class CompetencyRelationApi extends AbstractAtlasApi {
+
+ private final CompetencyRelationRepository competencyRelationRepository;
+
+ private final CompetencyExerciseLinkRepository competencyExerciseLinkRepository;
+
+ private final CompetencyLectureUnitLinkRepository lectureUnitLinkRepository;
+
+ public CompetencyRelationApi(CompetencyRelationRepository competencyRelationRepository, CompetencyExerciseLinkRepository competencyExerciseLinkRepository,
+ CompetencyLectureUnitLinkRepository lectureUnitLinkRepository) {
+ this.competencyRelationRepository = competencyRelationRepository;
+ this.competencyExerciseLinkRepository = competencyExerciseLinkRepository;
+ this.lectureUnitLinkRepository = lectureUnitLinkRepository;
+ }
+
+ public void deleteAllByCourseId(Long courseId) {
+ competencyRelationRepository.deleteAllByCourseId(courseId);
+ }
+
+ public void deleteAllExerciseLinks(Iterable competencyExerciseLinks) {
+ competencyExerciseLinkRepository.deleteAll(competencyExerciseLinks);
+ }
+
+ public List saveAllExerciseLinks(Iterable competencyExerciseLinks) {
+ return competencyExerciseLinkRepository.saveAll(competencyExerciseLinks);
+ }
+
+ public List saveAllLectureUnitLinks(Iterable lectureUnitLinks) {
+ return lectureUnitLinkRepository.saveAll(lectureUnitLinks);
+ }
+
+ public void deleteAllLectureUnitLinks(Iterable lectureUnitLinks) {
+ lectureUnitLinkRepository.deleteAll(lectureUnitLinks);
+ }
+
+ public void deleteAllLectureUnitLinksByLectureId(Long lectureId) {
+ lectureUnitLinkRepository.deleteAllByLectureId(lectureId);
+ }
+}
diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/api/CourseCompetencyApi.java b/src/main/java/de/tum/cit/aet/artemis/atlas/api/CourseCompetencyApi.java
new file mode 100644
index 000000000000..c970d2f137ff
--- /dev/null
+++ b/src/main/java/de/tum/cit/aet/artemis/atlas/api/CourseCompetencyApi.java
@@ -0,0 +1,24 @@
+package de.tum.cit.aet.artemis.atlas.api;
+
+import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE;
+
+import org.springframework.context.annotation.Profile;
+import org.springframework.stereotype.Controller;
+
+import de.tum.cit.aet.artemis.atlas.domain.competency.CourseCompetency;
+import de.tum.cit.aet.artemis.atlas.repository.CourseCompetencyRepository;
+
+@Controller
+@Profile(PROFILE_CORE)
+public class CourseCompetencyApi extends AbstractAtlasApi {
+
+ private final CourseCompetencyRepository courseCompetencyRepository;
+
+ public CourseCompetencyApi(CourseCompetencyRepository courseCompetencyRepository) {
+ this.courseCompetencyRepository = courseCompetencyRepository;
+ }
+
+ public void save(CourseCompetency courseCompetency) {
+ courseCompetencyRepository.save(courseCompetency);
+ }
+}
diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/api/LearningMetricsApi.java b/src/main/java/de/tum/cit/aet/artemis/atlas/api/LearningMetricsApi.java
new file mode 100644
index 000000000000..cf2a5271cab0
--- /dev/null
+++ b/src/main/java/de/tum/cit/aet/artemis/atlas/api/LearningMetricsApi.java
@@ -0,0 +1,24 @@
+package de.tum.cit.aet.artemis.atlas.api;
+
+import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE;
+
+import org.springframework.context.annotation.Profile;
+import org.springframework.stereotype.Controller;
+
+import de.tum.cit.aet.artemis.atlas.dto.metrics.StudentMetricsDTO;
+import de.tum.cit.aet.artemis.atlas.service.LearningMetricsService;
+
+@Controller
+@Profile(PROFILE_CORE)
+public class LearningMetricsApi extends AbstractAtlasApi {
+
+ private final LearningMetricsService metricsService;
+
+ public LearningMetricsApi(LearningMetricsService metricsService) {
+ this.metricsService = metricsService;
+ }
+
+ public StudentMetricsDTO getStudentCourseMetrics(long userId, long courseId) {
+ return metricsService.getStudentCourseMetrics(userId, courseId);
+ }
+}
diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/api/LearningPathApi.java b/src/main/java/de/tum/cit/aet/artemis/atlas/api/LearningPathApi.java
new file mode 100644
index 000000000000..834297c04288
--- /dev/null
+++ b/src/main/java/de/tum/cit/aet/artemis/atlas/api/LearningPathApi.java
@@ -0,0 +1,31 @@
+package de.tum.cit.aet.artemis.atlas.api;
+
+import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE;
+
+import jakarta.validation.constraints.NotNull;
+
+import org.springframework.context.annotation.Profile;
+import org.springframework.stereotype.Controller;
+
+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;
+
+@Controller
+@Profile(PROFILE_CORE)
+public class LearningPathApi extends AbstractAtlasApi {
+
+ private final LearningPathService learningPathService;
+
+ public LearningPathApi(LearningPathService learningPathService) {
+ this.learningPathService = learningPathService;
+ }
+
+ public void generateLearningPathForUser(@NotNull Course course, @NotNull User user) {
+ learningPathService.generateLearningPathForUser(course, user);
+ }
+
+ public void generateLearningPaths(@NotNull Course course) {
+ learningPathService.generateLearningPaths(course);
+ }
+}
diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/api/PrerequisitesApi.java b/src/main/java/de/tum/cit/aet/artemis/atlas/api/PrerequisitesApi.java
new file mode 100644
index 000000000000..ddcc8fa3c56a
--- /dev/null
+++ b/src/main/java/de/tum/cit/aet/artemis/atlas/api/PrerequisitesApi.java
@@ -0,0 +1,29 @@
+package de.tum.cit.aet.artemis.atlas.api;
+
+import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE;
+
+import org.springframework.context.annotation.Profile;
+import org.springframework.stereotype.Controller;
+
+import de.tum.cit.aet.artemis.atlas.domain.competency.Prerequisite;
+import de.tum.cit.aet.artemis.atlas.repository.PrerequisiteRepository;
+import de.tum.cit.aet.artemis.core.domain.Course;
+
+@Controller
+@Profile(PROFILE_CORE)
+public class PrerequisitesApi extends AbstractAtlasApi {
+
+ private final PrerequisiteRepository prerequisiteRepository;
+
+ public PrerequisitesApi(PrerequisiteRepository prerequisiteRepository) {
+ this.prerequisiteRepository = prerequisiteRepository;
+ }
+
+ public long countByCourse(Course course) {
+ return prerequisiteRepository.countByCourse(course);
+ }
+
+ public void deleteAll(Iterable prerequisites) {
+ prerequisiteRepository.deleteAll(prerequisites);
+ }
+}
diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/api/ScienceEventApi.java b/src/main/java/de/tum/cit/aet/artemis/atlas/api/ScienceEventApi.java
new file mode 100644
index 000000000000..0da5e4beb0ca
--- /dev/null
+++ b/src/main/java/de/tum/cit/aet/artemis/atlas/api/ScienceEventApi.java
@@ -0,0 +1,30 @@
+package de.tum.cit.aet.artemis.atlas.api;
+
+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.Controller;
+
+import de.tum.cit.aet.artemis.atlas.domain.science.ScienceEvent;
+import de.tum.cit.aet.artemis.atlas.repository.ScienceEventRepository;
+
+@Controller
+@Profile(PROFILE_CORE)
+public class ScienceEventApi extends AbstractAtlasApi {
+
+ private final ScienceEventRepository scienceEventRepository;
+
+ public ScienceEventApi(ScienceEventRepository scienceEventRepository) {
+ this.scienceEventRepository = scienceEventRepository;
+ }
+
+ public Set findAllByIdentity(String login) {
+ return scienceEventRepository.findAllByIdentity(login);
+ }
+
+ public void renameIdentity(String oldIdentity, String newIdentity) {
+ scienceEventRepository.renameIdentity(oldIdentity, newIdentity);
+ }
+}
diff --git a/src/main/java/de/tum/cit/aet/artemis/exercise/service/LearningMetricsService.java b/src/main/java/de/tum/cit/aet/artemis/atlas/service/LearningMetricsService.java
similarity index 99%
rename from src/main/java/de/tum/cit/aet/artemis/exercise/service/LearningMetricsService.java
rename to src/main/java/de/tum/cit/aet/artemis/atlas/service/LearningMetricsService.java
index c939f59367b5..38b4552c19c3 100644
--- a/src/main/java/de/tum/cit/aet/artemis/exercise/service/LearningMetricsService.java
+++ b/src/main/java/de/tum/cit/aet/artemis/atlas/service/LearningMetricsService.java
@@ -1,4 +1,4 @@
-package de.tum.cit.aet.artemis.exercise.service;
+package de.tum.cit.aet.artemis.atlas.service;
import static de.tum.cit.aet.artemis.core.config.Constants.MIN_SCORE_GREEN;
import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE;
diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/web/MetricsResource.java b/src/main/java/de/tum/cit/aet/artemis/atlas/web/MetricsResource.java
index ca2fa2f30251..db24d9569c88 100644
--- a/src/main/java/de/tum/cit/aet/artemis/atlas/web/MetricsResource.java
+++ b/src/main/java/de/tum/cit/aet/artemis/atlas/web/MetricsResource.java
@@ -12,9 +12,9 @@
import org.springframework.web.bind.annotation.RestController;
import de.tum.cit.aet.artemis.atlas.dto.metrics.StudentMetricsDTO;
+import de.tum.cit.aet.artemis.atlas.service.LearningMetricsService;
import de.tum.cit.aet.artemis.core.repository.UserRepository;
import de.tum.cit.aet.artemis.core.security.annotations.enforceRoleInCourse.EnforceAtLeastStudentInCourse;
-import de.tum.cit.aet.artemis.exercise.service.LearningMetricsService;
@Profile(PROFILE_CORE)
@RestController
diff --git a/src/main/java/de/tum/cit/aet/artemis/core/api/AbstractApi.java b/src/main/java/de/tum/cit/aet/artemis/core/api/AbstractApi.java
new file mode 100644
index 000000000000..8ca26f995aa0
--- /dev/null
+++ b/src/main/java/de/tum/cit/aet/artemis/core/api/AbstractApi.java
@@ -0,0 +1,4 @@
+package de.tum.cit.aet.artemis.core.api;
+
+public interface AbstractApi {
+}
diff --git a/src/main/java/de/tum/cit/aet/artemis/core/config/migration/entries/MigrationEntry20240614_140000.java b/src/main/java/de/tum/cit/aet/artemis/core/config/migration/entries/MigrationEntry20240614_140000.java
index 7371408c2df6..b385c3f01053 100644
--- a/src/main/java/de/tum/cit/aet/artemis/core/config/migration/entries/MigrationEntry20240614_140000.java
+++ b/src/main/java/de/tum/cit/aet/artemis/core/config/migration/entries/MigrationEntry20240614_140000.java
@@ -6,9 +6,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-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.atlas.service.competency.CompetencyProgressService;
+import de.tum.cit.aet.artemis.atlas.api.CompetencyProgressApi;
import de.tum.cit.aet.artemis.core.config.migration.MigrationEntry;
import de.tum.cit.aet.artemis.core.domain.Course;
import de.tum.cit.aet.artemis.core.repository.CourseRepository;
@@ -19,14 +17,11 @@ public class MigrationEntry20240614_140000 extends MigrationEntry {
private final CourseRepository courseRepository;
- private final CompetencyRepository competencyRepository;
+ private final CompetencyProgressApi competencyProgressApi;
- private final CompetencyProgressService competencyProgressService;
-
- public MigrationEntry20240614_140000(CourseRepository courseRepository, CompetencyRepository competencyRepository, CompetencyProgressService competencyProgressService) {
+ public MigrationEntry20240614_140000(CourseRepository courseRepository, CompetencyProgressApi competencyProgressApi) {
this.courseRepository = courseRepository;
- this.competencyRepository = competencyRepository;
- this.competencyProgressService = competencyProgressService;
+ this.competencyProgressApi = competencyProgressApi;
}
@Override
@@ -34,12 +29,7 @@ public void execute() {
List activeCourses = courseRepository.findAllActiveWithoutTestCourses(ZonedDateTime.now());
log.info("Updating competency progress for {} active courses", activeCourses.size());
-
- activeCourses.forEach(course -> {
- List competencies = competencyRepository.findByCourseIdOrderById(course.getId());
- // Asynchronously update the progress for each competency
- competencies.forEach(competencyProgressService::updateProgressByCompetencyAsync);
- });
+ competencyProgressApi.updateProgressForCoursesAsync(activeCourses);
}
@Override
diff --git a/src/main/java/de/tum/cit/aet/artemis/core/domain/User.java b/src/main/java/de/tum/cit/aet/artemis/core/domain/User.java
index 2ef2478cf295..c435194cdf40 100644
--- a/src/main/java/de/tum/cit/aet/artemis/core/domain/User.java
+++ b/src/main/java/de/tum/cit/aet/artemis/core/domain/User.java
@@ -156,23 +156,6 @@ public class User extends AbstractAuditingEntity implements Participant {
@Column(name = "vcs_access_token_expiry_date")
private ZonedDateTime vcsAccessTokenExpiryDate = null;
- /**
- * The actual full public ssh key of a user used to authenticate git clone and git push operations if available
- */
- @Nullable
- @JsonIgnore
- @Column(name = "ssh_public_key")
- private final String sshPublicKey = null;
-
- /**
- * A hash of the public ssh key for fast comparison in the database (with an index)
- */
- @Nullable
- @Size(max = 100)
- @JsonIgnore
- @Column(name = "ssh_public_key_hash")
- private final String sshPublicKeyHash = null;
-
@ElementCollection(fetch = FetchType.LAZY)
@CollectionTable(name = "user_groups", joinColumns = @JoinColumn(name = "user_id"))
@Column(name = "user_groups")
@@ -560,14 +543,4 @@ public void hasAcceptedIrisElseThrow() {
throw new AccessForbiddenException("The user has not accepted the Iris privacy policy yet.");
}
}
-
- @Nullable
- public String getSshPublicKey() {
- return sshPublicKey;
- }
-
- @Nullable
- public @Size(max = 100) String getSshPublicKeyHash() {
- return sshPublicKeyHash;
- }
}
diff --git a/src/main/java/de/tum/cit/aet/artemis/core/dto/UserDTO.java b/src/main/java/de/tum/cit/aet/artemis/core/dto/UserDTO.java
index 3d627425a8f7..1f5ea1e653b8 100644
--- a/src/main/java/de/tum/cit/aet/artemis/core/dto/UserDTO.java
+++ b/src/main/java/de/tum/cit/aet/artemis/core/dto/UserDTO.java
@@ -76,10 +76,6 @@ public class UserDTO extends AuditingEntityDTO {
private ZonedDateTime vcsAccessTokenExpiryDate;
- private String sshPublicKey;
-
- private String sshKeyHash;
-
private ZonedDateTime irisAccepted;
public UserDTO() {
@@ -262,14 +258,6 @@ public ZonedDateTime getVcsAccessTokenExpiryDate() {
return vcsAccessTokenExpiryDate;
}
- public String getSshPublicKey() {
- return sshPublicKey;
- }
-
- public void setSshPublicKey(String sshPublicKey) {
- this.sshPublicKey = sshPublicKey;
- }
-
@Override
public String toString() {
return "UserDTO{" + "login='" + login + '\'' + ", firstName='" + firstName + '\'' + ", lastName='" + lastName + '\'' + ", email='" + email + '\'' + ", imageUrl='"
@@ -293,12 +281,4 @@ public ZonedDateTime getIrisAccepted() {
public void setIrisAccepted(ZonedDateTime irisAccepted) {
this.irisAccepted = irisAccepted;
}
-
- public String getSshKeyHash() {
- return sshKeyHash;
- }
-
- public void setSshKeyHash(String sshKeyHash) {
- this.sshKeyHash = sshKeyHash;
- }
}
diff --git a/src/main/java/de/tum/cit/aet/artemis/core/repository/UserRepository.java b/src/main/java/de/tum/cit/aet/artemis/core/repository/UserRepository.java
index 5b66b31aee98..0d3280cf5d96 100644
--- a/src/main/java/de/tum/cit/aet/artemis/core/repository/UserRepository.java
+++ b/src/main/java/de/tum/cit/aet/artemis/core/repository/UserRepository.java
@@ -724,16 +724,6 @@ default Page searchAllWithGroupsByLoginOrNameInCourseAndReturnPage(Pageabl
""")
void updateUserLanguageKey(@Param("userId") long userId, @Param("languageKey") String languageKey);
- @Modifying
- @Transactional // ok because of modifying query
- @Query("""
- UPDATE User user
- SET user.sshPublicKeyHash = :sshPublicKeyHash,
- user.sshPublicKey = :sshPublicKey
- WHERE user.id = :userId
- """)
- void updateUserSshPublicKeyHash(@Param("userId") long userId, @Param("sshPublicKeyHash") String sshPublicKeyHash, @Param("sshPublicKey") String sshPublicKey);
-
@Modifying
@Transactional // ok because of modifying query
@Query("""
@@ -1120,8 +1110,6 @@ default boolean isCurrentUser(String login) {
return SecurityUtils.getCurrentUserLogin().map(currentLogin -> currentLogin.equals(login)).orElse(false);
}
- Optional findBySshPublicKeyHash(String keyString);
-
/**
* Finds all users which a non-null VCS access token that expires before some given date.
*
diff --git a/src/main/java/de/tum/cit/aet/artemis/core/service/CourseService.java b/src/main/java/de/tum/cit/aet/artemis/core/service/CourseService.java
index 81aa6a27f6b0..1b037532e453 100644
--- a/src/main/java/de/tum/cit/aet/artemis/core/service/CourseService.java
+++ b/src/main/java/de/tum/cit/aet/artemis/core/service/CourseService.java
@@ -53,10 +53,11 @@
import de.tum.cit.aet.artemis.assessment.service.ComplaintService;
import de.tum.cit.aet.artemis.assessment.service.PresentationPointsCalculationService;
import de.tum.cit.aet.artemis.assessment.service.TutorLeaderboardService;
-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.PrerequisiteRepository;
-import de.tum.cit.aet.artemis.atlas.service.learningpath.LearningPathService;
+import de.tum.cit.aet.artemis.atlas.api.CompetencyProgressApi;
+import de.tum.cit.aet.artemis.atlas.api.CompetencyRelationApi;
+import de.tum.cit.aet.artemis.atlas.api.LearningPathApi;
+import de.tum.cit.aet.artemis.atlas.api.PrerequisitesApi;
+import de.tum.cit.aet.artemis.communication.domain.FaqState;
import de.tum.cit.aet.artemis.communication.domain.NotificationType;
import de.tum.cit.aet.artemis.communication.domain.Post;
import de.tum.cit.aet.artemis.communication.domain.notification.GroupNotification;
@@ -131,7 +132,7 @@ public class CourseService {
private final TutorialGroupChannelManagementService tutorialGroupChannelManagementService;
- private final CompetencyRelationRepository competencyRelationRepository;
+ private final CompetencyRelationApi competencyRelationApi;
private final ExerciseService exerciseService;
@@ -161,9 +162,9 @@ public class CourseService {
private final AuditEventRepository auditEventRepository;
- private final CompetencyRepository competencyRepository;
+ private final CompetencyProgressApi competencyProgressApi;
- private final PrerequisiteRepository prerequisiteRepository;
+ private final PrerequisitesApi prerequisitesApi;
private final GradingScaleRepository gradingScaleRepository;
@@ -199,7 +200,7 @@ public class CourseService {
private final ConversationRepository conversationRepository;
- private final LearningPathService learningPathService;
+ private final LearningPathApi learningPathApi;
private final Optional irisSettingsService;
@@ -216,17 +217,17 @@ public class CourseService {
public CourseService(CourseRepository courseRepository, ExerciseService exerciseService, ExerciseDeletionService exerciseDeletionService,
AuthorizationCheckService authCheckService, UserRepository userRepository, LectureService lectureService, GroupNotificationRepository groupNotificationRepository,
ExerciseGroupRepository exerciseGroupRepository, AuditEventRepository auditEventRepository, UserService userService, ExamDeletionService examDeletionService,
- CompetencyRepository competencyRepository, GroupNotificationService groupNotificationService, ExamRepository examRepository,
+ CompetencyProgressApi competencyProgressApi, GroupNotificationService groupNotificationService, ExamRepository examRepository,
CourseExamExportService courseExamExportService, GradingScaleRepository gradingScaleRepository, StatisticsRepository statisticsRepository,
StudentParticipationRepository studentParticipationRepository, TutorLeaderboardService tutorLeaderboardService, RatingRepository ratingRepository,
ComplaintService complaintService, ComplaintRepository complaintRepository, ResultRepository resultRepository, ComplaintResponseRepository complaintResponseRepository,
SubmissionRepository submissionRepository, ProgrammingExerciseRepository programmingExerciseRepository, ExerciseRepository exerciseRepository,
ParticipantScoreRepository participantScoreRepository, PresentationPointsCalculationService presentationPointsCalculationService,
TutorialGroupRepository tutorialGroupRepository, PlagiarismCaseRepository plagiarismCaseRepository, ConversationRepository conversationRepository,
- LearningPathService learningPathService, Optional irisSettingsService, LectureRepository lectureRepository,
+ LearningPathApi learningPathApi, Optional irisSettingsService, LectureRepository lectureRepository,
TutorialGroupNotificationRepository tutorialGroupNotificationRepository, TutorialGroupChannelManagementService tutorialGroupChannelManagementService,
- PrerequisiteRepository prerequisiteRepository, CompetencyRelationRepository competencyRelationRepository, PostRepository postRepository,
- AnswerPostRepository answerPostRepository, BuildJobRepository buildJobRepository, FaqRepository faqRepository) {
+ PrerequisitesApi prerequisitesApi, CompetencyRelationApi competencyRelationApi, PostRepository postRepository, AnswerPostRepository answerPostRepository,
+ BuildJobRepository buildJobRepository, FaqRepository faqRepository) {
this.courseRepository = courseRepository;
this.exerciseService = exerciseService;
this.exerciseDeletionService = exerciseDeletionService;
@@ -238,7 +239,7 @@ public CourseService(CourseRepository courseRepository, ExerciseService exercise
this.auditEventRepository = auditEventRepository;
this.userService = userService;
this.examDeletionService = examDeletionService;
- this.competencyRepository = competencyRepository;
+ this.competencyProgressApi = competencyProgressApi;
this.groupNotificationService = groupNotificationService;
this.examRepository = examRepository;
this.courseExamExportService = courseExamExportService;
@@ -259,13 +260,13 @@ public CourseService(CourseRepository courseRepository, ExerciseService exercise
this.tutorialGroupRepository = tutorialGroupRepository;
this.plagiarismCaseRepository = plagiarismCaseRepository;
this.conversationRepository = conversationRepository;
- this.learningPathService = learningPathService;
+ this.learningPathApi = learningPathApi;
this.irisSettingsService = irisSettingsService;
this.lectureRepository = lectureRepository;
this.tutorialGroupNotificationRepository = tutorialGroupNotificationRepository;
this.tutorialGroupChannelManagementService = tutorialGroupChannelManagementService;
- this.prerequisiteRepository = prerequisiteRepository;
- this.competencyRelationRepository = competencyRelationRepository;
+ this.prerequisitesApi = prerequisitesApi;
+ this.competencyRelationApi = competencyRelationApi;
this.buildJobRepository = buildJobRepository;
this.postRepository = postRepository;
this.answerPostRepository = answerPostRepository;
@@ -343,7 +344,7 @@ public void fetchPlagiarismCasesForCourseExercises(Set exercises, Long
* @param user the user entity
* @return the course including exercises, lectures, exams, competencies and tutorial groups (filtered for given user)
*/
- public Course findOneWithExercisesAndLecturesAndExamsAndCompetenciesAndTutorialGroupsForUser(Long courseId, User user) {
+ public Course findOneWithExercisesAndLecturesAndExamsAndCompetenciesAndTutorialGroupsAndFaqForUser(Long courseId, User user) {
Course course = courseRepository.findByIdWithLecturesElseThrow(courseId);
// Load exercises with categories separately because this is faster than loading them with lectures and exam above (the query would become too complex)
course.setExercises(exerciseRepository.findByCourseIdWithCategories(course.getId()));
@@ -353,11 +354,14 @@ public Course findOneWithExercisesAndLecturesAndExamsAndCompetenciesAndTutorialG
// TODO: in the future, we only want to know if lectures exist, the actual lectures will be loaded when the user navigates into the lecture
course.setLectures(lectureService.filterVisibleLecturesWithActiveAttachments(course, course.getLectures(), user));
// NOTE: in this call we only want to know if competencies exist in the course, we will load them when the user navigates into them
- course.setNumberOfCompetencies(competencyRepository.countByCourse(course));
+ course.setNumberOfCompetencies(competencyProgressApi.countByCourse(course));
// NOTE: in this call we only want to know if prerequisites exist in the course, we will load them when the user navigates into them
- course.setNumberOfPrerequisites(prerequisiteRepository.countByCourse(course));
+ course.setNumberOfPrerequisites(prerequisitesApi.countByCourse(course));
// NOTE: in this call we only want to know if tutorial groups exist in the course, we will load them when the user navigates into them
course.setNumberOfTutorialGroups(tutorialGroupRepository.countByCourse(course));
+ if (course.isFaqEnabled()) {
+ course.setFaqs(faqRepository.findAllByCourseIdAndFaqState(courseId, FaqState.ACCEPTED));
+ }
if (authCheckService.isOnlyStudentInCourse(course, user)) {
course.setExams(examRepository.filterVisibleExams(course.getExams()));
}
@@ -574,9 +578,9 @@ private void deleteExercisesOfCourse(Course course) {
}
private void deleteCompetenciesOfCourse(Course course) {
- competencyRelationRepository.deleteAllByCourseId(course.getId());
- prerequisiteRepository.deleteAll(course.getPrerequisites());
- competencyRepository.deleteAll(course.getCompetencies());
+ competencyRelationApi.deleteAllByCourseId(course.getId());
+ prerequisitesApi.deleteAll(course.getPrerequisites());
+ competencyProgressApi.deleteAll(course.getCompetencies());
}
private void deleteFaqsOfCourse(Course course) {
@@ -614,7 +618,7 @@ public void enrollUserForCourseOrThrow(User user, Course course) {
authCheckService.checkUserAllowedToEnrollInCourseElseThrow(user, course);
userService.addUserToGroup(user, course.getStudentGroupName());
if (course.getLearningPathsEnabled()) {
- learningPathService.generateLearningPathForUser(course, user);
+ learningPathApi.generateLearningPathForUser(course, user);
}
final var auditEvent = new AuditEvent(user.getLogin(), Constants.ENROLL_IN_COURSE, "course=" + course.getTitle());
auditEventRepository.add(auditEvent);
@@ -647,7 +651,7 @@ public List registerUsersForCourseGroup(Long courseId, List ldapUserService, GuidedTourSettingsRepository guidedTourSettingsRepository, PasswordService passwordService,
Optional optionalVcsUserManagementService, Optional optionalCIUserManagementService,
- InstanceMessageSendService instanceMessageSendService, FileService fileService, ScienceEventRepository scienceEventRepository,
+ InstanceMessageSendService instanceMessageSendService, FileService fileService, ScienceEventApi scienceEventApi,
ParticipationVcsAccessTokenService participationVCSAccessTokenService, SavedPostRepository savedPostRepository) {
this.userCreationService = userCreationService;
this.userRepository = userRepository;
@@ -134,7 +134,7 @@ public UserService(UserCreationService userCreationService, UserRepository userR
this.optionalCIUserManagementService = optionalCIUserManagementService;
this.instanceMessageSendService = instanceMessageSendService;
this.fileService = fileService;
- this.scienceEventRepository = scienceEventRepository;
+ this.scienceEventApi = scienceEventApi;
this.participationVCSAccessTokenService = participationVCSAccessTokenService;
this.savedPostRepository = savedPostRepository;
}
@@ -508,7 +508,7 @@ protected void anonymizeUser(User user) {
clearUserCaches(user);
userRepository.flush();
- scienceEventRepository.renameIdentity(originalLogin, anonymizedLogin);
+ scienceEventApi.renameIdentity(originalLogin, anonymizedLogin);
if (userImageString != null) {
fileService.schedulePathForDeletion(FilePathService.actualPathForPublicPath(URI.create(userImageString)), 0);
diff --git a/src/main/java/de/tum/cit/aet/artemis/core/web/AccountResource.java b/src/main/java/de/tum/cit/aet/artemis/core/web/AccountResource.java
index 997574c76da7..16c94629047c 100644
--- a/src/main/java/de/tum/cit/aet/artemis/core/web/AccountResource.java
+++ b/src/main/java/de/tum/cit/aet/artemis/core/web/AccountResource.java
@@ -2,19 +2,15 @@
import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_CORE;
-import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Path;
-import java.security.GeneralSecurityException;
-import java.security.PublicKey;
import java.time.ZonedDateTime;
import java.util.Optional;
import jakarta.validation.Valid;
import jakarta.ws.rs.BadRequestException;
-import org.apache.sshd.common.config.keys.AuthorizedKeyEntry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Profile;
@@ -45,7 +41,6 @@
import de.tum.cit.aet.artemis.core.service.user.UserCreationService;
import de.tum.cit.aet.artemis.core.service.user.UserService;
import de.tum.cit.aet.artemis.programming.service.localvc.LocalVCPersonalAccessTokenManagementService;
-import de.tum.cit.aet.artemis.programming.service.localvc.ssh.HashUtils;
/**
* REST controller for managing the current user's account.
@@ -128,50 +123,6 @@ public ResponseEntity changePassword(@RequestBody PasswordChangeDTO passwo
return ResponseEntity.ok().build();
}
- /**
- * PUT account/ssh-public-key : sets the ssh public key
- *
- * @param sshPublicKey the ssh public key to set
- *
- * @return the ResponseEntity with status 200 (OK), with status 404 (Not Found), or with status 400 (Bad Request)
- */
- @PutMapping("account/ssh-public-key")
- @EnforceAtLeastStudent
- public ResponseEntity addSshPublicKey(@RequestBody String sshPublicKey) throws GeneralSecurityException, IOException {
-
- User user = userRepository.getUser();
- log.debug("REST request to add SSH key to user {}", user.getLogin());
- // Parse the public key string
- AuthorizedKeyEntry keyEntry;
- try {
- keyEntry = AuthorizedKeyEntry.parseAuthorizedKeyEntry(sshPublicKey);
- }
- catch (IllegalArgumentException e) {
- throw new BadRequestAlertException("Invalid SSH key format", "SSH key", "invalidKeyFormat", true);
- }
- // Extract the PublicKey object
- PublicKey publicKey = keyEntry.resolvePublicKey(null, null, null);
- String keyHash = HashUtils.getSha512Fingerprint(publicKey);
- userRepository.updateUserSshPublicKeyHash(user.getId(), keyHash, sshPublicKey);
- return ResponseEntity.ok().build();
- }
-
- /**
- * PUT account/ssh-public-key : sets the ssh public key
- *
- * @return the ResponseEntity with status 200 (OK), with status 404 (Not Found), or with status 400 (Bad Request)
- */
- @DeleteMapping("account/ssh-public-key")
- @EnforceAtLeastStudent
- public ResponseEntity deleteSshPublicKey() {
- User user = userRepository.getUser();
- log.debug("REST request to remove SSH key of user {}", user.getLogin());
- userRepository.updateUserSshPublicKeyHash(user.getId(), null, null);
-
- log.debug("Successfully deleted SSH key of user {}", user.getLogin());
- return ResponseEntity.ok().build();
- }
-
/**
* PUT account/user-vcs-access-token : creates a vcsAccessToken for a user
*
diff --git a/src/main/java/de/tum/cit/aet/artemis/core/web/CourseResource.java b/src/main/java/de/tum/cit/aet/artemis/core/web/CourseResource.java
index ba364d0c4fb5..2545a6ffb192 100644
--- a/src/main/java/de/tum/cit/aet/artemis/core/web/CourseResource.java
+++ b/src/main/java/de/tum/cit/aet/artemis/core/web/CourseResource.java
@@ -66,7 +66,7 @@
import de.tum.cit.aet.artemis.assessment.service.CourseScoreCalculationService;
import de.tum.cit.aet.artemis.assessment.service.GradingScaleService;
import de.tum.cit.aet.artemis.athena.service.AthenaModuleService;
-import de.tum.cit.aet.artemis.atlas.service.learningpath.LearningPathService;
+import de.tum.cit.aet.artemis.atlas.api.LearningPathApi;
import de.tum.cit.aet.artemis.communication.service.ConductAgreementService;
import de.tum.cit.aet.artemis.core.config.Constants;
import de.tum.cit.aet.artemis.core.domain.Course;
@@ -180,7 +180,7 @@ public class CourseResource {
@Value("${artemis.course-archives-path}")
private String courseArchivesDirPath;
- private final LearningPathService learningPathService;
+ private final LearningPathApi learningPathApi;
private final ExamRepository examRepository;
@@ -193,7 +193,7 @@ public CourseResource(UserRepository userRepository, CourseService courseService
TutorParticipationRepository tutorParticipationRepository, SubmissionService submissionService, Optional optionalVcsUserManagementService,
AssessmentDashboardService assessmentDashboardService, ExerciseRepository exerciseRepository, Optional optionalCiUserManagementService,
FileService fileService, TutorialGroupsConfigurationService tutorialGroupsConfigurationService, GradingScaleService gradingScaleService,
- CourseScoreCalculationService courseScoreCalculationService, GradingScaleRepository gradingScaleRepository, LearningPathService learningPathService,
+ CourseScoreCalculationService courseScoreCalculationService, GradingScaleRepository gradingScaleRepository, LearningPathApi learningPathApi,
ConductAgreementService conductAgreementService, Optional athenaModuleService, ExamRepository examRepository, ComplaintService complaintService,
TeamRepository teamRepository) {
this.courseService = courseService;
@@ -213,7 +213,7 @@ public CourseResource(UserRepository userRepository, CourseService courseService
this.gradingScaleService = gradingScaleService;
this.courseScoreCalculationService = courseScoreCalculationService;
this.gradingScaleRepository = gradingScaleRepository;
- this.learningPathService = learningPathService;
+ this.learningPathApi = learningPathApi;
this.conductAgreementService = conductAgreementService;
this.athenaModuleService = athenaModuleService;
this.examRepository = examRepository;
@@ -335,7 +335,7 @@ else if (courseUpdate.getCourseIcon() == null && existingCourse.getCourseIcon()
// if learning paths got enabled, generate learning paths for students
if (existingCourse.getLearningPathsEnabled() != courseUpdate.getLearningPathsEnabled() && courseUpdate.getLearningPathsEnabled()) {
Course courseWithCompetencies = courseRepository.findWithEagerCompetenciesAndPrerequisitesByIdElseThrow(result.getId());
- learningPathService.generateLearningPaths(courseWithCompetencies);
+ learningPathApi.generateLearningPaths(courseWithCompetencies);
}
// if access to restricted athena modules got disabled for the course, we need to set all exercises that use restricted modules to null
@@ -589,7 +589,7 @@ public ResponseEntity getCourseForDashboard(@PathVariable
log.debug("REST request to get one course {} with exams, lectures, exercises, participations, submissions and results, etc.", courseId);
User user = userRepository.getUserWithGroupsAndAuthorities();
- Course course = courseService.findOneWithExercisesAndLecturesAndExamsAndCompetenciesAndTutorialGroupsForUser(courseId, user);
+ Course course = courseService.findOneWithExercisesAndLecturesAndExamsAndCompetenciesAndTutorialGroupsAndFaqForUser(courseId, user);
log.debug("courseService.findOneWithExercisesAndLecturesAndExamsAndCompetenciesAndTutorialGroupsForUser done");
if (!authCheckService.isAtLeastStudentInCourse(course, user)) {
// user might be allowed to enroll in the course
@@ -1244,7 +1244,7 @@ public ResponseEntity addUserToCourseGroup(String userLogin, User instruct
courseService.addUserToGroup(userToAddToGroup.get(), group);
if (role == Role.STUDENT && course.getLearningPathsEnabled()) {
Course courseWithCompetencies = courseRepository.findWithEagerCompetenciesAndPrerequisitesByIdElseThrow(course.getId());
- learningPathService.generateLearningPathForUser(courseWithCompetencies, userToAddToGroup.get());
+ learningPathApi.generateLearningPathForUser(courseWithCompetencies, userToAddToGroup.get());
}
return ResponseEntity.ok().body(null);
}
diff --git a/src/main/java/de/tum/cit/aet/artemis/core/web/open/PublicAccountResource.java b/src/main/java/de/tum/cit/aet/artemis/core/web/open/PublicAccountResource.java
index bd673ece51d4..543ad85964da 100644
--- a/src/main/java/de/tum/cit/aet/artemis/core/web/open/PublicAccountResource.java
+++ b/src/main/java/de/tum/cit/aet/artemis/core/web/open/PublicAccountResource.java
@@ -166,8 +166,6 @@ public ResponseEntity getAccount() {
// we set this value on purpose here: the user can only fetch their own information, make the token available for constructing the token-based clone-URL
userDTO.setVcsAccessToken(user.getVcsAccessToken());
userDTO.setVcsAccessTokenExpiryDate(user.getVcsAccessTokenExpiryDate());
- userDTO.setSshPublicKey(user.getSshPublicKey());
- userDTO.setSshKeyHash(user.getSshPublicKeyHash());
log.info("GET /account {} took {}ms", user.getLogin(), System.currentTimeMillis() - start);
return ResponseEntity.ok(userDTO);
}
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 36b7e99fedd0..fd80e0a00fb5 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,8 +16,8 @@
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.api.CompetencyProgressApi;
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;
import de.tum.cit.aet.artemis.communication.service.conversation.ChannelService;
@@ -78,7 +78,7 @@ public class ExerciseDeletionService {
private final ChannelService channelService;
- private final CompetencyProgressService competencyProgressService;
+ private final CompetencyProgressApi competencyProgressApi;
private final Optional irisSettingsService;
@@ -86,8 +86,7 @@ public ExerciseDeletionService(ExerciseRepository exerciseRepository, ExerciseUn
ProgrammingExerciseService programmingExerciseService, ModelingExerciseService modelingExerciseService, QuizExerciseService quizExerciseService,
TutorParticipationRepository tutorParticipationRepository, ExampleSubmissionService exampleSubmissionService, StudentExamRepository studentExamRepository,
LectureUnitService lectureUnitService, PlagiarismResultRepository plagiarismResultRepository, TextExerciseService textExerciseService,
- ChannelRepository channelRepository, ChannelService channelService, CompetencyProgressService competencyProgressService,
- Optional irisSettingsService) {
+ ChannelRepository channelRepository, ChannelService channelService, CompetencyProgressApi competencyProgressApi, Optional irisSettingsService) {
this.exerciseRepository = exerciseRepository;
this.participationService = participationService;
this.programmingExerciseService = programmingExerciseService;
@@ -102,7 +101,7 @@ public ExerciseDeletionService(ExerciseRepository exerciseRepository, ExerciseUn
this.textExerciseService = textExerciseService;
this.channelRepository = channelRepository;
this.channelService = channelService;
- this.competencyProgressService = competencyProgressService;
+ this.competencyProgressApi = competencyProgressApi;
this.irisSettingsService = irisSettingsService;
}
@@ -215,7 +214,7 @@ public void delete(long exerciseId, boolean deleteStudentReposBuildPlans, boolea
exerciseRepository.delete(exercise);
}
- competencyLinks.stream().map(CompetencyExerciseLink::getCompetency).forEach(competencyProgressService::updateProgressByCompetencyAsync);
+ competencyLinks.stream().map(CompetencyExerciseLink::getCompetency).forEach(competencyProgressApi::updateProgressByCompetencyAsync);
}
/**
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 0a355a870620..73fce3b0f9f1 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
@@ -45,9 +45,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.api.CompetencyRelationApi;
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;
@@ -130,7 +130,7 @@ public class ExerciseService {
private final GroupNotificationScheduleService groupNotificationScheduleService;
- private final CompetencyExerciseLinkRepository competencyExerciseLinkRepository;
+ private final CompetencyRelationApi competencyRelationApi;
public ExerciseService(ExerciseRepository exerciseRepository, AuthorizationCheckService authCheckService, AuditEventRepository auditEventRepository,
TeamRepository teamRepository, ProgrammingExerciseRepository programmingExerciseRepository, Optional lti13ResourceLaunchRepository,
@@ -139,7 +139,7 @@ public ExerciseService(ExerciseRepository exerciseRepository, AuthorizationCheck
TutorLeaderboardService tutorLeaderboardService, ComplaintResponseRepository complaintResponseRepository, GradingCriterionRepository gradingCriterionRepository,
FeedbackRepository feedbackRepository, RatingService ratingService, ExerciseDateService exerciseDateService, ExampleSubmissionRepository exampleSubmissionRepository,
QuizBatchService quizBatchService, ExamLiveEventsService examLiveEventsService, GroupNotificationScheduleService groupNotificationScheduleService,
- CompetencyExerciseLinkRepository competencyExerciseLinkRepository) {
+ CompetencyRelationApi competencyRelationApi) {
this.exerciseRepository = exerciseRepository;
this.resultRepository = resultRepository;
this.authCheckService = authCheckService;
@@ -162,7 +162,7 @@ public ExerciseService(ExerciseRepository exerciseRepository, AuthorizationCheck
this.quizBatchService = quizBatchService;
this.examLiveEventsService = examLiveEventsService;
this.groupNotificationScheduleService = groupNotificationScheduleService;
- this.competencyExerciseLinkRepository = competencyExerciseLinkRepository;
+ this.competencyRelationApi = competencyRelationApi;
}
/**
@@ -773,7 +773,7 @@ public List getFeedbackToBeDeletedAfterGradingInstructionUpdate(boolea
* @param competency competency to remove
*/
public void removeCompetency(@NotNull Set exerciseLinks, @NotNull CourseCompetency competency) {
- competencyExerciseLinkRepository.deleteAll(exerciseLinks);
+ competencyRelationApi.deleteAllExerciseLinks(exerciseLinks);
competency.getExerciseLinks().removeAll(exerciseLinks);
}
@@ -815,7 +815,7 @@ public T saveWithCompetencyLinks(T exercise, Function
if (Hibernate.isInitialized(links) && !links.isEmpty()) {
savedExercise.setCompetencyLinks(links);
reconnectCompetencyExerciseLinks(savedExercise);
- savedExercise.setCompetencyLinks(new HashSet<>(competencyExerciseLinkRepository.saveAll(links)));
+ savedExercise.setCompetencyLinks(new HashSet<>(competencyRelationApi.saveAllExerciseLinks(links)));
}
return savedExercise;
diff --git a/src/main/java/de/tum/cit/aet/artemis/exercise/service/ParticipationService.java b/src/main/java/de/tum/cit/aet/artemis/exercise/service/ParticipationService.java
index 9074ad8ec1f8..a8c4d3507813 100644
--- a/src/main/java/de/tum/cit/aet/artemis/exercise/service/ParticipationService.java
+++ b/src/main/java/de/tum/cit/aet/artemis/exercise/service/ParticipationService.java
@@ -24,7 +24,7 @@
import de.tum.cit.aet.artemis.assessment.repository.StudentScoreRepository;
import de.tum.cit.aet.artemis.assessment.repository.TeamScoreRepository;
import de.tum.cit.aet.artemis.assessment.service.ResultService;
-import de.tum.cit.aet.artemis.atlas.service.competency.CompetencyProgressService;
+import de.tum.cit.aet.artemis.atlas.api.CompetencyProgressApi;
import de.tum.cit.aet.artemis.core.domain.User;
import de.tum.cit.aet.artemis.core.exception.ContinuousIntegrationException;
import de.tum.cit.aet.artemis.core.exception.EntityNotFoundException;
@@ -109,7 +109,7 @@ public class ParticipationService {
private final ParticipationVcsAccessTokenService participationVCSAccessTokenService;
- private final CompetencyProgressService competencyProgressService;
+ private final CompetencyProgressApi competencyProgressApi;
public ParticipationService(GitService gitService, Optional continuousIntegrationService, Optional versionControlService,
BuildLogEntryService buildLogEntryService, ParticipationRepository participationRepository, StudentParticipationRepository studentParticipationRepository,
@@ -118,7 +118,7 @@ public ParticipationService(GitService gitService, Optional localCISharedBuildJobQueueService, ProfileService profileService,
- ParticipationVcsAccessTokenService participationVCSAccessTokenService, CompetencyProgressService competencyProgressService) {
+ ParticipationVcsAccessTokenService participationVCSAccessTokenService, CompetencyProgressApi competencyProgressApi) {
this.gitService = gitService;
this.continuousIntegrationService = continuousIntegrationService;
this.versionControlService = versionControlService;
@@ -139,7 +139,7 @@ public ParticipationService(GitService gitService, Optional createFileUploadExercise(@RequestBody
channelService.createExerciseChannel(result, Optional.ofNullable(fileUploadExercise.getChannelName()));
groupNotificationScheduleService.checkNotificationsForNewExerciseAsync(fileUploadExercise);
- competencyProgressService.updateProgressByLearningObjectAsync(result);
+ competencyProgressApi.updateProgressByLearningObjectAsync(result);
return ResponseEntity.created(new URI("/api/file-upload-exercises/" + result.getId())).body(result);
}
@@ -287,7 +287,7 @@ public ResponseEntity updateFileUploadExercise(@RequestBody
participationRepository.removeIndividualDueDatesIfBeforeDueDate(updatedExercise, fileUploadExerciseBeforeUpdate.getDueDate());
exerciseService.notifyAboutExerciseChanges(fileUploadExerciseBeforeUpdate, updatedExercise, notificationText);
- competencyProgressService.updateProgressForUpdatedLearningObjectAsync(fileUploadExerciseBeforeUpdate, Optional.of(fileUploadExercise));
+ competencyProgressApi.updateProgressForUpdatedLearningObjectAsync(fileUploadExerciseBeforeUpdate, Optional.of(fileUploadExercise));
return ResponseEntity.ok(updatedExercise);
}
diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/dto/IngestionState.java b/src/main/java/de/tum/cit/aet/artemis/iris/dto/IngestionState.java
new file mode 100644
index 000000000000..6ed226ce4400
--- /dev/null
+++ b/src/main/java/de/tum/cit/aet/artemis/iris/dto/IngestionState.java
@@ -0,0 +1,5 @@
+package de.tum.cit.aet.artemis.iris.dto;
+
+public enum IngestionState {
+ NOT_STARTED, IN_PROGRESS, PARTIALLY_INGESTED, DONE, ERROR
+}
diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/dto/IngestionStateResponseDTO.java b/src/main/java/de/tum/cit/aet/artemis/iris/dto/IngestionStateResponseDTO.java
new file mode 100644
index 000000000000..54d5a04bd3cf
--- /dev/null
+++ b/src/main/java/de/tum/cit/aet/artemis/iris/dto/IngestionStateResponseDTO.java
@@ -0,0 +1,4 @@
+package de.tum.cit.aet.artemis.iris.dto;
+
+public record IngestionStateResponseDTO(IngestionState state) {
+}
diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisConnectorService.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisConnectorService.java
index f41de6b6c97d..911428b5b176 100644
--- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisConnectorService.java
+++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisConnectorService.java
@@ -2,6 +2,8 @@
import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_IRIS;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
@@ -20,11 +22,15 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import de.tum.cit.aet.artemis.iris.domain.settings.IrisSubSettingsType;
+import de.tum.cit.aet.artemis.iris.dto.IngestionState;
+import de.tum.cit.aet.artemis.iris.dto.IngestionStateResponseDTO;
import de.tum.cit.aet.artemis.iris.exception.IrisException;
import de.tum.cit.aet.artemis.iris.exception.IrisForbiddenException;
import de.tum.cit.aet.artemis.iris.exception.IrisInternalPyrisErrorException;
import de.tum.cit.aet.artemis.iris.service.pyris.dto.PyrisVariantDTO;
+import de.tum.cit.aet.artemis.iris.service.pyris.dto.lectureingestionwebhook.PyrisWebhookLectureDeletionExecutionDTO;
import de.tum.cit.aet.artemis.iris.service.pyris.dto.lectureingestionwebhook.PyrisWebhookLectureIngestionExecutionDTO;
+import de.tum.cit.aet.artemis.iris.service.pyris.job.IngestionWebhookJob;
import de.tum.cit.aet.artemis.iris.web.open.PublicPyrisStatusUpdateResource;
/**
@@ -42,12 +48,19 @@ public class PyrisConnectorService {
private final ObjectMapper objectMapper;
+ private final PyrisJobService pyrisJobService;
+
+ @Value("${server.url}")
+ private String artemisBaseUrl;
+
@Value("${artemis.iris.url}")
private String pyrisUrl;
- public PyrisConnectorService(@Qualifier("pyrisRestTemplate") RestTemplate restTemplate, MappingJackson2HttpMessageConverter springMvcJacksonConverter) {
+ public PyrisConnectorService(@Qualifier("pyrisRestTemplate") RestTemplate restTemplate, MappingJackson2HttpMessageConverter springMvcJacksonConverter,
+ PyrisJobService pyrisJobService) {
this.restTemplate = restTemplate;
this.objectMapper = springMvcJacksonConverter.getObjectMapper();
+ this.pyrisJobService = pyrisJobService;
}
/**
@@ -97,15 +110,63 @@ public void executePipeline(String feature, String variant, Object executionDTO)
* @param variant The variant of the feature to execute
* @param executionDTO The DTO sent as a body for the execution
*/
- public void executeLectureWebhook(String variant, PyrisWebhookLectureIngestionExecutionDTO executionDTO) {
+ public void executeLectureAddtionWebhook(String variant, PyrisWebhookLectureIngestionExecutionDTO executionDTO) {
var endpoint = "/api/v1/webhooks/lectures/" + variant;
try {
restTemplate.postForEntity(pyrisUrl + endpoint, objectMapper.valueToTree(executionDTO), Void.class);
}
+ catch (HttpStatusCodeException e) {
+ log.error("Failed to send lecture unit {} to Pyris: {}", executionDTO.pyrisLectureUnit().lectureUnitId(), e.getMessage());
+ throw toIrisException(e);
+ }
+ catch (RestClientException | IllegalArgumentException e) {
+ log.error("Failed to send lecture unit {} to Pyris: {}", executionDTO.pyrisLectureUnit().lectureUnitId(), e.getMessage());
+ throw new PyrisConnectorException("Could not fetch response from Pyris");
+ }
+ }
+
+ /**
+ * Retrieves the ingestion state of the lecture unit specified by retrieving the ingestion state from the vector database in Pyris.
+ *
+ * @param courseId id of the course
+ * @param lectureId id of the lecture
+ * @param lectureUnitId id of the lectureUnit to check in the Pyris vector database
+ * @return The ingestion state of the lecture Unit
+ *
+ */
+ IngestionState getLectureUnitIngestionState(long courseId, long lectureId, long lectureUnitId) {
+ try {
+ String encodedBaseUrl = URLEncoder.encode(artemisBaseUrl, StandardCharsets.UTF_8);
+ String url = pyrisUrl + "/api/v1/courses/" + courseId + "/lectures/" + lectureId + "/lectureUnits/" + lectureUnitId + "/ingestion-state?base_url=" + encodedBaseUrl;
+ IngestionStateResponseDTO response = restTemplate.getForObject(url, IngestionStateResponseDTO.class);
+ IngestionState state = response.state();
+ if (state != IngestionState.DONE) {
+ if (pyrisJobService.currentJobs().stream().filter(job -> job instanceof IngestionWebhookJob).map(job -> (IngestionWebhookJob) job)
+ .anyMatch(ingestionJob -> ingestionJob.courseId() == courseId && ingestionJob.lectureId() == lectureId && ingestionJob.lectureUnitId() == lectureUnitId)) {
+ return IngestionState.IN_PROGRESS;
+ }
+ }
+ return state;
+ }
+ catch (RestClientException | IllegalArgumentException e) {
+ log.error("Error fetching ingestion state for lecture {}, lecture unit {}", lectureId, lectureUnitId, e);
+ throw new PyrisConnectorException("Error fetching ingestion state for lecture unit" + lectureUnitId);
+ }
+ }
+
+ /**
+ * Executes a webhook and send lectures to the webhook with the given variant
+ *
+ * @param executionDTO The DTO sent as a body for the execution
+ */
+ public void executeLectureDeletionWebhook(PyrisWebhookLectureDeletionExecutionDTO executionDTO) {
+ var endpoint = "/api/v1/webhooks/lectures/delete";
+ try {
+ restTemplate.postForEntity(pyrisUrl + endpoint, objectMapper.valueToTree(executionDTO), Void.class);
+ }
catch (HttpStatusCodeException e) {
log.error("Failed to send lectures to Pyris", e);
throw toIrisException(e);
- // TODO : add error ingestion UI.
}
catch (RestClientException | IllegalArgumentException e) {
log.error("Failed to send lectures to Pyris", e);
diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisJobService.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisJobService.java
index 16e8969bc463..46c0dd7547cd 100644
--- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisJobService.java
+++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisJobService.java
@@ -3,6 +3,7 @@
import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_IRIS;
import java.security.SecureRandom;
+import java.util.Collection;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
@@ -93,11 +94,14 @@ public String addCourseChatJob(Long courseId, Long sessionId) {
/**
* Adds a new ingestion webhook job to the job map with a timeout.
*
+ * @param courseId the ID of the course associated with the webhook job
+ * @param lectureId the ID of the lecture associated with the webhook job
+ * @param lectureUnitId the ID of the lecture unit associated with the webhook job
* @return a unique token identifying the created webhook job
*/
- public String addIngestionWebhookJob() {
+ public String addIngestionWebhookJob(long courseId, long lectureId, long lectureUnitId) {
var token = generateJobIdToken();
- var job = new IngestionWebhookJob(token);
+ var job = new IngestionWebhookJob(token, courseId, lectureId, lectureUnitId);
long timeoutWebhookJob = 60;
TimeUnit unitWebhookJob = TimeUnit.MINUTES;
jobMap.put(token, job, timeoutWebhookJob, unitWebhookJob);
@@ -122,6 +126,15 @@ public void updateJob(PyrisJob job) {
jobMap.put(job.jobId(), job);
}
+ /**
+ * Get all current jobs.
+ *
+ * @return the all current jobs
+ */
+ public Collection currentJobs() {
+ return jobMap.values();
+ }
+
/**
* Get the job of a token.
*
diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisPipelineService.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisPipelineService.java
index 08861c06a773..c8851341e29e 100644
--- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisPipelineService.java
+++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisPipelineService.java
@@ -17,13 +17,13 @@
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
+import de.tum.cit.aet.artemis.atlas.api.LearningMetricsApi;
import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyJol;
import de.tum.cit.aet.artemis.atlas.dto.CompetencyJolDTO;
import de.tum.cit.aet.artemis.core.domain.Course;
import de.tum.cit.aet.artemis.core.repository.CourseRepository;
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.exercise.service.LearningMetricsService;
import de.tum.cit.aet.artemis.iris.domain.session.IrisCourseChatSession;
import de.tum.cit.aet.artemis.iris.domain.session.IrisExerciseChatSession;
import de.tum.cit.aet.artemis.iris.exception.IrisException;
@@ -61,20 +61,20 @@ public class PyrisPipelineService {
private final StudentParticipationRepository studentParticipationRepository;
- private final LearningMetricsService learningMetricsService;
+ private final LearningMetricsApi learningMetricsApi;
@Value("${server.url}")
private String artemisBaseUrl;
public PyrisPipelineService(PyrisConnectorService pyrisConnectorService, PyrisJobService pyrisJobService, PyrisDTOService pyrisDTOService,
- IrisChatWebsocketService irisChatWebsocketService, CourseRepository courseRepository, LearningMetricsService learningMetricsService,
+ IrisChatWebsocketService irisChatWebsocketService, CourseRepository courseRepository, LearningMetricsApi learningMetricsApi,
StudentParticipationRepository studentParticipationRepository) {
this.pyrisConnectorService = pyrisConnectorService;
this.pyrisJobService = pyrisJobService;
this.pyrisDTOService = pyrisDTOService;
this.irisChatWebsocketService = irisChatWebsocketService;
this.courseRepository = courseRepository;
- this.learningMetricsService = learningMetricsService;
+ this.learningMetricsApi = learningMetricsApi;
this.studentParticipationRepository = studentParticipationRepository;
}
@@ -186,7 +186,7 @@ public void executeCourseChatPipeline(String variant, IrisCourseChatSession sess
var fullCourse = loadCourseWithParticipationOfStudent(courseId, studentId);
return new PyrisCourseChatPipelineExecutionDTO(
PyrisExtendedCourseDTO.of(fullCourse),
- learningMetricsService.getStudentCourseMetrics(session.getUser().getId(), courseId),
+ learningMetricsApi.getStudentCourseMetrics(session.getUser().getId(), courseId),
competencyJol == null ? null : CompetencyJolDTO.of(competencyJol),
pyrisDTOService.toPyrisMessageDTOList(session.getMessages()),
new PyrisUserDTO(session.getUser()),
diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisStatusUpdateService.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisStatusUpdateService.java
index cdd398e5c683..ad1a63e60c58 100644
--- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisStatusUpdateService.java
+++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisStatusUpdateService.java
@@ -126,14 +126,12 @@ private void removeJobIfTerminatedElseUpdate(List stages, PyrisJo
}
/**
- * Handles the status update of a lecture ingestion job and logs the results for now => will change later
- * TODO: Update this method to handle changes beyond logging
+ * Handles the status update of a lecture ingestion job.
*
* @param job the job that is updated
* @param statusUpdate the status update
*/
public void handleStatusUpdate(IngestionWebhookJob job, PyrisLectureIngestionStatusUpdateDTO statusUpdate) {
- statusUpdate.stages().forEach(stage -> log.info(stage.name() + ":" + stage.message()));
removeJobIfTerminatedElseUpdate(statusUpdate.stages(), job);
}
}
diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisWebhookService.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisWebhookService.java
index 03f58ad5ba14..2138b8789b0b 100644
--- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisWebhookService.java
+++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisWebhookService.java
@@ -9,6 +9,9 @@
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -17,16 +20,23 @@
import org.springframework.stereotype.Service;
import de.tum.cit.aet.artemis.core.domain.Course;
+import de.tum.cit.aet.artemis.core.domain.DomainObject;
import de.tum.cit.aet.artemis.core.service.FilePathService;
import de.tum.cit.aet.artemis.iris.domain.settings.IrisCourseSettings;
+import de.tum.cit.aet.artemis.iris.dto.IngestionState;
import de.tum.cit.aet.artemis.iris.exception.IrisInternalPyrisErrorException;
import de.tum.cit.aet.artemis.iris.repository.IrisSettingsRepository;
import de.tum.cit.aet.artemis.iris.service.pyris.dto.PyrisPipelineExecutionSettingsDTO;
import de.tum.cit.aet.artemis.iris.service.pyris.dto.lectureingestionwebhook.PyrisLectureUnitWebhookDTO;
+import de.tum.cit.aet.artemis.iris.service.pyris.dto.lectureingestionwebhook.PyrisWebhookLectureDeletionExecutionDTO;
import de.tum.cit.aet.artemis.iris.service.pyris.dto.lectureingestionwebhook.PyrisWebhookLectureIngestionExecutionDTO;
import de.tum.cit.aet.artemis.iris.service.settings.IrisSettingsService;
import de.tum.cit.aet.artemis.lecture.domain.AttachmentType;
import de.tum.cit.aet.artemis.lecture.domain.AttachmentUnit;
+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.LectureRepository;
+import de.tum.cit.aet.artemis.lecture.repository.LectureUnitRepository;
@Service
@Profile(PROFILE_IRIS)
@@ -42,15 +52,21 @@ public class PyrisWebhookService {
private final IrisSettingsRepository irisSettingsRepository;
+ private final LectureUnitRepository lectureUnitRepository;
+
+ private final LectureRepository lectureRepository;
+
@Value("${server.url}")
private String artemisBaseUrl;
public PyrisWebhookService(PyrisConnectorService pyrisConnectorService, PyrisJobService pyrisJobService, IrisSettingsService irisSettingsService,
- IrisSettingsRepository irisSettingsRepository) {
+ IrisSettingsRepository irisSettingsRepository, LectureUnitRepository lectureUnitRepository, LectureRepository lectureRepository) {
this.pyrisConnectorService = pyrisConnectorService;
this.pyrisJobService = pyrisJobService;
this.irisSettingsService = irisSettingsService;
this.irisSettingsRepository = irisSettingsRepository;
+ this.lectureUnitRepository = lectureUnitRepository;
+ this.lectureRepository = lectureRepository;
}
private boolean lectureIngestionEnabled(Course course) {
@@ -78,15 +94,15 @@ private PyrisLectureUnitWebhookDTO processAttachmentForUpdate(AttachmentUnit att
String courseTitle = attachmentUnit.getLecture().getCourse().getTitle();
String courseDescription = attachmentUnit.getLecture().getCourse().getDescription() == null ? "" : attachmentUnit.getLecture().getCourse().getDescription();
String base64EncodedPdf = attachmentToBase64(attachmentUnit);
- return new PyrisLectureUnitWebhookDTO(true, artemisBaseUrl, base64EncodedPdf, lectureUnitId, lectureUnitName, lectureId, lectureTitle, courseId, courseTitle,
- courseDescription);
+ lectureUnitRepository.save(attachmentUnit);
+ return new PyrisLectureUnitWebhookDTO(base64EncodedPdf, lectureUnitId, lectureUnitName, lectureId, lectureTitle, courseId, courseTitle, courseDescription);
}
private PyrisLectureUnitWebhookDTO processAttachmentForDeletion(AttachmentUnit attachmentUnit) {
Long lectureUnitId = attachmentUnit.getId();
Long lectureId = attachmentUnit.getLecture().getId();
Long courseId = attachmentUnit.getLecture().getCourse().getId();
- return new PyrisLectureUnitWebhookDTO(false, artemisBaseUrl, "", lectureUnitId, "", lectureId, "", courseId, "", "");
+ return new PyrisLectureUnitWebhookDTO("", lectureUnitId, "", lectureId, "", courseId, "", "");
}
/**
@@ -94,15 +110,15 @@ private PyrisLectureUnitWebhookDTO processAttachmentForDeletion(AttachmentUnit a
*
* @param courseId Id of the course where the attachment is added
* @param newAttachmentUnits the new attachment Units to be sent to pyris for ingestion
- * @return true if the units were sent to pyris
*/
- public boolean autoUpdateAttachmentUnitsInPyris(Long courseId, List newAttachmentUnits) {
+ public void autoUpdateAttachmentUnitsInPyris(Long courseId, List newAttachmentUnits) {
IrisCourseSettings courseSettings = irisSettingsRepository.findCourseSettings(courseId).isPresent() ? irisSettingsRepository.findCourseSettings(courseId).get() : null;
if (courseSettings != null && courseSettings.getIrisLectureIngestionSettings() != null && courseSettings.getIrisLectureIngestionSettings().isEnabled()
&& courseSettings.getIrisLectureIngestionSettings().getAutoIngestOnLectureAttachmentUpload()) {
- return addLectureUnitsToPyrisDB(newAttachmentUnits) != null;
+ for (AttachmentUnit attachmentUnit : newAttachmentUnits) {
+ addLectureUnitToPyrisDB(attachmentUnit);
+ }
}
- return false;
}
/**
@@ -112,44 +128,29 @@ public boolean autoUpdateAttachmentUnitsInPyris(Long courseId, List attachmentUnits) {
- try {
- List toUpdateAttachmentUnits = new ArrayList<>();
- attachmentUnits.stream().filter(unit -> unit.getAttachment().getAttachmentType() == AttachmentType.FILE && unit.getAttachment().getLink().endsWith(".pdf"))
- .forEach(unit -> {
- toUpdateAttachmentUnits.add(processAttachmentForDeletion(unit));
- });
- if (!toUpdateAttachmentUnits.isEmpty()) {
- return executeLectureWebhook(toUpdateAttachmentUnits);
- }
- }
- catch (Exception e) {
- log.error(e.getMessage());
+ List toUpdateAttachmentUnits = new ArrayList<>();
+ attachmentUnits.stream().filter(unit -> unit.getAttachment().getAttachmentType() == AttachmentType.FILE && unit.getAttachment().getLink().endsWith(".pdf"))
+ .forEach(unit -> {
+ toUpdateAttachmentUnits.add(processAttachmentForDeletion(unit));
+ });
+ if (!toUpdateAttachmentUnits.isEmpty()) {
+ return executeLectureDeletionWebhook(toUpdateAttachmentUnits);
}
return null;
}
/**
- * adds the lectures to the vector database on pyris
+ * adds the lectures to the vector database in Pyris
*
- * @param attachmentUnits The attachmentUnit that got Updated / erased
- * @return jobToken if the job was created
+ * @param attachmentUnit The attachmentUnit that got Updated
+ * @return jobToken if the job was created else null
*/
- public String addLectureUnitsToPyrisDB(List attachmentUnits) {
- if (!lectureIngestionEnabled(attachmentUnits.getFirst().getLecture().getCourse())) {
- return null;
- }
- try {
- List toUpdateAttachmentUnits = new ArrayList<>();
- attachmentUnits.stream().filter(unit -> unit.getAttachment().getAttachmentType() == AttachmentType.FILE && unit.getAttachment().getLink().endsWith(".pdf"))
- .forEach(unit -> {
- toUpdateAttachmentUnits.add(processAttachmentForUpdate(unit));
- });
- if (!toUpdateAttachmentUnits.isEmpty()) {
- return executeLectureWebhook(toUpdateAttachmentUnits);
+ public String addLectureUnitToPyrisDB(AttachmentUnit attachmentUnit) {
+ if (lectureIngestionEnabled(attachmentUnit.getLecture().getCourse())) {
+ if (attachmentUnit.getAttachment().getAttachmentType() == AttachmentType.FILE && attachmentUnit.getAttachment().getLink().endsWith(".pdf")) {
+ return executeLectureAdditionWebhook(processAttachmentForUpdate(attachmentUnit));
}
- }
- catch (Exception e) {
- log.error(e.getMessage());
+ log.error("Attachment {} is not a file or is not of type pdf thus it will not be sent to Pyris", attachmentUnit.getId());
}
return null;
}
@@ -157,15 +158,85 @@ public String addLectureUnitsToPyrisDB(List attachmentUnits) {
/**
* executes executeLectureWebhook add or delete lectures from to the vector database on pyris
*
- * @param toUpdateAttachmentUnits The attachmentUnit that are goin to be Updated / deleted
+ * @param toUpdateAttachmentUnits The attachmentUnit that are goin to be deleted
* @return jobToken if the job was created
*/
- private String executeLectureWebhook(List toUpdateAttachmentUnits) {
- String jobToken = pyrisJobService.addIngestionWebhookJob();
+ private String executeLectureDeletionWebhook(List toUpdateAttachmentUnits) {
+ String jobToken = pyrisJobService.addIngestionWebhookJob(0, 0, 0);
PyrisPipelineExecutionSettingsDTO settingsDTO = new PyrisPipelineExecutionSettingsDTO(jobToken, List.of(), artemisBaseUrl);
- PyrisWebhookLectureIngestionExecutionDTO executionDTO = new PyrisWebhookLectureIngestionExecutionDTO(toUpdateAttachmentUnits, settingsDTO, List.of());
- pyrisConnectorService.executeLectureWebhook("fullIngestion", executionDTO);
+ PyrisWebhookLectureDeletionExecutionDTO executionDTO = new PyrisWebhookLectureDeletionExecutionDTO(toUpdateAttachmentUnits, settingsDTO, List.of());
+ pyrisConnectorService.executeLectureDeletionWebhook(executionDTO);
return jobToken;
}
+ /**
+ * executes executeLectureAdditionWebhook add lecture from to the vector database on pyris
+ *
+ * @param toUpdateAttachmentUnit The attachmentUnit that are going to be Updated
+ * @return jobToken if the job was created
+ */
+ private String executeLectureAdditionWebhook(PyrisLectureUnitWebhookDTO toUpdateAttachmentUnit) {
+ String jobToken = pyrisJobService.addIngestionWebhookJob(toUpdateAttachmentUnit.courseId(), toUpdateAttachmentUnit.lectureId(), toUpdateAttachmentUnit.lectureUnitId());
+ PyrisPipelineExecutionSettingsDTO settingsDTO = new PyrisPipelineExecutionSettingsDTO(jobToken, List.of(), artemisBaseUrl);
+ PyrisWebhookLectureIngestionExecutionDTO executionDTO = new PyrisWebhookLectureIngestionExecutionDTO(toUpdateAttachmentUnit, settingsDTO, List.of());
+ pyrisConnectorService.executeLectureAddtionWebhook("fullIngestion", executionDTO);
+ return jobToken;
+ }
+
+ /**
+ * uses getLectureUnitIngestionState for all lecture units and then determines the IngestionState of the lecture
+ *
+ * @param courseId id of the course
+ * @return The ingestion state of the lecture
+ *
+ */
+ public Map getLecturesIngestionState(long courseId) {
+ Set lectures = lectureRepository.findAllByCourseId(courseId);
+ return lectures.stream().collect(Collectors.toMap(DomainObject::getId, lecture -> getLectureIngestionState(courseId, lecture.getId())));
+
+ }
+
+ /**
+ * uses getLectureUnitIngestionState for all lecture units and then determines the IngestionState of the lecture
+ *
+ * @param courseId id of the course
+ * @param lectureId id of the lecture
+ * @return The ingestion state of the lecture
+ *
+ */
+ private IngestionState getLectureIngestionState(long courseId, long lectureId) {
+ Map states = getLectureUnitsIngestionState(courseId, lectureId);
+
+ if (states.values().stream().allMatch(state -> state == IngestionState.DONE)) {
+ return IngestionState.DONE;
+ }
+
+ if (states.values().stream().allMatch(state -> state == IngestionState.NOT_STARTED)) {
+ return IngestionState.NOT_STARTED;
+ }
+
+ if (states.values().stream().allMatch(state -> state == IngestionState.ERROR)) {
+ return IngestionState.ERROR;
+ }
+
+ if (states.containsValue(IngestionState.DONE) || states.containsValue(IngestionState.IN_PROGRESS)) {
+ return IngestionState.PARTIALLY_INGESTED;
+ }
+
+ return IngestionState.NOT_STARTED;
+ }
+
+ /**
+ * uses send an api call to get all the ingestion states of the lecture units of one lecture in Pyris
+ *
+ * @param courseId id of the course
+ * @param lectureId id of the lecture
+ * @return The ingestion state of the lecture Unit
+ */
+ public Map getLectureUnitsIngestionState(long courseId, long lectureId) {
+ List lectureunits = lectureRepository.findByIdWithLectureUnitsElseThrow(lectureId).getLectureUnits();
+ return lectureunits.stream().filter(lectureUnit -> lectureUnit instanceof AttachmentUnit)
+ .collect(Collectors.toMap(DomainObject::getId, unit -> pyrisConnectorService.getLectureUnitIngestionState(courseId, lectureId, unit.getId())));
+ }
+
}
diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/lectureingestionwebhook/PyrisLectureIngestionStatusUpdateDTO.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/lectureingestionwebhook/PyrisLectureIngestionStatusUpdateDTO.java
index b49aafefac79..56582b2e1ac2 100644
--- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/lectureingestionwebhook/PyrisLectureIngestionStatusUpdateDTO.java
+++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/lectureingestionwebhook/PyrisLectureIngestionStatusUpdateDTO.java
@@ -7,5 +7,5 @@
import de.tum.cit.aet.artemis.iris.service.pyris.dto.status.PyrisStageDTO;
@JsonInclude(JsonInclude.Include.NON_EMPTY)
-public record PyrisLectureIngestionStatusUpdateDTO(String result, List stages) {
+public record PyrisLectureIngestionStatusUpdateDTO(String result, List stages, long jobId) {
}
diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/lectureingestionwebhook/PyrisLectureUnitWebhookDTO.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/lectureingestionwebhook/PyrisLectureUnitWebhookDTO.java
index 1bef48d26c2e..b2f2cde1019d 100644
--- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/lectureingestionwebhook/PyrisLectureUnitWebhookDTO.java
+++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/lectureingestionwebhook/PyrisLectureUnitWebhookDTO.java
@@ -8,6 +8,6 @@
* providing necessary details such as lecture and course identifiers, names, and descriptions.
*/
@JsonInclude(JsonInclude.Include.NON_EMPTY)
-public record PyrisLectureUnitWebhookDTO(Boolean toUpdate, String artemisBaseUrl, String pdfFile, Long lectureUnitId, String lectureUnitName, Long lectureId, String lectureName,
- Long courseId, String courseName, String courseDescription) {
+public record PyrisLectureUnitWebhookDTO(String pdfFile, long lectureUnitId, String lectureUnitName, long lectureId, String lectureName, long courseId, String courseName,
+ String courseDescription) {
}
diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/lectureingestionwebhook/PyrisWebhookLectureDeletionExecutionDTO.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/lectureingestionwebhook/PyrisWebhookLectureDeletionExecutionDTO.java
new file mode 100644
index 000000000000..45f3bb809f01
--- /dev/null
+++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/lectureingestionwebhook/PyrisWebhookLectureDeletionExecutionDTO.java
@@ -0,0 +1,13 @@
+package de.tum.cit.aet.artemis.iris.service.pyris.dto.lectureingestionwebhook;
+
+import java.util.List;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+
+import de.tum.cit.aet.artemis.iris.service.pyris.dto.PyrisPipelineExecutionSettingsDTO;
+import de.tum.cit.aet.artemis.iris.service.pyris.dto.status.PyrisStageDTO;
+
+@JsonInclude(JsonInclude.Include.NON_EMPTY)
+public record PyrisWebhookLectureDeletionExecutionDTO(List pyrisLectureUnits, PyrisPipelineExecutionSettingsDTO settings,
+ List initialStages) {
+}
diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/lectureingestionwebhook/PyrisWebhookLectureIngestionExecutionDTO.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/lectureingestionwebhook/PyrisWebhookLectureIngestionExecutionDTO.java
index 4ef052e91de3..d1eb256e27b0 100644
--- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/lectureingestionwebhook/PyrisWebhookLectureIngestionExecutionDTO.java
+++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/lectureingestionwebhook/PyrisWebhookLectureIngestionExecutionDTO.java
@@ -8,6 +8,5 @@
import de.tum.cit.aet.artemis.iris.service.pyris.dto.status.PyrisStageDTO;
@JsonInclude(JsonInclude.Include.NON_EMPTY)
-public record PyrisWebhookLectureIngestionExecutionDTO(List pyrisLectureUnitWebhookDTOS, PyrisPipelineExecutionSettingsDTO settings,
- List initialStages) {
+public record PyrisWebhookLectureIngestionExecutionDTO(PyrisLectureUnitWebhookDTO pyrisLectureUnit, PyrisPipelineExecutionSettingsDTO settings, List initialStages) {
}
diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/job/IngestionWebhookJob.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/job/IngestionWebhookJob.java
index e29bb966520f..5dbfe0955520 100644
--- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/job/IngestionWebhookJob.java
+++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/job/IngestionWebhookJob.java
@@ -7,7 +7,7 @@
* An implementation of a PyrisJob for Lecture Ingestion in Pyris.
* This job is used to reference the details of then Ingestion when Pyris sends a status update.
*/
-public record IngestionWebhookJob(String jobId) implements PyrisJob {
+public record IngestionWebhookJob(String jobId, long courseId, long lectureId, long lectureUnitId) implements PyrisJob {
@Override
public boolean canAccess(Course course) {
diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/web/IrisResource.java b/src/main/java/de/tum/cit/aet/artemis/iris/web/IrisResource.java
index 58f40981a861..37e2983e39e0 100644
--- a/src/main/java/de/tum/cit/aet/artemis/iris/web/IrisResource.java
+++ b/src/main/java/de/tum/cit/aet/artemis/iris/web/IrisResource.java
@@ -2,34 +2,60 @@
import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_IRIS;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.boot.actuate.health.Status;
import org.springframework.context.annotation.Profile;
+import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
+import de.tum.cit.aet.artemis.core.domain.Course;
+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.security.Role;
import de.tum.cit.aet.artemis.core.security.annotations.EnforceAtLeastStudent;
+import de.tum.cit.aet.artemis.core.security.annotations.enforceRoleInCourse.EnforceAtLeastInstructorInCourse;
+import de.tum.cit.aet.artemis.core.service.AuthorizationCheckService;
+import de.tum.cit.aet.artemis.iris.dto.IngestionState;
import de.tum.cit.aet.artemis.iris.dto.IrisStatusDTO;
import de.tum.cit.aet.artemis.iris.service.IrisRateLimitService;
+import de.tum.cit.aet.artemis.iris.service.pyris.PyrisConnectorException;
import de.tum.cit.aet.artemis.iris.service.pyris.PyrisHealthIndicator;
+import de.tum.cit.aet.artemis.iris.service.pyris.PyrisWebhookService;
@Profile(PROFILE_IRIS)
@RestController
@RequestMapping("api/iris/")
public class IrisResource {
+ private static final Logger log = LoggerFactory.getLogger(IrisResource.class);
+
protected final UserRepository userRepository;
protected final IrisRateLimitService irisRateLimitService;
protected final PyrisHealthIndicator pyrisHealthIndicator;
- public IrisResource(UserRepository userRepository, PyrisHealthIndicator pyrisHealthIndicator, IrisRateLimitService irisRateLimitService) {
+ private final AuthorizationCheckService authorizationCheckService;
+
+ private final CourseRepository courseRepository;
+
+ private final PyrisWebhookService pyrisWebhookService;
+
+ public IrisResource(UserRepository userRepository, PyrisHealthIndicator pyrisHealthIndicator, IrisRateLimitService irisRateLimitService,
+ AuthorizationCheckService authorizationCheckService, CourseRepository courseRepository, PyrisWebhookService pyrisWebhookService) {
this.userRepository = userRepository;
this.pyrisHealthIndicator = pyrisHealthIndicator;
this.irisRateLimitService = irisRateLimitService;
+ this.authorizationCheckService = authorizationCheckService;
+ this.courseRepository = courseRepository;
+ this.pyrisWebhookService = pyrisWebhookService;
}
/**
@@ -47,4 +73,58 @@ public ResponseEntity getStatus() {
return ResponseEntity.ok(new IrisStatusDTO(health.getStatus() == Status.UP, rateLimitInfo));
}
+ /**
+ * Retrieves the overall ingestion state of a lecture by communicating with Pyris.
+ *
+ *
+ * This method sends a GET request to the external Pyris service to fetch the current ingestion
+ * state of all lectures in a course, identified by its `lectureId`. The ingestion state can be aggregated from
+ * multiple lecture units or can reflect the overall status of the lecture ingestion process.
+ *
+ *
+ * @param courseId the ID of the lecture for which the ingestion state is being requested
+ * @return a {@link ResponseEntity} containing the {@link IngestionState} of the lecture,
+ */
+ @GetMapping("courses/{courseId}/lectures/ingestion-state")
+ @EnforceAtLeastInstructorInCourse
+ public ResponseEntity