From fb007c3760ce52525e738f0d10523b97a959a6d2 Mon Sep 17 00:00:00 2001 From: Michael Dyer Date: Tue, 15 Oct 2024 19:53:04 +0200 Subject: [PATCH 1/3] Refactor --- .../IrisCompetencyGenerationService.java | 8 +- .../iris/service/pyris/PyrisJobService.java | 20 +-- .../service/pyris/PyrisPipelineService.java | 158 ++---------------- ...> PyrisProgrammingExerciseDTOService.java} | 63 ++++--- ...PyrisChatPipelineExecutionBaseDataDTO.java | 15 -- .../dto/chat/PyrisChatStatusUpdateDTO.java | 7 + .../PyrisCourseChatPipelineExecutionDTO.java | 26 ++- ...PyrisExerciseChatPipelineExecutionDTO.java | 27 ++- ...sTextExerciseChatPipelineExecutionDTO.java | 19 ++- .../PyrisTextExerciseChatStatusUpdateDTO.java | 7 + .../PyrisCompetencyExtractionInputDTO.java | 6 + ...petencyExtractionPipelineExecutionDTO.java | 14 +- .../pyris/dto/data/PyrisCompetencyDTO.java | 11 +- .../pyris/dto/data/PyrisCourseDTO.java | 4 +- ...yrisExerciseWithStudentSubmissionsDTO.java | 49 +++++- .../dto/data/PyrisExtendedCourseDTO.java | 44 ++++- .../pyris/dto/data/PyrisLectureDTO.java | 12 +- .../pyris/dto/data/PyrisLectureUnitDTO.java | 11 +- .../dto/data/PyrisMessageContentBaseDTO.java | 9 +- .../dto/data/PyrisProgrammingExerciseDTO.java | 17 +- .../pyris/dto/data/PyrisResultDTO.java | 9 +- .../pyris/dto/data/PyrisSubmissionDTO.java | 14 +- .../pyris/dto/data/PyrisTextExerciseDTO.java | 2 +- .../service/pyris/dto/data/PyrisUserDTO.java | 4 +- .../session/IrisCourseChatSessionService.java | 85 +++++++++- .../IrisExerciseChatSessionService.java | 48 +++++- .../IrisTextExerciseChatSessionService.java | 17 +- .../iris/PyrisLectureIngestionTest.java | 3 +- 28 files changed, 427 insertions(+), 282 deletions(-) rename src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/{PyrisDTOService.java => PyrisProgrammingExerciseDTOService.java} (78%) delete mode 100644 src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/chat/PyrisChatPipelineExecutionBaseDataDTO.java diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/IrisCompetencyGenerationService.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/IrisCompetencyGenerationService.java index 98182ae92b06..c41621b309ed 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/IrisCompetencyGenerationService.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/IrisCompetencyGenerationService.java @@ -8,7 +8,6 @@ import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyTaxonomy; import de.tum.cit.aet.artemis.core.domain.Course; import de.tum.cit.aet.artemis.core.domain.User; -import de.tum.cit.aet.artemis.iris.service.pyris.PyrisJobService; import de.tum.cit.aet.artemis.iris.service.pyris.PyrisPipelineService; import de.tum.cit.aet.artemis.iris.service.pyris.dto.competency.PyrisCompetencyExtractionPipelineExecutionDTO; import de.tum.cit.aet.artemis.iris.service.pyris.dto.competency.PyrisCompetencyRecommendationDTO; @@ -27,12 +26,9 @@ public class IrisCompetencyGenerationService { private final IrisWebsocketService websocketService; - private final PyrisJobService pyrisJobService; - - public IrisCompetencyGenerationService(PyrisPipelineService pyrisPipelineService, IrisWebsocketService websocketService, PyrisJobService pyrisJobService) { + public IrisCompetencyGenerationService(PyrisPipelineService pyrisPipelineService, IrisWebsocketService websocketService) { this.pyrisPipelineService = pyrisPipelineService; this.websocketService = websocketService; - this.pyrisJobService = pyrisJobService; } /** @@ -48,7 +44,7 @@ public void executeCompetencyExtractionPipeline(User user, Course course, String pyrisPipelineService.executePipeline( "competency-extraction", "default", - pyrisJobService.createTokenForJob(token -> new CompetencyExtractionJob(token, course.getId(), user.getLogin())), + jobId -> new CompetencyExtractionJob(jobId, course.getId(), user.getLogin()), executionDto -> new PyrisCompetencyExtractionPipelineExecutionDTO(executionDto, courseDescription, currentCompetencies, CompetencyTaxonomy.values(), 5), stages -> websocketService.send(user.getLogin(), websocketTopic(course.getId()), new PyrisCompetencyStatusUpdateDTO(stages, null)) ); 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 7933e9e20920..ae83ff611465 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 @@ -19,8 +19,6 @@ import de.tum.cit.aet.artemis.core.exception.AccessForbiddenException; import de.tum.cit.aet.artemis.core.exception.ConflictException; -import de.tum.cit.aet.artemis.iris.service.pyris.job.CourseChatJob; -import de.tum.cit.aet.artemis.iris.service.pyris.job.ExerciseChatJob; import de.tum.cit.aet.artemis.iris.service.pyris.job.IngestionWebhookJob; import de.tum.cit.aet.artemis.iris.service.pyris.job.PyrisJob; @@ -69,27 +67,13 @@ public void init() { * @param tokenToJobFunction the function to run with the token * @return the generated token */ - public String createTokenForJob(Function tokenToJobFunction) { + public String registerJob(Function tokenToJobFunction) { var token = generateJobIdToken(); var job = tokenToJobFunction.apply(token); jobMap.put(token, job); return token; } - public String addExerciseChatJob(Long courseId, Long exerciseId, Long sessionId) { - var token = generateJobIdToken(); - var job = new ExerciseChatJob(token, courseId, exerciseId, sessionId); - jobMap.put(token, job); - return token; - } - - public String addCourseChatJob(Long courseId, Long sessionId) { - var token = generateJobIdToken(); - var job = new CourseChatJob(token, courseId, sessionId); - jobMap.put(token, job); - return token; - } - /** * Adds a new ingestion webhook job to the job map with a timeout. * @@ -129,7 +113,7 @@ public PyrisJob getJob(String token) { * 2. Retrieves the PyrisJob object associated with the provided token. * 3. Throws an AccessForbiddenException if the token is invalid or not provided. *

- * The token was previously generated via {@link #createTokenForJob(Function)} + * The token was previously generated via {@link #registerJob(Function)} * * @param request the HttpServletRequest object representing the incoming request * @param jobClass the class of the PyrisJob object to cast the retrieved job to 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..c5c352d3dc5f 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 @@ -2,12 +2,7 @@ import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_IRIS; -import java.util.HashMap; -import java.util.HashSet; import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; import java.util.function.Consumer; import java.util.function.Function; @@ -17,27 +12,11 @@ import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; -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; import de.tum.cit.aet.artemis.iris.service.pyris.dto.PyrisPipelineExecutionDTO; import de.tum.cit.aet.artemis.iris.service.pyris.dto.PyrisPipelineExecutionSettingsDTO; -import de.tum.cit.aet.artemis.iris.service.pyris.dto.chat.course.PyrisCourseChatPipelineExecutionDTO; -import de.tum.cit.aet.artemis.iris.service.pyris.dto.chat.exercise.PyrisExerciseChatPipelineExecutionDTO; -import de.tum.cit.aet.artemis.iris.service.pyris.dto.data.PyrisCourseDTO; -import de.tum.cit.aet.artemis.iris.service.pyris.dto.data.PyrisExtendedCourseDTO; -import de.tum.cit.aet.artemis.iris.service.pyris.dto.data.PyrisUserDTO; import de.tum.cit.aet.artemis.iris.service.pyris.dto.status.PyrisStageDTO; -import de.tum.cit.aet.artemis.iris.service.websocket.IrisChatWebsocketService; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingSubmission; +import de.tum.cit.aet.artemis.iris.service.pyris.job.PyrisJob; /** * Service responsible for executing the various Pyris pipelines in a type-safe manner. @@ -53,34 +32,18 @@ public class PyrisPipelineService { private final PyrisJobService pyrisJobService; - private final PyrisDTOService pyrisDTOService; - - private final IrisChatWebsocketService irisChatWebsocketService; - - private final CourseRepository courseRepository; - - private final StudentParticipationRepository studentParticipationRepository; - - private final LearningMetricsService learningMetricsService; - @Value("${server.url}") private String artemisBaseUrl; - public PyrisPipelineService(PyrisConnectorService pyrisConnectorService, PyrisJobService pyrisJobService, PyrisDTOService pyrisDTOService, - IrisChatWebsocketService irisChatWebsocketService, CourseRepository courseRepository, LearningMetricsService learningMetricsService, - StudentParticipationRepository studentParticipationRepository) { + public PyrisPipelineService(PyrisConnectorService pyrisConnectorService, PyrisJobService pyrisJobService) { this.pyrisConnectorService = pyrisConnectorService; this.pyrisJobService = pyrisJobService; - this.pyrisDTOService = pyrisDTOService; - this.irisChatWebsocketService = irisChatWebsocketService; - this.courseRepository = courseRepository; - this.learningMetricsService = learningMetricsService; - this.studentParticipationRepository = studentParticipationRepository; } /** * Executes a pipeline on Pyris, identified by the given name and variant. - * The pipeline execution is tracked by a unique job token, which must be provided by the caller. + * The pipeline execution is tracked by a unique job token, which is created for each new job automatically. + * The caller must provide a mapper function to take this job token and produce a {@code PyrisJob} object to be registered. * The caller must additionally provide a mapper function to create the concrete DTO type for this pipeline from the base DTO. * The status of the pipeline execution is updated via a consumer that accepts a list of stages. This method will * call the consumer with the initial stages of the pipeline execution. Later stages will be sent back from Pyris, @@ -89,11 +52,12 @@ public PyrisPipelineService(PyrisConnectorService pyrisConnectorService, PyrisJo * * @param name the name of the pipeline to be executed * @param variant the variant of the pipeline - * @param jobToken a unique job token for tracking the pipeline execution - * @param dtoMapper a function to create the concrete DTO type for this pipeline from the base DTO - * @param statusUpdater a consumer to update the status of the pipeline execution + * @param jobFunction a function from job ID to job. Creates a new {@code PyrisJob} which will be registered in Hazelcast + * @param dtoMapper a function to create the concrete DTO type for this pipeline from the base execution DTO + * @param statusUpdater a consumer of stages to send status updates while the pipeline is being prepared */ - public void executePipeline(String name, String variant, String jobToken, Function dtoMapper, Consumer> statusUpdater) { + public void executePipeline(String name, String variant, Function jobFunction, Function dtoMapper, + Consumer> statusUpdater) { // Define the preparation stages of pipeline execution with their initial states // There will be more stages added in Pyris later var preparing = new PyrisStageDTO("Preparing", 10, null, null); @@ -102,6 +66,8 @@ public void executePipeline(String name, String variant, String jobToken, Functi // Send initial status update indicating that the preparation stage is in progress statusUpdater.accept(List.of(preparing.inProgress(), executing.notStarted())); + String jobToken = pyrisJobService.registerJob(jobFunction); + var baseDto = new PyrisPipelineExecutionDTO(new PyrisPipelineExecutionSettingsDTO(jobToken, List.of(), artemisBaseUrl), List.of(preparing.done())); var pipelineDto = dtoMapper.apply(baseDto); @@ -123,106 +89,4 @@ public void executePipeline(String name, String variant, String jobToken, Functi statusUpdater.accept(List.of(preparing.error("An internal error occurred"), executing.notStarted())); } } - - /** - * Execute the exercise chat pipeline for the given session. - * It provides specific data for the exercise chat pipeline, including: - * - The latest submission of the student - * - The programming exercise - * - The course the exercise is part of - *

- * - * @param variant the variant of the pipeline - * @param latestSubmission the latest submission of the student - * @param exercise the programming exercise - * @param session the chat session - * @see PyrisPipelineService#executePipeline for more details on the pipeline execution process. - */ - public void executeExerciseChatPipeline(String variant, Optional latestSubmission, ProgrammingExercise exercise, IrisExerciseChatSession session) { - // @formatter:off - executePipeline( - "tutor-chat", // TODO: Rename this to 'exercise-chat' with next breaking Pyris version - variant, - pyrisJobService.addExerciseChatJob(exercise.getCourseViaExerciseGroupOrCourseMember().getId(), exercise.getId(), session.getId()), - executionDto -> { - var course = exercise.getCourseViaExerciseGroupOrCourseMember(); - return new PyrisExerciseChatPipelineExecutionDTO( - latestSubmission.map(pyrisDTOService::toPyrisSubmissionDTO).orElse(null), - pyrisDTOService.toPyrisProgrammingExerciseDTO(exercise), - new PyrisCourseDTO(course), - pyrisDTOService.toPyrisMessageDTOList(session.getMessages()), - new PyrisUserDTO(session.getUser()), - executionDto.settings(), - executionDto.initialStages() - ); - }, - stages -> irisChatWebsocketService.sendStatusUpdate(session, stages) - ); - // @formatter:on - } - - /** - * Execute the course chat pipeline for the given session. - * It provides specific data for the course chat pipeline, including: - * - The full course with the participation of the student - * - The metrics of the student in the course - * - The competency JoL if this is due to a JoL set event - *

- * - * @param variant the variant of the pipeline - * @param session the chat session - * @param competencyJol if this is due to a JoL set event, this must be the newly created competencyJoL - * @see PyrisPipelineService#executePipeline for more details on the pipeline execution process. - */ - public void executeCourseChatPipeline(String variant, IrisCourseChatSession session, CompetencyJol competencyJol) { - // @formatter:off - var courseId = session.getCourse().getId(); - var studentId = session.getUser().getId(); - executePipeline( - "course-chat", - variant, - pyrisJobService.addCourseChatJob(courseId, session.getId()), - executionDto -> { - var fullCourse = loadCourseWithParticipationOfStudent(courseId, studentId); - return new PyrisCourseChatPipelineExecutionDTO( - PyrisExtendedCourseDTO.of(fullCourse), - learningMetricsService.getStudentCourseMetrics(session.getUser().getId(), courseId), - competencyJol == null ? null : CompetencyJolDTO.of(competencyJol), - pyrisDTOService.toPyrisMessageDTOList(session.getMessages()), - new PyrisUserDTO(session.getUser()), - executionDto.settings(), // flatten the execution dto here - executionDto.initialStages() - ); - }, - stages -> irisChatWebsocketService.sendStatusUpdate(session, stages) - ); - // @formatter:on - } - - /** - * Load the course with the participation of the student and set the participations on the exercises. - *

- * Spring Boot 3 does not support conditional left joins, so we have to load the participations separately. - * - * @param courseId the id of the course - * @param studentId the id of the student - */ - private Course loadCourseWithParticipationOfStudent(long courseId, long studentId) { - Course course = courseRepository.findWithEagerExercisesAndLecturesAndAttachmentsAndLectureUnitsAndCompetenciesAndExamsById(courseId).orElseThrow(); - List participations = studentParticipationRepository.findByStudentIdAndIndividualExercisesWithEagerSubmissionsResultIgnoreTestRuns(studentId, - course.getExercises()); - - Map> participationMap = new HashMap<>(); - for (StudentParticipation participation : participations) { - Long exerciseId = participation.getExercise().getId(); - participationMap.computeIfAbsent(exerciseId, k -> new HashSet<>()).add(participation); - } - - course.getExercises().forEach(exercise -> { - Set exerciseParticipations = participationMap.getOrDefault(exercise.getId(), Set.of()); - exercise.setStudentParticipations(exerciseParticipations); - }); - - return course; - } } diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisDTOService.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisProgrammingExerciseDTOService.java similarity index 78% rename from src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisDTOService.java rename to src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisProgrammingExerciseDTOService.java index ca42f85363bc..bb8e6f6b3c81 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisDTOService.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisProgrammingExerciseDTOService.java @@ -4,7 +4,6 @@ import static de.tum.cit.aet.artemis.core.util.TimeUtil.toInstant; import java.io.IOException; -import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -17,10 +16,8 @@ import org.springframework.stereotype.Service; import de.tum.cit.aet.artemis.core.service.ProfileService; -import de.tum.cit.aet.artemis.iris.domain.message.IrisMessage; import de.tum.cit.aet.artemis.iris.service.pyris.dto.data.PyrisBuildLogEntryDTO; import de.tum.cit.aet.artemis.iris.service.pyris.dto.data.PyrisFeedbackDTO; -import de.tum.cit.aet.artemis.iris.service.pyris.dto.data.PyrisMessageDTO; import de.tum.cit.aet.artemis.iris.service.pyris.dto.data.PyrisProgrammingExerciseDTO; import de.tum.cit.aet.artemis.iris.service.pyris.dto.data.PyrisResultDTO; import de.tum.cit.aet.artemis.iris.service.pyris.dto.data.PyrisSubmissionDTO; @@ -31,11 +28,15 @@ import de.tum.cit.aet.artemis.programming.service.GitService; import de.tum.cit.aet.artemis.programming.service.RepositoryService; +/** + * This service provides conversion methods for domain objects related to programming exercises into their DTO types. + * These conversions require special handling too complex for constructors in the DTOs themselves. + */ @Service @Profile(PROFILE_IRIS) -public class PyrisDTOService { +public class PyrisProgrammingExerciseDTOService { - private static final Logger log = LoggerFactory.getLogger(PyrisDTOService.class); + private static final Logger log = LoggerFactory.getLogger(PyrisProgrammingExerciseDTOService.class); private final GitService gitService; @@ -43,7 +44,7 @@ public class PyrisDTOService { private final ProfileService profileService; - public PyrisDTOService(GitService gitService, RepositoryService repositoryService, ProfileService profileService) { + public PyrisProgrammingExerciseDTOService(GitService gitService, RepositoryService repositoryService, ProfileService profileService) { this.gitService = gitService; this.repositoryService = repositoryService; this.profileService = profileService; @@ -56,7 +57,7 @@ public PyrisDTOService(GitService gitService, RepositoryService repositoryServic * @param exercise the programming exercise to convert * @return the converted PyrisProgrammingExerciseDTO */ - public PyrisProgrammingExerciseDTO toPyrisProgrammingExerciseDTO(ProgrammingExercise exercise) { + public PyrisProgrammingExerciseDTO convert(ProgrammingExercise exercise) { var templateRepositoryContents = getFilteredRepositoryContents(exercise.getTemplateParticipation()); var solutionRepositoryContents = getFilteredRepositoryContents(exercise.getSolutionParticipation()); Optional testRepo = Optional.empty(); @@ -68,8 +69,19 @@ public PyrisProgrammingExerciseDTO toPyrisProgrammingExerciseDTO(ProgrammingExer } var testsRepositoryContents = testRepo.map(repositoryService::getFilesContentFromWorkingCopy).orElse(Map.of()); - return new PyrisProgrammingExerciseDTO(exercise.getId(), exercise.getTitle(), exercise.getProgrammingLanguage(), templateRepositoryContents, solutionRepositoryContents, - testsRepositoryContents, exercise.getProblemStatement(), toInstant(exercise.getReleaseDate()), toInstant(exercise.getDueDate())); + // @formatter:off + return new PyrisProgrammingExerciseDTO( + exercise.getId(), + exercise.getTitle(), + exercise.getProgrammingLanguage(), + templateRepositoryContents, + solutionRepositoryContents, + testsRepositoryContents, + exercise.getProblemStatement(), + toInstant(exercise.getReleaseDate()), + toInstant(exercise.getDueDate()) + ); + // @formatter:on } /** @@ -79,23 +91,26 @@ public PyrisProgrammingExerciseDTO toPyrisProgrammingExerciseDTO(ProgrammingExer * @param submission the students submission * @return the converted PyrisSubmissionDTO */ - public PyrisSubmissionDTO toPyrisSubmissionDTO(ProgrammingSubmission submission) { - var buildLogEntries = submission.getBuildLogEntries().stream().map(buildLogEntry -> new PyrisBuildLogEntryDTO(toInstant(buildLogEntry.getTime()), buildLogEntry.getLog())) + public PyrisSubmissionDTO convert(ProgrammingSubmission submission) { + // @formatter:off + var buildLogEntries = submission.getBuildLogEntries() + .stream() + .map(buildLogEntry -> new PyrisBuildLogEntryDTO( + toInstant(buildLogEntry.getTime()), + buildLogEntry.getLog() + )) .toList(); var studentRepositoryContents = getFilteredRepositoryContents((ProgrammingExerciseParticipation) submission.getParticipation()); - return new PyrisSubmissionDTO(submission.getId(), toInstant(submission.getSubmissionDate()), studentRepositoryContents, submission.getParticipation().isPracticeMode(), - submission.isBuildFailed(), buildLogEntries, getLatestResult(submission)); - } - - /** - * Helper method to convert a list of IrisMessages to a list of PyrisMessageDTOs. - * This needs separate handling for the different types of message content. - * - * @param messages the messages with contents to convert - * @return the converted list of PyrisMessageDTOs - */ - public List toPyrisMessageDTOList(List messages) { - return messages.stream().map(PyrisMessageDTO::of).toList(); + return new PyrisSubmissionDTO( + submission.getId(), + toInstant(submission.getSubmissionDate()), + studentRepositoryContents, + submission.getParticipation().isPracticeMode(), + submission.isBuildFailed(), + buildLogEntries, + getLatestResult(submission) + ); + // @formatter:on } /** diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/chat/PyrisChatPipelineExecutionBaseDataDTO.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/chat/PyrisChatPipelineExecutionBaseDataDTO.java deleted file mode 100644 index fcd60b66b86b..000000000000 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/chat/PyrisChatPipelineExecutionBaseDataDTO.java +++ /dev/null @@ -1,15 +0,0 @@ -package de.tum.cit.aet.artemis.iris.service.pyris.dto.chat; - -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.data.PyrisMessageDTO; -import de.tum.cit.aet.artemis.iris.service.pyris.dto.data.PyrisUserDTO; -import de.tum.cit.aet.artemis.iris.service.pyris.dto.status.PyrisStageDTO; - -@JsonInclude(JsonInclude.Include.NON_EMPTY) -public record PyrisChatPipelineExecutionBaseDataDTO(List chatHistory, PyrisUserDTO user, PyrisPipelineExecutionSettingsDTO settings, - List initialStages) { -} diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/chat/PyrisChatStatusUpdateDTO.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/chat/PyrisChatStatusUpdateDTO.java index cbfa0b2d98dd..c40022a86426 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/chat/PyrisChatStatusUpdateDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/chat/PyrisChatStatusUpdateDTO.java @@ -6,6 +6,13 @@ import de.tum.cit.aet.artemis.iris.service.pyris.dto.status.PyrisStageDTO; +/** + * A DTO to send chat-based Pyris pipeline status updates to the client. + * + * @param result Iris' textual response, if any. TODO: Wrap in Optional for API clarity + * @param stages The pipeline execution progress described in a list of stages + * @param suggestions A list of suggested next inputs for the user to click on + */ @JsonInclude(JsonInclude.Include.NON_EMPTY) public record PyrisChatStatusUpdateDTO(String result, List stages, List suggestions) { } diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/chat/course/PyrisCourseChatPipelineExecutionDTO.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/chat/course/PyrisCourseChatPipelineExecutionDTO.java index a27dba39442c..d92182f2d62c 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/chat/course/PyrisCourseChatPipelineExecutionDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/chat/course/PyrisCourseChatPipelineExecutionDTO.java @@ -12,7 +12,27 @@ import de.tum.cit.aet.artemis.iris.service.pyris.dto.data.PyrisUserDTO; import de.tum.cit.aet.artemis.iris.service.pyris.dto.status.PyrisStageDTO; +/** + * DTO for executing the course chat pipeline on Pyris. + * + * @param course the course being chatted about + * @param metrics some metrics about the user + * @param competencyJol the competency judgement-of-learning, if any. TODO: Could wrap in Optional for API clarity + * @param chatHistory the conversation history so far (including the user's latest message) + * @param user the user + * @param settings execution settings + * @param initialStages initial stages from pipeline preparation + */ +// TODO: Instead of including settings and initialStages, include PyrisPipelineExecutionDTO @JsonInclude(JsonInclude.Include.NON_EMPTY) -public record PyrisCourseChatPipelineExecutionDTO(PyrisExtendedCourseDTO course, StudentMetricsDTO metrics, CompetencyJolDTO competencyJol, List chatHistory, - PyrisUserDTO user, PyrisPipelineExecutionSettingsDTO settings, List initialStages) { -} +// @formatter:off +public record PyrisCourseChatPipelineExecutionDTO( + PyrisExtendedCourseDTO course, + StudentMetricsDTO metrics, + CompetencyJolDTO competencyJol, + List chatHistory, + PyrisUserDTO user, + PyrisPipelineExecutionSettingsDTO settings, + List initialStages +) {} +// @formatter:on diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/chat/exercise/PyrisExerciseChatPipelineExecutionDTO.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/chat/exercise/PyrisExerciseChatPipelineExecutionDTO.java index e4c609042e6d..3eb8e198971a 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/chat/exercise/PyrisExerciseChatPipelineExecutionDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/chat/exercise/PyrisExerciseChatPipelineExecutionDTO.java @@ -12,8 +12,27 @@ import de.tum.cit.aet.artemis.iris.service.pyris.dto.data.PyrisUserDTO; import de.tum.cit.aet.artemis.iris.service.pyris.dto.status.PyrisStageDTO; +/** + * DTO for executing the programming exercise chat pipeline on Pyris. + * + * @param submission the user's latest programming submission, if any. TODO: could wrap in Optional for API clarity + * @param exercise the programming exercise being chatted about + * @param course the course + * @param chatHistory the conversation history so far (including the user's latest message) + * @param user the user + * @param settings execution settings + * @param initialStages the initial stages from pipeline preparation + */ +// TODO: Instead of including settings and initialStages, just include PyrisPipelineExecutionDTO (requires a change on Pyris) @JsonInclude(JsonInclude.Include.NON_EMPTY) -public record PyrisExerciseChatPipelineExecutionDTO(PyrisSubmissionDTO submission, PyrisProgrammingExerciseDTO exercise, PyrisCourseDTO course, List chatHistory, - PyrisUserDTO user, PyrisPipelineExecutionSettingsDTO settings, List initialStages) { - -} +// @formatter:off +public record PyrisExerciseChatPipelineExecutionDTO( + PyrisSubmissionDTO submission, + PyrisProgrammingExerciseDTO exercise, + PyrisCourseDTO course, + List chatHistory, + PyrisUserDTO user, + PyrisPipelineExecutionSettingsDTO settings, + List initialStages +) {} +// @formatter:on diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/chat/textexercise/PyrisTextExerciseChatPipelineExecutionDTO.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/chat/textexercise/PyrisTextExerciseChatPipelineExecutionDTO.java index 1b2cf59d9fdb..41adc60b0ee8 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/chat/textexercise/PyrisTextExerciseChatPipelineExecutionDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/chat/textexercise/PyrisTextExerciseChatPipelineExecutionDTO.java @@ -8,7 +8,20 @@ import de.tum.cit.aet.artemis.iris.service.pyris.dto.data.PyrisMessageDTO; import de.tum.cit.aet.artemis.iris.service.pyris.dto.data.PyrisTextExerciseDTO; +/** + * DTO to execute the text exercise chat pipeline on Pyris. + * + * @param execution pipeline execution details + * @param exercise the text exercise being chatted about + * @param conversation the chat history + * @param currentSubmission the user's current submission to the exercise + */ @JsonInclude(JsonInclude.Include.NON_EMPTY) -public record PyrisTextExerciseChatPipelineExecutionDTO(PyrisPipelineExecutionDTO execution, PyrisTextExerciseDTO exercise, List conversation, - String currentSubmission) { -} +// @formatter:off +public record PyrisTextExerciseChatPipelineExecutionDTO( + PyrisPipelineExecutionDTO execution, + PyrisTextExerciseDTO exercise, + List conversation, + String currentSubmission +) {} +// @formatter:on diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/chat/textexercise/PyrisTextExerciseChatStatusUpdateDTO.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/chat/textexercise/PyrisTextExerciseChatStatusUpdateDTO.java index e8b5de7a950b..3291dc3505c9 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/chat/textexercise/PyrisTextExerciseChatStatusUpdateDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/chat/textexercise/PyrisTextExerciseChatStatusUpdateDTO.java @@ -6,6 +6,13 @@ import de.tum.cit.aet.artemis.iris.service.pyris.dto.status.PyrisStageDTO; +/** + * A response from Pyris in the text exercise chat pipeline. + * The text exercise chat pipeline does not use {@code PyrisChatStatusUpdateDTO} because of the lack of suggestions. + * + * @param result The chat response from Iris, if this is the final update + * @param stages The status of the pipeline execution described in a list of stages + */ @JsonInclude(JsonInclude.Include.NON_EMPTY) public record PyrisTextExerciseChatStatusUpdateDTO(String result, List stages) { } diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/competency/PyrisCompetencyExtractionInputDTO.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/competency/PyrisCompetencyExtractionInputDTO.java index ab0b03b1ce2f..4156244088b3 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/competency/PyrisCompetencyExtractionInputDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/competency/PyrisCompetencyExtractionInputDTO.java @@ -2,6 +2,12 @@ import com.fasterxml.jackson.annotation.JsonInclude; +/** + * A DTO representing the data required from the client to start a Pyris competency extraction job. + * + * @param courseDescription the course description (might have been edited in the client) + * @param currentCompetencies the current competencies (might be unsaved in the client) + */ @JsonInclude(JsonInclude.Include.NON_EMPTY) public record PyrisCompetencyExtractionInputDTO(String courseDescription, PyrisCompetencyRecommendationDTO[] currentCompetencies) { } diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/competency/PyrisCompetencyExtractionPipelineExecutionDTO.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/competency/PyrisCompetencyExtractionPipelineExecutionDTO.java index 22fcaa3c1f9a..292eb75e59d0 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/competency/PyrisCompetencyExtractionPipelineExecutionDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/competency/PyrisCompetencyExtractionPipelineExecutionDTO.java @@ -6,7 +6,7 @@ import de.tum.cit.aet.artemis.iris.service.pyris.dto.PyrisPipelineExecutionDTO; /** - * DTO to execute the Iris competency extraction pipeline on Pyris + * DTO to execute the Iris competency extraction pipeline on Pyris. * * @param execution The pipeline execution details * @param courseDescription The description of the course @@ -15,6 +15,12 @@ * @param maxN The maximum number of competencies to extract */ @JsonInclude(JsonInclude.Include.NON_EMPTY) -public record PyrisCompetencyExtractionPipelineExecutionDTO(PyrisPipelineExecutionDTO execution, String courseDescription, PyrisCompetencyRecommendationDTO[] currentCompetencies, - CompetencyTaxonomy[] taxonomyOptions, int maxN) { -} +// @formatter:off +public record PyrisCompetencyExtractionPipelineExecutionDTO( + PyrisPipelineExecutionDTO execution, + String courseDescription, + PyrisCompetencyRecommendationDTO[] currentCompetencies, + CompetencyTaxonomy[] taxonomyOptions, + int maxN +) {} +// @formatter:on diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisCompetencyDTO.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisCompetencyDTO.java index 61ed07d77012..4d1e48b97e49 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisCompetencyDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisCompetencyDTO.java @@ -10,7 +10,16 @@ import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyTaxonomy; @JsonInclude(JsonInclude.Include.NON_EMPTY) -public record PyrisCompetencyDTO(long id, String title, String description, CompetencyTaxonomy taxonomy, Instant softDueDate, boolean optional) { +// @formatter:off +public record PyrisCompetencyDTO( + long id, + String title, + String description, + CompetencyTaxonomy taxonomy, + Instant softDueDate, + boolean optional +// @formatter:on +) { public static PyrisCompetencyDTO of(Competency competency) { return new PyrisCompetencyDTO(competency.getId(), competency.getTitle(), competency.getDescription(), competency.getTaxonomy(), toInstant(competency.getSoftDueDate()), diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisCourseDTO.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisCourseDTO.java index 911bf958cee1..6b59253640d2 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisCourseDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisCourseDTO.java @@ -7,7 +7,7 @@ @JsonInclude(JsonInclude.Include.NON_EMPTY) public record PyrisCourseDTO(long id, String name, String description) { - public PyrisCourseDTO(Course course) { - this(course.getId(), course.getTitle(), course.getDescription()); + public static PyrisCourseDTO of(Course course) { + return new PyrisCourseDTO(course.getId(), course.getTitle(), course.getDescription()); } } diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisExerciseWithStudentSubmissionsDTO.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisExerciseWithStudentSubmissionsDTO.java index 438dd0dfa171..200a081fa563 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisExerciseWithStudentSubmissionsDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisExerciseWithStudentSubmissionsDTO.java @@ -17,9 +17,22 @@ import de.tum.cit.aet.artemis.exercise.domain.IncludedInOverallScore; @JsonInclude(JsonInclude.Include.NON_EMPTY) -public record PyrisExerciseWithStudentSubmissionsDTO(long id, String title, ExerciseType type, ExerciseMode mode, double maxPoints, double bonusPoints, - DifficultyLevel difficultyLevel, Instant releaseDate, Instant dueDate, IncludedInOverallScore inclusionMode, boolean presentationScoreEnabled, - Set submissions) { +// @formatter:off +public record PyrisExerciseWithStudentSubmissionsDTO( + long id, + String title, + ExerciseType type, + ExerciseMode mode, + double maxPoints, + double bonusPoints, + DifficultyLevel difficultyLevel, + Instant releaseDate, + Instant dueDate, + IncludedInOverallScore inclusionMode, + boolean presentationScoreEnabled, + Set submissions +// @formatter:on +) { /** * Convert an exercise to a PyrisExerciseWithStudentSubmissionsDTO. @@ -28,13 +41,31 @@ public record PyrisExerciseWithStudentSubmissionsDTO(long id, String title, Exer * @return The converted exercise. */ public static PyrisExerciseWithStudentSubmissionsDTO of(Exercise exercise) { - var submissionDTOSet = exercise.getStudentParticipations().stream().filter(participation -> !participation.isTestRun()) - .flatMap(participation -> participation.getSubmissions().stream()).map(submission -> new PyrisStudentSubmissionDTO(toInstant(submission.getSubmissionDate()), - Optional.ofNullable(submission.getLatestResult()).map(Result::getScore).orElse(0D))) + // @formatter:off + var submissionDTOSet = exercise.getStudentParticipations() + .stream() + .filter(participation -> !participation.isTestRun()) + .flatMap(participation -> participation.getSubmissions().stream()) + .map(submission -> new PyrisStudentSubmissionDTO( + toInstant(submission.getSubmissionDate()), + Optional.ofNullable(submission.getLatestResult()).map(Result::getScore).orElse(0D) + )) .collect(Collectors.toSet()); - return new PyrisExerciseWithStudentSubmissionsDTO(exercise.getId(), exercise.getTitle(), exercise.getExerciseType(), exercise.getMode(), exercise.getMaxPoints(), - exercise.getBonusPoints(), exercise.getDifficulty(), toInstant(exercise.getReleaseDate()), toInstant(exercise.getDueDate()), exercise.getIncludedInOverallScore(), - Boolean.TRUE.equals(exercise.getPresentationScoreEnabled()), submissionDTOSet); + return new PyrisExerciseWithStudentSubmissionsDTO( + exercise.getId(), + exercise.getTitle(), + exercise.getExerciseType(), + exercise.getMode(), + exercise.getMaxPoints(), + exercise.getBonusPoints(), + exercise.getDifficulty(), + toInstant(exercise.getReleaseDate()), + toInstant(exercise.getDueDate()), + exercise.getIncludedInOverallScore(), + Boolean.TRUE.equals(exercise.getPresentationScoreEnabled()), + submissionDTOSet + ); + // @formatter:on } } diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisExtendedCourseDTO.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisExtendedCourseDTO.java index 9439793b8887..c1f6ac4c0e19 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisExtendedCourseDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisExtendedCourseDTO.java @@ -15,9 +15,25 @@ * questions regarding the course organization and content. */ @JsonInclude(JsonInclude.Include.NON_EMPTY) -public record PyrisExtendedCourseDTO(long id, String name, String description, Instant startTime, Instant endTime, ProgrammingLanguage defaultProgrammingLanguage, - int maxComplaints, int maxTeamComplaints, int maxComplaintTimeDays, int maxRequestMoreFeedbackTimeDays, Integer maxPoints, Integer presentationScore, - List exercises, List exams, List competencies) { +// @formatter:off +public record PyrisExtendedCourseDTO( + long id, + String name, + String description, + Instant startTime, + Instant endTime, + ProgrammingLanguage defaultProgrammingLanguage, + int maxComplaints, + int maxTeamComplaints, + int maxComplaintTimeDays, + int maxRequestMoreFeedbackTimeDays, + Integer maxPoints, + Integer presentationScore, + List exercises, + List exams, + List competencies +) { + // @formatter:on /** * Convert a course to a PyrisExtendedCourseDTO. @@ -31,8 +47,24 @@ public static PyrisExtendedCourseDTO of(Course course) { List exams = course.getExams().stream().map(PyrisExamDTO::of).toList(); List competencies = course.getCompetencies().stream().map(PyrisCompetencyDTO::of).toList(); - return new PyrisExtendedCourseDTO(course.getId(), course.getTitle(), course.getDescription(), toInstant(course.getStartDate()), toInstant(course.getEndDate()), - course.getDefaultProgrammingLanguage(), course.getMaxComplaints(), course.getMaxTeamComplaints(), course.getMaxComplaintTimeDays(), - course.getMaxRequestMoreFeedbackTimeDays(), course.getMaxPoints(), course.getPresentationScore(), exercises, exams, competencies); + // @formatter:off + return new PyrisExtendedCourseDTO( + course.getId(), + course.getTitle(), + course.getDescription(), + toInstant(course.getStartDate()), + toInstant(course.getEndDate()), + course.getDefaultProgrammingLanguage(), + course.getMaxComplaints(), + course.getMaxTeamComplaints(), + course.getMaxComplaintTimeDays(), + course.getMaxRequestMoreFeedbackTimeDays(), + course.getMaxPoints(), + course.getPresentationScore(), + exercises, + exams, + competencies + ); + // @formatter:on } } diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisLectureDTO.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisLectureDTO.java index 1bffb1a99369..d9d352a64747 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisLectureDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisLectureDTO.java @@ -6,5 +6,13 @@ import com.fasterxml.jackson.annotation.JsonInclude; @JsonInclude(JsonInclude.Include.NON_EMPTY) -public record PyrisLectureDTO(long id, String title, String description, ZonedDateTime startDate, ZonedDateTime endDate, List units) { -} +// @formatter:off +public record PyrisLectureDTO( + long id, + String title, + String description, + ZonedDateTime startDate, + ZonedDateTime endDate, + List units +) {} +// @formatter:on diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisLectureUnitDTO.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisLectureUnitDTO.java index f14c77f5d162..f77b0ff777ee 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisLectureUnitDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisLectureUnitDTO.java @@ -5,5 +5,12 @@ import com.fasterxml.jackson.annotation.JsonInclude; @JsonInclude(JsonInclude.Include.NON_EMPTY) -public record PyrisLectureUnitDTO(long id, long lectureId, Instant releaseDate, String name, int attachmentVersion) { -} +// @formatter:off +public record PyrisLectureUnitDTO( + long id, + long lectureId, + Instant releaseDate, + String name, + int attachmentVersion +) {} +// @formatter:on diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisMessageContentBaseDTO.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisMessageContentBaseDTO.java index cba000fe4e57..3c28332a0eb0 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisMessageContentBaseDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisMessageContentBaseDTO.java @@ -5,8 +5,13 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") -@JsonSubTypes({ @JsonSubTypes.Type(value = PyrisTextMessageContentDTO.class, name = "text"), @JsonSubTypes.Type(value = PyrisJsonMessageContentDTO.class, name = "json"), - @JsonSubTypes.Type(value = PyrisImageMessageContentDTO.class, name = "image"), }) +// @formatter:off +@JsonSubTypes({ + @JsonSubTypes.Type(value = PyrisTextMessageContentDTO.class, name = "text"), + @JsonSubTypes.Type(value = PyrisJsonMessageContentDTO.class, name = "json"), + @JsonSubTypes.Type(value = PyrisImageMessageContentDTO.class, name = "image"), +}) +// @formatter:on @JsonInclude(JsonInclude.Include.NON_EMPTY) public interface PyrisMessageContentBaseDTO { } diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisProgrammingExerciseDTO.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisProgrammingExerciseDTO.java index ecb84ca7ab8a..286e513c71be 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisProgrammingExerciseDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisProgrammingExerciseDTO.java @@ -8,7 +8,16 @@ import de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage; @JsonInclude(JsonInclude.Include.NON_EMPTY) -public record PyrisProgrammingExerciseDTO(long id, String name, ProgrammingLanguage programmingLanguage, Map templateRepository, - Map solutionRepository, Map testRepository, String problemStatement, Instant startDate, Instant endDate) { - -} +// @formatter:off +public record PyrisProgrammingExerciseDTO( + long id, + String name, + ProgrammingLanguage programmingLanguage, + Map templateRepository, + Map solutionRepository, + Map testRepository, + String problemStatement, + Instant startDate, + Instant endDate +) {} +// @formatter:off diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisResultDTO.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisResultDTO.java index b8c94756bc87..3cd329125491 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisResultDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisResultDTO.java @@ -6,5 +6,10 @@ import com.fasterxml.jackson.annotation.JsonInclude; @JsonInclude(JsonInclude.Include.NON_EMPTY) -public record PyrisResultDTO(Instant completionDate, boolean successful, List feedbacks) { -} +// @formatter:off +public record PyrisResultDTO( + Instant completionDate, + boolean successful, + List feedbacks +) {} +// @formatter:on diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisSubmissionDTO.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisSubmissionDTO.java index 67fe29dbba2c..1b98229b260e 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisSubmissionDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisSubmissionDTO.java @@ -7,6 +7,14 @@ import com.fasterxml.jackson.annotation.JsonInclude; @JsonInclude(JsonInclude.Include.NON_EMPTY) -public record PyrisSubmissionDTO(long id, Instant date, Map repository, boolean isPractice, boolean buildFailed, List buildLogEntries, - PyrisResultDTO latestResult) { -} +// @formatter:off +public record PyrisSubmissionDTO( + long id, + Instant date, + Map repository, + boolean isPractice, + boolean buildFailed, + List buildLogEntries, + PyrisResultDTO latestResult +) {} +// @formatter:on diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisTextExerciseDTO.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisTextExerciseDTO.java index 8ba3b1f9aaa8..621de55d7e1a 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisTextExerciseDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisTextExerciseDTO.java @@ -22,7 +22,7 @@ public static PyrisTextExerciseDTO of(TextExercise exercise) { return new PyrisTextExerciseDTO( exercise.getId(), exercise.getTitle(), - new PyrisCourseDTO(exercise.getCourseViaExerciseGroupOrCourseMember()), + PyrisCourseDTO.of(exercise.getCourseViaExerciseGroupOrCourseMember()), exercise.getProblemStatement(), Optional.ofNullable(exercise.getStartDate()).map(ChronoZonedDateTime::toInstant).orElse(null), Optional.ofNullable(exercise.getDueDate()).map(ChronoZonedDateTime::toInstant).orElse(null) diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisUserDTO.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisUserDTO.java index 4a9ec39fd7f8..9a38ca50ffda 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisUserDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisUserDTO.java @@ -7,7 +7,7 @@ @JsonInclude(JsonInclude.Include.NON_EMPTY) public record PyrisUserDTO(long id, String firstName, String lastName) { - public PyrisUserDTO(User user) { - this(user.getId(), user.getFirstName(), user.getLastName()); + public static PyrisUserDTO of(User user) { + return new PyrisUserDTO(user.getId(), user.getFirstName(), user.getLastName()); } } diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/session/IrisCourseChatSessionService.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/session/IrisCourseChatSessionService.java index 6dea7a728ca6..b9ac15d2cb73 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/session/IrisCourseChatSessionService.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/session/IrisCourseChatSessionService.java @@ -4,7 +4,13 @@ import java.time.LocalDate; import java.time.ZoneId; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; import java.util.Objects; +import java.util.Optional; +import java.util.Set; import java.util.concurrent.CompletableFuture; import org.springframework.context.annotation.Profile; @@ -14,11 +20,16 @@ import com.fasterxml.jackson.databind.ObjectMapper; 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.domain.User; import de.tum.cit.aet.artemis.core.exception.AccessForbiddenException; +import de.tum.cit.aet.artemis.core.repository.CourseRepository; import de.tum.cit.aet.artemis.core.security.Role; import de.tum.cit.aet.artemis.core.service.AuthorizationCheckService; +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.message.IrisMessage; import de.tum.cit.aet.artemis.iris.domain.message.IrisMessageSender; import de.tum.cit.aet.artemis.iris.domain.message.IrisTextMessageContent; @@ -30,6 +41,10 @@ import de.tum.cit.aet.artemis.iris.service.IrisRateLimitService; import de.tum.cit.aet.artemis.iris.service.pyris.PyrisPipelineService; import de.tum.cit.aet.artemis.iris.service.pyris.dto.chat.PyrisChatStatusUpdateDTO; +import de.tum.cit.aet.artemis.iris.service.pyris.dto.chat.course.PyrisCourseChatPipelineExecutionDTO; +import de.tum.cit.aet.artemis.iris.service.pyris.dto.data.PyrisExtendedCourseDTO; +import de.tum.cit.aet.artemis.iris.service.pyris.dto.data.PyrisMessageDTO; +import de.tum.cit.aet.artemis.iris.service.pyris.dto.data.PyrisUserDTO; import de.tum.cit.aet.artemis.iris.service.pyris.job.CourseChatJob; import de.tum.cit.aet.artemis.iris.service.settings.IrisSettingsService; import de.tum.cit.aet.artemis.iris.service.websocket.IrisChatWebsocketService; @@ -57,9 +72,16 @@ public class IrisCourseChatSessionService extends AbstractIrisChatSessionService private final PyrisPipelineService pyrisPipelineService; + private final CourseRepository courseRepository; + + private final StudentParticipationRepository studentParticipationRepository; + + private final LearningMetricsService learningMetricsService; + public IrisCourseChatSessionService(IrisMessageService irisMessageService, IrisSettingsService irisSettingsService, IrisChatWebsocketService irisChatWebsocketService, AuthorizationCheckService authCheckService, IrisSessionRepository irisSessionRepository, IrisRateLimitService rateLimitService, - IrisCourseChatSessionRepository irisCourseChatSessionRepository, PyrisPipelineService pyrisPipelineService, ObjectMapper objectMapper) { + IrisCourseChatSessionRepository irisCourseChatSessionRepository, PyrisPipelineService pyrisPipelineService, ObjectMapper objectMapper, + CourseRepository courseRepository, StudentParticipationRepository studentParticipationRepository, LearningMetricsService learningMetricsService) { super(irisSessionRepository, objectMapper); this.irisMessageService = irisMessageService; this.irisSettingsService = irisSettingsService; @@ -69,6 +91,9 @@ public IrisCourseChatSessionService(IrisMessageService irisMessageService, IrisS this.rateLimitService = rateLimitService; this.irisCourseChatSessionRepository = irisCourseChatSessionRepository; this.pyrisPipelineService = pyrisPipelineService; + this.courseRepository = courseRepository; + this.studentParticipationRepository = studentParticipationRepository; + this.learningMetricsService = learningMetricsService; } /** @@ -120,10 +145,62 @@ public void requestAndHandleResponse(IrisCourseChatSession session) { requestAndHandleResponse(session, variant, null); } - private void requestAndHandleResponse(IrisCourseChatSession session, String variant, CompetencyJol competencyJol) { - var chatSession = (IrisCourseChatSession) irisSessionRepository.findByIdWithMessagesAndContents(session.getId()); + private void requestAndHandleResponse(IrisCourseChatSession _session, String variant, CompetencyJol competencyJol) { + var session = (IrisCourseChatSession) irisSessionRepository.findByIdWithMessagesAndContents(_session.getId()); + + var courseId = session.getCourse().getId(); + var userId = session.getUser().getId(); + + var courseDTO = PyrisExtendedCourseDTO.of(loadCourseWithParticipationOfStudent(courseId, userId)); + var metricsDTO = learningMetricsService.getStudentCourseMetrics(userId, courseId); + var competencyJolDTO = Optional.ofNullable(competencyJol).map(CompetencyJolDTO::of).orElse(null); + var conversation = session.getMessages().stream().map(PyrisMessageDTO::of).toList(); + var userDTO = PyrisUserDTO.of(session.getUser()); + + // @formatter:off + pyrisPipelineService.executePipeline( + "course-chat", + variant, + token -> new CourseChatJob(token, courseId, session.getId()), + executionDto -> new PyrisCourseChatPipelineExecutionDTO( + courseDTO, + metricsDTO, + competencyJolDTO, + conversation, + userDTO, + executionDto.settings(), + executionDto.initialStages() + ), + stages -> irisChatWebsocketService.sendStatusUpdate(session, stages) + ); + // @formatter:on + } + + /** + * Load the course with the participation of the student and set the participations on the exercises. + *

+ * Spring Boot 3 does not support conditional left joins, so we have to load the participations separately. + * + * @param courseId the id of the course + * @param studentId the id of the student + */ + private Course loadCourseWithParticipationOfStudent(long courseId, long studentId) { + Course course = courseRepository.findWithEagerExercisesAndLecturesAndAttachmentsAndLectureUnitsAndCompetenciesAndExamsById(courseId).orElseThrow(); + List participations = studentParticipationRepository.findByStudentIdAndIndividualExercisesWithEagerSubmissionsResultIgnoreTestRuns(studentId, + course.getExercises()); + + Map> participationMap = new HashMap<>(); + for (StudentParticipation participation : participations) { + Long exerciseId = participation.getExercise().getId(); + participationMap.computeIfAbsent(exerciseId, k -> new HashSet<>()).add(participation); + } + + course.getExercises().forEach(exercise -> { + Set exerciseParticipations = participationMap.getOrDefault(exercise.getId(), Set.of()); + exercise.setStudentParticipations(exerciseParticipations); + }); - pyrisPipelineService.executeCourseChatPipeline(variant, chatSession, competencyJol); + return course; } /** diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/session/IrisExerciseChatSessionService.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/session/IrisExerciseChatSessionService.java index d520540a2db4..f2b1f5b2159c 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/session/IrisExerciseChatSessionService.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/session/IrisExerciseChatSessionService.java @@ -25,7 +25,12 @@ import de.tum.cit.aet.artemis.iris.service.IrisMessageService; import de.tum.cit.aet.artemis.iris.service.IrisRateLimitService; import de.tum.cit.aet.artemis.iris.service.pyris.PyrisPipelineService; +import de.tum.cit.aet.artemis.iris.service.pyris.PyrisProgrammingExerciseDTOService; import de.tum.cit.aet.artemis.iris.service.pyris.dto.chat.PyrisChatStatusUpdateDTO; +import de.tum.cit.aet.artemis.iris.service.pyris.dto.chat.exercise.PyrisExerciseChatPipelineExecutionDTO; +import de.tum.cit.aet.artemis.iris.service.pyris.dto.data.PyrisCourseDTO; +import de.tum.cit.aet.artemis.iris.service.pyris.dto.data.PyrisMessageDTO; +import de.tum.cit.aet.artemis.iris.service.pyris.dto.data.PyrisUserDTO; import de.tum.cit.aet.artemis.iris.service.pyris.job.ExerciseChatJob; import de.tum.cit.aet.artemis.iris.service.settings.IrisSettingsService; import de.tum.cit.aet.artemis.iris.service.websocket.IrisChatWebsocketService; @@ -62,11 +67,13 @@ public class IrisExerciseChatSessionService extends AbstractIrisChatSessionServi private final ProgrammingExerciseRepository programmingExerciseRepository; + private final PyrisProgrammingExerciseDTOService pyrisProgrammingExerciseDTOService; + public IrisExerciseChatSessionService(IrisMessageService irisMessageService, IrisSettingsService irisSettingsService, IrisChatWebsocketService irisChatWebsocketService, AuthorizationCheckService authCheckService, IrisSessionRepository irisSessionRepository, ProgrammingExerciseStudentParticipationRepository programmingExerciseStudentParticipationRepository, ProgrammingSubmissionRepository programmingSubmissionRepository, IrisRateLimitService rateLimitService, PyrisPipelineService pyrisPipelineService, ProgrammingExerciseRepository programmingExerciseRepository, - ObjectMapper objectMapper) { + ObjectMapper objectMapper, PyrisProgrammingExerciseDTOService pyrisProgrammingExerciseDTOService) { super(irisSessionRepository, objectMapper); this.irisMessageService = irisMessageService; this.irisSettingsService = irisSettingsService; @@ -78,6 +85,7 @@ public IrisExerciseChatSessionService(IrisMessageService irisMessageService, Iri this.rateLimitService = rateLimitService; this.pyrisPipelineService = pyrisPipelineService; this.programmingExerciseRepository = programmingExerciseRepository; + this.pyrisProgrammingExerciseDTOService = pyrisProgrammingExerciseDTOService; } /** @@ -134,19 +142,41 @@ public void checkRateLimit(User user) { * Sends all messages of the session to an LLM and handles the response by saving the message * and sending it to the student via the Websocket. * - * @param session The chat session to send to the LLM + * @param _irisSession The chat session to send to the LLM. The full session is loaded from the database by ID. */ @Override - public void requestAndHandleResponse(IrisExerciseChatSession session) { - var chatSession = (IrisExerciseChatSession) irisSessionRepository.findByIdWithMessagesAndContents(session.getId()); - if (chatSession.getExercise().isExamExercise()) { + public void requestAndHandleResponse(IrisExerciseChatSession _irisSession) { + var session = (IrisExerciseChatSession) irisSessionRepository.findByIdWithMessagesAndContents(_irisSession.getId()); + if (session.getExercise().isExamExercise()) { throw new ConflictException("Iris is not supported for exam exercises", "Iris", "irisExamExercise"); } - var exercise = programmingExerciseRepository.findByIdWithTemplateAndSolutionParticipationElseThrow(chatSession.getExercise().getId()); - var latestSubmission = getLatestSubmissionIfExists(exercise, chatSession.getUser()); - var variant = irisSettingsService.getCombinedIrisSettingsFor(session.getExercise(), false).irisChatSettings().selectedVariant(); - pyrisPipelineService.executeExerciseChatPipeline(variant, latestSubmission, exercise, chatSession); + String variant = irisSettingsService.getCombinedIrisSettingsFor(session.getExercise(), false).irisChatSettings().selectedVariant(); + var exercise = programmingExerciseRepository.findByIdWithTemplateAndSolutionParticipationElseThrow(session.getExercise().getId()); + var latestSubmissionDTO = getLatestSubmissionIfExists(exercise, session.getUser()).map(pyrisProgrammingExerciseDTOService::convert).orElse(null); + var exerciseDTO = pyrisProgrammingExerciseDTOService.convert(exercise); + var course = exercise.getCourseViaExerciseGroupOrCourseMember(); + var courseDTO = PyrisCourseDTO.of(course); + var conversationDTO = session.getMessages().stream().map(PyrisMessageDTO::of).toList(); + var userDTO = PyrisUserDTO.of(session.getUser()); + + // @formatter:off + pyrisPipelineService.executePipeline( + "tutor-chat", // TODO: Rename this to 'programming-exercise-chat' + variant, + token -> new ExerciseChatJob(token, course.getId(), exercise.getId(), session.getId()), + executionDto -> new PyrisExerciseChatPipelineExecutionDTO( + latestSubmissionDTO, + exerciseDTO, + courseDTO, + conversationDTO, + userDTO, + executionDto.settings(), + executionDto.initialStages() + ), + stages -> irisChatWebsocketService.sendStatusUpdate(session, stages) + ); + // @formatter:on } private Optional getLatestSubmissionIfExists(ProgrammingExercise exercise, User user) { diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/session/IrisTextExerciseChatSessionService.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/session/IrisTextExerciseChatSessionService.java index 4520417aad48..40b7629daa27 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/session/IrisTextExerciseChatSessionService.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/session/IrisTextExerciseChatSessionService.java @@ -20,7 +20,6 @@ import de.tum.cit.aet.artemis.iris.repository.IrisSessionRepository; import de.tum.cit.aet.artemis.iris.service.IrisMessageService; import de.tum.cit.aet.artemis.iris.service.IrisRateLimitService; -import de.tum.cit.aet.artemis.iris.service.pyris.PyrisJobService; import de.tum.cit.aet.artemis.iris.service.pyris.PyrisPipelineService; import de.tum.cit.aet.artemis.iris.service.pyris.dto.chat.textexercise.PyrisTextExerciseChatPipelineExecutionDTO; import de.tum.cit.aet.artemis.iris.service.pyris.dto.chat.textexercise.PyrisTextExerciseChatStatusUpdateDTO; @@ -50,16 +49,13 @@ public class IrisTextExerciseChatSessionService implements IrisChatBasedFeatureI private final PyrisPipelineService pyrisPipelineService; - private final PyrisJobService pyrisJobService; - private final IrisChatWebsocketService irisChatWebsocketService; private final AuthorizationCheckService authCheckService; public IrisTextExerciseChatSessionService(IrisSettingsService irisSettingsService, IrisSessionRepository irisSessionRepository, IrisRateLimitService rateLimitService, IrisMessageService irisMessageService, TextExerciseRepository textExerciseRepository, StudentParticipationRepository studentParticipationRepository, - PyrisPipelineService pyrisPipelineService, PyrisJobService pyrisJobService, IrisChatWebsocketService irisChatWebsocketService, - AuthorizationCheckService authCheckService) { + PyrisPipelineService pyrisPipelineService, IrisChatWebsocketService irisChatWebsocketService, AuthorizationCheckService authCheckService) { this.irisSettingsService = irisSettingsService; this.irisSessionRepository = irisSessionRepository; this.rateLimitService = rateLimitService; @@ -67,7 +63,6 @@ public IrisTextExerciseChatSessionService(IrisSettingsService irisSettingsServic this.textExerciseRepository = textExerciseRepository; this.studentParticipationRepository = studentParticipationRepository; this.pyrisPipelineService = pyrisPipelineService; - this.pyrisJobService = pyrisJobService; this.irisChatWebsocketService = irisChatWebsocketService; this.authCheckService = authCheckService; } @@ -88,6 +83,7 @@ public void requestAndHandleResponse(IrisTextExerciseChatSession irisSession) { throw new ConflictException("Iris is not enabled for this exercise", "Iris", "irisDisabled"); } var course = exercise.getCourseViaExerciseGroupOrCourseMember(); + var exerciseDTO = PyrisTextExerciseDTO.of(exercise); // TODO: Once we can receive client form data through the IrisMessageResource, we should use that instead of fetching the latest submission to get the text var participation = studentParticipationRepository.findWithEagerLegalSubmissionsByExerciseIdAndStudentLogin(exercise.getId(), session.getUser().getLogin()); var latestSubmission = participation.flatMap(p -> p.getSubmissions().stream().max(Comparator.comparingLong(Submission::getId))).orElse(null); @@ -103,8 +99,13 @@ public void requestAndHandleResponse(IrisTextExerciseChatSession irisSession) { pyrisPipelineService.executePipeline( "text-exercise-chat", "default", - pyrisJobService.createTokenForJob(token -> new TextExerciseChatJob(token, course.getId(), exercise.getId(), session.getId())), - dto -> new PyrisTextExerciseChatPipelineExecutionDTO(dto, PyrisTextExerciseDTO.of(exercise), conversation, latestSubmissionText), + token -> new TextExerciseChatJob(token, course.getId(), exercise.getId(), session.getId()), + dto -> new PyrisTextExerciseChatPipelineExecutionDTO( + dto, + exerciseDTO, + conversation, + latestSubmissionText + ), stages -> irisChatWebsocketService.sendMessage(session, null, stages) ); // @formatter:on diff --git a/src/test/java/de/tum/cit/aet/artemis/iris/PyrisLectureIngestionTest.java b/src/test/java/de/tum/cit/aet/artemis/iris/PyrisLectureIngestionTest.java index e7cdc125faa8..12128ba116f6 100644 --- a/src/test/java/de/tum/cit/aet/artemis/iris/PyrisLectureIngestionTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/iris/PyrisLectureIngestionTest.java @@ -26,6 +26,7 @@ import de.tum.cit.aet.artemis.iris.service.pyris.dto.lectureingestionwebhook.PyrisLectureIngestionStatusUpdateDTO; import de.tum.cit.aet.artemis.iris.service.pyris.dto.status.PyrisStageDTO; import de.tum.cit.aet.artemis.iris.service.pyris.dto.status.PyrisStageState; +import de.tum.cit.aet.artemis.iris.service.pyris.job.CourseChatJob; 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.repository.LectureRepository; @@ -287,7 +288,7 @@ void testRunIdIngestionJob() throws Exception { assertThat(dto.settings().authenticationToken()).isNotNull(); }); String newJobToken = pyrisJobService.addIngestionWebhookJob(); - String chatJobToken = pyrisJobService.addCourseChatJob(123L, 123L); + String chatJobToken = pyrisJobService.registerJob(token -> new CourseChatJob(token, 123L, 123L)); PyrisStageDTO errorStage = new PyrisStageDTO("error", 1, PyrisStageState.ERROR, "Stage not broke due to error."); PyrisLectureIngestionStatusUpdateDTO statusUpdate = new PyrisLectureIngestionStatusUpdateDTO("Success", List.of(errorStage)); var headers = new HttpHeaders(new LinkedMultiValueMap<>(Map.of("Authorization", List.of("Bearer " + chatJobToken)))); From a45f2a55931da0d2dfe439664a6c0c8d020d29b6 Mon Sep 17 00:00:00 2001 From: Michael Dyer Date: Wed, 16 Oct 2024 11:07:25 +0200 Subject: [PATCH 2/3] Refactor --- .../artemis/atlas/dto/CompetencyJolDTO.java | 27 +++++++++++++---- .../atlas/dto/CompetencyJolPairDTO.java | 4 +-- .../competency/CompetencyJolService.java | 2 +- .../dto/chat/PyrisChatStatusUpdateDTO.java | 11 +++++-- .../PyrisCourseChatPipelineExecutionDTO.java | 6 ++-- ...PyrisExerciseChatPipelineExecutionDTO.java | 6 ++-- ...sTextExerciseChatPipelineExecutionDTO.java | 2 +- .../PyrisCompetencyExtractionInputDTO.java | 8 +++-- ...petencyExtractionPipelineExecutionDTO.java | 2 +- .../PyrisCompetencyRecommendationDTO.java | 9 ++++-- .../PyrisCompetencyStatusUpdateDTO.java | 8 +++-- .../pyris/dto/data/PyrisCompetencyDTO.java | 19 +++++++++--- .../pyris/dto/data/PyrisCourseDTO.java | 2 +- .../service/pyris/dto/data/PyrisExamDTO.java | 30 +++++++++++++++---- ...yrisExerciseWithStudentSubmissionsDTO.java | 26 ++++++++-------- .../dto/data/PyrisExtendedCourseDTO.java | 9 +++--- .../pyris/dto/data/PyrisLectureDTO.java | 2 +- .../pyris/dto/data/PyrisLectureUnitDTO.java | 5 +++- .../pyris/dto/data/PyrisMessageDTO.java | 2 +- .../dto/data/PyrisProgrammingExerciseDTO.java | 2 +- .../pyris/dto/data/PyrisResultDTO.java | 9 +++++- .../pyris/dto/data/PyrisSubmissionDTO.java | 2 +- .../pyris/dto/data/PyrisTextExerciseDTO.java | 17 ++++++++--- .../session/IrisCourseChatSessionService.java | 4 +-- .../IrisExerciseChatSessionService.java | 4 +-- .../IrisTextExerciseChatSessionService.java | 4 +-- .../CompetencyJolIntegrationTest.java | 2 +- 27 files changed, 154 insertions(+), 70 deletions(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/dto/CompetencyJolDTO.java b/src/main/java/de/tum/cit/aet/artemis/atlas/dto/CompetencyJolDTO.java index aba792f2025c..608d83694b89 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/dto/CompetencyJolDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/dto/CompetencyJolDTO.java @@ -9,13 +9,30 @@ import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyJol; /** - * A DTO for the CompetencyJol entity. + * Pyris DTO mapping for a {@code CompetencyJol}. */ +// @formatter:off @JsonInclude(JsonInclude.Include.NON_EMPTY) -public record CompetencyJolDTO(long id, long competencyId, short jolValue, ZonedDateTime judgementTime, double competencyProgress, double competencyConfidence) { +public record CompetencyJolDTO( + long id, + long competencyId, + short jolValue, + ZonedDateTime judgementTime, + double competencyProgress, + double competencyConfidence +// @formatter:on +) { - public static CompetencyJolDTO of(@NotNull CompetencyJol competencyJol) { - return new CompetencyJolDTO(competencyJol.getId(), competencyJol.getCompetency().getId(), competencyJol.getValue(), competencyJol.getJudgementTime(), - competencyJol.getCompetencyProgress(), competencyJol.getCompetencyConfidence()); + public static CompetencyJolDTO from(@NotNull CompetencyJol competencyJol) { + // @formatter:off + return new CompetencyJolDTO( + competencyJol.getId(), + competencyJol.getCompetency().getId(), + competencyJol.getValue(), + competencyJol.getJudgementTime(), + competencyJol.getCompetencyProgress(), + competencyJol.getCompetencyConfidence() + ); + // @formatter:on } } diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/dto/CompetencyJolPairDTO.java b/src/main/java/de/tum/cit/aet/artemis/atlas/dto/CompetencyJolPairDTO.java index 1f8a195498cc..741ec7314e57 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/dto/CompetencyJolPairDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/dto/CompetencyJolPairDTO.java @@ -14,7 +14,7 @@ @JsonInclude(JsonInclude.Include.NON_EMPTY) public record CompetencyJolPairDTO(CompetencyJolDTO current, CompetencyJolDTO prior) { - public static CompetencyJolPairDTO of(CompetencyJol current, CompetencyJol prior) { - return new CompetencyJolPairDTO(current != null ? CompetencyJolDTO.of(current) : null, prior != null ? CompetencyJolDTO.of(prior) : null); + public static CompetencyJolPairDTO from(CompetencyJol current, CompetencyJol prior) { + return new CompetencyJolPairDTO(current != null ? CompetencyJolDTO.from(current) : null, prior != null ? CompetencyJolDTO.from(prior) : null); } } diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CompetencyJolService.java b/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CompetencyJolService.java index 8deffa786626..12fa0038100f 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CompetencyJolService.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/service/competency/CompetencyJolService.java @@ -133,7 +133,7 @@ public CompetencyJolPairDTO getLatestJudgementOfLearningPairForUserByCompetencyI return new CompetencyJolPairDTO(null, null); } final var priorJol = competencyJolRepository.findLatestByCompetencyIdAndUserIdExceptJolId(competencyId, userId, currentJol.get().getId()); - return CompetencyJolPairDTO.of(currentJol.get(), priorJol.orElse(null)); + return CompetencyJolPairDTO.from(currentJol.get(), priorJol.orElse(null)); } /** diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/chat/PyrisChatStatusUpdateDTO.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/chat/PyrisChatStatusUpdateDTO.java index c40022a86426..83e6188c0020 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/chat/PyrisChatStatusUpdateDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/chat/PyrisChatStatusUpdateDTO.java @@ -9,10 +9,15 @@ /** * A DTO to send chat-based Pyris pipeline status updates to the client. * - * @param result Iris' textual response, if any. TODO: Wrap in Optional for API clarity + * @param result Iris' textual response, if any. * @param stages The pipeline execution progress described in a list of stages * @param suggestions A list of suggested next inputs for the user to click on */ +// @formatter:off @JsonInclude(JsonInclude.Include.NON_EMPTY) -public record PyrisChatStatusUpdateDTO(String result, List stages, List suggestions) { -} +public record PyrisChatStatusUpdateDTO( + String result, // TODO: Wrap in Optional for API clarity + List stages, // TODO: Wrap in Optional for API clarity + List suggestions +) {} +// @formatter:on diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/chat/course/PyrisCourseChatPipelineExecutionDTO.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/chat/course/PyrisCourseChatPipelineExecutionDTO.java index d92182f2d62c..6b6b008a829b 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/chat/course/PyrisCourseChatPipelineExecutionDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/chat/course/PyrisCourseChatPipelineExecutionDTO.java @@ -17,19 +17,19 @@ * * @param course the course being chatted about * @param metrics some metrics about the user - * @param competencyJol the competency judgement-of-learning, if any. TODO: Could wrap in Optional for API clarity + * @param competencyJol the competency judgement-of-learning, if any. * @param chatHistory the conversation history so far (including the user's latest message) * @param user the user * @param settings execution settings * @param initialStages initial stages from pipeline preparation */ // TODO: Instead of including settings and initialStages, include PyrisPipelineExecutionDTO -@JsonInclude(JsonInclude.Include.NON_EMPTY) // @formatter:off +@JsonInclude(JsonInclude.Include.NON_EMPTY) public record PyrisCourseChatPipelineExecutionDTO( PyrisExtendedCourseDTO course, StudentMetricsDTO metrics, - CompetencyJolDTO competencyJol, + CompetencyJolDTO competencyJol, // TODO: Could wrap in Optional for API clarity List chatHistory, PyrisUserDTO user, PyrisPipelineExecutionSettingsDTO settings, diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/chat/exercise/PyrisExerciseChatPipelineExecutionDTO.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/chat/exercise/PyrisExerciseChatPipelineExecutionDTO.java index 3eb8e198971a..09dea35cf801 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/chat/exercise/PyrisExerciseChatPipelineExecutionDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/chat/exercise/PyrisExerciseChatPipelineExecutionDTO.java @@ -15,7 +15,7 @@ /** * DTO for executing the programming exercise chat pipeline on Pyris. * - * @param submission the user's latest programming submission, if any. TODO: could wrap in Optional for API clarity + * @param submission the user's latest programming submission, if any. * @param exercise the programming exercise being chatted about * @param course the course * @param chatHistory the conversation history so far (including the user's latest message) @@ -24,10 +24,10 @@ * @param initialStages the initial stages from pipeline preparation */ // TODO: Instead of including settings and initialStages, just include PyrisPipelineExecutionDTO (requires a change on Pyris) -@JsonInclude(JsonInclude.Include.NON_EMPTY) // @formatter:off +@JsonInclude(JsonInclude.Include.NON_EMPTY) public record PyrisExerciseChatPipelineExecutionDTO( - PyrisSubmissionDTO submission, + PyrisSubmissionDTO submission, // TODO: could wrap in Optional for API clarity PyrisProgrammingExerciseDTO exercise, PyrisCourseDTO course, List chatHistory, diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/chat/textexercise/PyrisTextExerciseChatPipelineExecutionDTO.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/chat/textexercise/PyrisTextExerciseChatPipelineExecutionDTO.java index 41adc60b0ee8..b6b3992ebafb 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/chat/textexercise/PyrisTextExerciseChatPipelineExecutionDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/chat/textexercise/PyrisTextExerciseChatPipelineExecutionDTO.java @@ -16,8 +16,8 @@ * @param conversation the chat history * @param currentSubmission the user's current submission to the exercise */ -@JsonInclude(JsonInclude.Include.NON_EMPTY) // @formatter:off +@JsonInclude(JsonInclude.Include.NON_EMPTY) public record PyrisTextExerciseChatPipelineExecutionDTO( PyrisPipelineExecutionDTO execution, PyrisTextExerciseDTO exercise, diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/competency/PyrisCompetencyExtractionInputDTO.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/competency/PyrisCompetencyExtractionInputDTO.java index 4156244088b3..60f142430257 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/competency/PyrisCompetencyExtractionInputDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/competency/PyrisCompetencyExtractionInputDTO.java @@ -8,6 +8,10 @@ * @param courseDescription the course description (might have been edited in the client) * @param currentCompetencies the current competencies (might be unsaved in the client) */ +// @formatter:off @JsonInclude(JsonInclude.Include.NON_EMPTY) -public record PyrisCompetencyExtractionInputDTO(String courseDescription, PyrisCompetencyRecommendationDTO[] currentCompetencies) { -} +public record PyrisCompetencyExtractionInputDTO( + String courseDescription, + PyrisCompetencyRecommendationDTO[] currentCompetencies +) {} +// @formatter:on diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/competency/PyrisCompetencyExtractionPipelineExecutionDTO.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/competency/PyrisCompetencyExtractionPipelineExecutionDTO.java index 292eb75e59d0..b32d3986c45f 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/competency/PyrisCompetencyExtractionPipelineExecutionDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/competency/PyrisCompetencyExtractionPipelineExecutionDTO.java @@ -14,8 +14,8 @@ * @param taxonomyOptions The taxonomy options to use * @param maxN The maximum number of competencies to extract */ -@JsonInclude(JsonInclude.Include.NON_EMPTY) // @formatter:off +@JsonInclude(JsonInclude.Include.NON_EMPTY) public record PyrisCompetencyExtractionPipelineExecutionDTO( PyrisPipelineExecutionDTO execution, String courseDescription, diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/competency/PyrisCompetencyRecommendationDTO.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/competency/PyrisCompetencyRecommendationDTO.java index fae4365feed5..e56c4b87ef20 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/competency/PyrisCompetencyRecommendationDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/competency/PyrisCompetencyRecommendationDTO.java @@ -12,6 +12,11 @@ * @param description The description of the competency * @param taxonomy The taxonomy of the competency */ +// @formatter:off @JsonInclude(JsonInclude.Include.NON_EMPTY) -public record PyrisCompetencyRecommendationDTO(String title, String description, CompetencyTaxonomy taxonomy) { -} +public record PyrisCompetencyRecommendationDTO( + String title, + String description, + CompetencyTaxonomy taxonomy +) {} +// @formatter:on diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/competency/PyrisCompetencyStatusUpdateDTO.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/competency/PyrisCompetencyStatusUpdateDTO.java index 0956a52f26e8..2bb1cc177c9b 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/competency/PyrisCompetencyStatusUpdateDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/competency/PyrisCompetencyStatusUpdateDTO.java @@ -14,6 +14,10 @@ * @param stages List of stages of the generation process * @param result List of competencies recommendations that have been generated so far */ +// @formatter:off @JsonInclude(JsonInclude.Include.NON_EMPTY) -public record PyrisCompetencyStatusUpdateDTO(List stages, List result) { -} +public record PyrisCompetencyStatusUpdateDTO( + List stages, + List result +) {} +// @formatter:on diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisCompetencyDTO.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisCompetencyDTO.java index 4d1e48b97e49..c0d0518b6a42 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisCompetencyDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisCompetencyDTO.java @@ -9,8 +9,11 @@ import de.tum.cit.aet.artemis.atlas.domain.competency.Competency; import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyTaxonomy; -@JsonInclude(JsonInclude.Include.NON_EMPTY) +/** + * Pyris DTO mapping for a {@code Competency}. + */ // @formatter:off +@JsonInclude(JsonInclude.Include.NON_EMPTY) public record PyrisCompetencyDTO( long id, String title, @@ -21,8 +24,16 @@ public record PyrisCompetencyDTO( // @formatter:on ) { - public static PyrisCompetencyDTO of(Competency competency) { - return new PyrisCompetencyDTO(competency.getId(), competency.getTitle(), competency.getDescription(), competency.getTaxonomy(), toInstant(competency.getSoftDueDate()), - competency.isOptional()); + public static PyrisCompetencyDTO from(Competency competency) { + // @formatter:off + return new PyrisCompetencyDTO( + competency.getId(), + competency.getTitle(), + competency.getDescription(), + competency.getTaxonomy(), + toInstant(competency.getSoftDueDate()), + competency.isOptional() + ); + // @formatter:on } } diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisCourseDTO.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisCourseDTO.java index 6b59253640d2..e554a4ebaa1b 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisCourseDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisCourseDTO.java @@ -7,7 +7,7 @@ @JsonInclude(JsonInclude.Include.NON_EMPTY) public record PyrisCourseDTO(long id, String name, String description) { - public static PyrisCourseDTO of(Course course) { + public static PyrisCourseDTO from(Course course) { return new PyrisCourseDTO(course.getId(), course.getTitle(), course.getDescription()); } } diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisExamDTO.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisExamDTO.java index 4dbc4de37f0a..e54d4744bcc1 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisExamDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisExamDTO.java @@ -8,12 +8,32 @@ import de.tum.cit.aet.artemis.exam.domain.Exam; +// @formatter:off @JsonInclude(JsonInclude.Include.NON_EMPTY) -public record PyrisExamDTO(long id, String title, boolean isTextExam, Instant startDate, Instant endDate, Instant publishResultsDate, Instant examStudentReviewStart, - Instant examStudentReviewEnd) { +public record PyrisExamDTO( + long id, + String title, + boolean isTextExam, + Instant startDate, + Instant endDate, + Instant publishResultsDate, + Instant examStudentReviewStart, + Instant examStudentReviewEnd +// @formatter:on +) { - public static PyrisExamDTO of(Exam exam) { - return new PyrisExamDTO(exam.getId(), exam.getTitle(), exam.isTestExam(), toInstant(exam.getStartDate()), toInstant(exam.getEndDate()), - toInstant(exam.getPublishResultsDate()), toInstant(exam.getExamStudentReviewStart()), toInstant(exam.getExamStudentReviewEnd())); + public static PyrisExamDTO from(Exam exam) { + // @formatter:off + return new PyrisExamDTO( + exam.getId(), + exam.getTitle(), + exam.isTestExam(), + toInstant(exam.getStartDate()), + toInstant(exam.getEndDate()), + toInstant(exam.getPublishResultsDate()), + toInstant(exam.getExamStudentReviewStart()), + toInstant(exam.getExamStudentReviewEnd()) + ); + // @formatter:on } } diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisExerciseWithStudentSubmissionsDTO.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisExerciseWithStudentSubmissionsDTO.java index 200a081fa563..7456c446351b 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisExerciseWithStudentSubmissionsDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisExerciseWithStudentSubmissionsDTO.java @@ -16,8 +16,11 @@ import de.tum.cit.aet.artemis.exercise.domain.ExerciseType; import de.tum.cit.aet.artemis.exercise.domain.IncludedInOverallScore; -@JsonInclude(JsonInclude.Include.NON_EMPTY) +/** + * Pyris DTO mapping for an {@code Exercise} together with all its student submissions. + */ // @formatter:off +@JsonInclude(JsonInclude.Include.NON_EMPTY) public record PyrisExerciseWithStudentSubmissionsDTO( long id, String title, @@ -40,18 +43,8 @@ public record PyrisExerciseWithStudentSubmissionsDTO( * @param exercise The exercise to convert. * @return The converted exercise. */ - public static PyrisExerciseWithStudentSubmissionsDTO of(Exercise exercise) { + public static PyrisExerciseWithStudentSubmissionsDTO from(Exercise exercise) { // @formatter:off - var submissionDTOSet = exercise.getStudentParticipations() - .stream() - .filter(participation -> !participation.isTestRun()) - .flatMap(participation -> participation.getSubmissions().stream()) - .map(submission -> new PyrisStudentSubmissionDTO( - toInstant(submission.getSubmissionDate()), - Optional.ofNullable(submission.getLatestResult()).map(Result::getScore).orElse(0D) - )) - .collect(Collectors.toSet()); - return new PyrisExerciseWithStudentSubmissionsDTO( exercise.getId(), exercise.getTitle(), @@ -64,8 +57,15 @@ public static PyrisExerciseWithStudentSubmissionsDTO of(Exercise exercise) { toInstant(exercise.getDueDate()), exercise.getIncludedInOverallScore(), Boolean.TRUE.equals(exercise.getPresentationScoreEnabled()), - submissionDTOSet + createSubmissionDTOSet(exercise) ); // @formatter:on } + + private static Set createSubmissionDTOSet(Exercise exercise) { + return exercise.getStudentParticipations().stream().filter(participation -> !participation.isTestRun()).flatMap(participation -> participation.getSubmissions().stream()) + .map(submission -> new PyrisStudentSubmissionDTO(toInstant(submission.getSubmissionDate()), + Optional.ofNullable(submission.getLatestResult()).map(Result::getScore).orElse(0D))) + .collect(Collectors.toSet()); + } } diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisExtendedCourseDTO.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisExtendedCourseDTO.java index c1f6ac4c0e19..000160d0ce66 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisExtendedCourseDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisExtendedCourseDTO.java @@ -14,8 +14,8 @@ * An extended course DTO for Pyris so it can better answer * questions regarding the course organization and content. */ -@JsonInclude(JsonInclude.Include.NON_EMPTY) // @formatter:off +@JsonInclude(JsonInclude.Include.NON_EMPTY) public record PyrisExtendedCourseDTO( long id, String name, @@ -42,10 +42,9 @@ public record PyrisExtendedCourseDTO( * @return The converted course. */ public static PyrisExtendedCourseDTO of(Course course) { - List exercises = course.getExercises().stream().map(PyrisExerciseWithStudentSubmissionsDTO::of).toList(); - - List exams = course.getExams().stream().map(PyrisExamDTO::of).toList(); - List competencies = course.getCompetencies().stream().map(PyrisCompetencyDTO::of).toList(); + List exercises = course.getExercises().stream().map(PyrisExerciseWithStudentSubmissionsDTO::from).toList(); + List exams = course.getExams().stream().map(PyrisExamDTO::from).toList(); + List competencies = course.getCompetencies().stream().map(PyrisCompetencyDTO::from).toList(); // @formatter:off return new PyrisExtendedCourseDTO( diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisLectureDTO.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisLectureDTO.java index d9d352a64747..759aa35a4f53 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisLectureDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisLectureDTO.java @@ -5,8 +5,8 @@ import com.fasterxml.jackson.annotation.JsonInclude; -@JsonInclude(JsonInclude.Include.NON_EMPTY) // @formatter:off +@JsonInclude(JsonInclude.Include.NON_EMPTY) public record PyrisLectureDTO( long id, String title, diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisLectureUnitDTO.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisLectureUnitDTO.java index f77b0ff777ee..e3a866932cc8 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisLectureUnitDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisLectureUnitDTO.java @@ -4,8 +4,11 @@ import com.fasterxml.jackson.annotation.JsonInclude; -@JsonInclude(JsonInclude.Include.NON_EMPTY) +/** + * Pyris DTO mapping for a {@code LectureUnit}. + */ // @formatter:off +@JsonInclude(JsonInclude.Include.NON_EMPTY) public record PyrisLectureUnitDTO( long id, long lectureId, diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisMessageDTO.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisMessageDTO.java index 4b7b9f785f4e..998fcfa70462 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisMessageDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisMessageDTO.java @@ -22,7 +22,7 @@ public record PyrisMessageDTO(Instant sentAt, IrisMessageSender sender, List { PyrisMessageContentBaseDTO result = null; if (messageContent.getClass().equals(IrisTextMessageContent.class)) { diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisProgrammingExerciseDTO.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisProgrammingExerciseDTO.java index 286e513c71be..1ab14a48dcf6 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisProgrammingExerciseDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisProgrammingExerciseDTO.java @@ -7,8 +7,8 @@ import de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage; -@JsonInclude(JsonInclude.Include.NON_EMPTY) // @formatter:off +@JsonInclude(JsonInclude.Include.NON_EMPTY) public record PyrisProgrammingExerciseDTO( long id, String name, diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisResultDTO.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisResultDTO.java index 3cd329125491..9cbe424c5120 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisResultDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisResultDTO.java @@ -5,8 +5,15 @@ import com.fasterxml.jackson.annotation.JsonInclude; -@JsonInclude(JsonInclude.Include.NON_EMPTY) +/** + * Pyris DTO mapping of a {@code ProgrammingSubmission} + * + * @param completionDate date of submission + * @param successful whether the submission was successful + * @param feedbacks the feedback on the submission + */ // @formatter:off +@JsonInclude(JsonInclude.Include.NON_EMPTY) public record PyrisResultDTO( Instant completionDate, boolean successful, diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisSubmissionDTO.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisSubmissionDTO.java index 1b98229b260e..e267b77a903b 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisSubmissionDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisSubmissionDTO.java @@ -6,8 +6,8 @@ import com.fasterxml.jackson.annotation.JsonInclude; -@JsonInclude(JsonInclude.Include.NON_EMPTY) // @formatter:off +@JsonInclude(JsonInclude.Include.NON_EMPTY) public record PyrisSubmissionDTO( long id, Instant date, diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisTextExerciseDTO.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisTextExerciseDTO.java index 621de55d7e1a..ab64e2cde9e5 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisTextExerciseDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisTextExerciseDTO.java @@ -8,8 +8,17 @@ import de.tum.cit.aet.artemis.text.domain.TextExercise; +// @formatter:off @JsonInclude(JsonInclude.Include.NON_EMPTY) -public record PyrisTextExerciseDTO(long id, String title, PyrisCourseDTO course, String problemStatement, Instant startDate, Instant endDate) { +public record PyrisTextExerciseDTO( + long id, + String title, + PyrisCourseDTO course, + String problemStatement, + Instant startDate, + Instant endDate +// @formatter:on +) { /** * Create a new PyrisTextExerciseDTO from the given TextExercise @@ -17,12 +26,12 @@ public record PyrisTextExerciseDTO(long id, String title, PyrisCourseDTO course, * @param exercise the exercise * @return the dto */ - public static PyrisTextExerciseDTO of(TextExercise exercise) { + public static PyrisTextExerciseDTO from(TextExercise exercise) { // @formatter:off - return new PyrisTextExerciseDTO( + return new PyrisTextExerciseDTO( exercise.getId(), exercise.getTitle(), - PyrisCourseDTO.of(exercise.getCourseViaExerciseGroupOrCourseMember()), + PyrisCourseDTO.from(exercise.getCourseViaExerciseGroupOrCourseMember()), exercise.getProblemStatement(), Optional.ofNullable(exercise.getStartDate()).map(ChronoZonedDateTime::toInstant).orElse(null), Optional.ofNullable(exercise.getDueDate()).map(ChronoZonedDateTime::toInstant).orElse(null) diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/session/IrisCourseChatSessionService.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/session/IrisCourseChatSessionService.java index b9ac15d2cb73..af51b80fd506 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/session/IrisCourseChatSessionService.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/session/IrisCourseChatSessionService.java @@ -153,8 +153,8 @@ private void requestAndHandleResponse(IrisCourseChatSession _session, String var var courseDTO = PyrisExtendedCourseDTO.of(loadCourseWithParticipationOfStudent(courseId, userId)); var metricsDTO = learningMetricsService.getStudentCourseMetrics(userId, courseId); - var competencyJolDTO = Optional.ofNullable(competencyJol).map(CompetencyJolDTO::of).orElse(null); - var conversation = session.getMessages().stream().map(PyrisMessageDTO::of).toList(); + var competencyJolDTO = Optional.ofNullable(competencyJol).map(CompetencyJolDTO::from).orElse(null); + var conversation = session.getMessages().stream().map(PyrisMessageDTO::from).toList(); var userDTO = PyrisUserDTO.of(session.getUser()); // @formatter:off diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/session/IrisExerciseChatSessionService.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/session/IrisExerciseChatSessionService.java index f2b1f5b2159c..3c296dccc944 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/session/IrisExerciseChatSessionService.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/session/IrisExerciseChatSessionService.java @@ -156,8 +156,8 @@ public void requestAndHandleResponse(IrisExerciseChatSession _irisSession) { var latestSubmissionDTO = getLatestSubmissionIfExists(exercise, session.getUser()).map(pyrisProgrammingExerciseDTOService::convert).orElse(null); var exerciseDTO = pyrisProgrammingExerciseDTOService.convert(exercise); var course = exercise.getCourseViaExerciseGroupOrCourseMember(); - var courseDTO = PyrisCourseDTO.of(course); - var conversationDTO = session.getMessages().stream().map(PyrisMessageDTO::of).toList(); + var courseDTO = PyrisCourseDTO.from(course); + var conversationDTO = session.getMessages().stream().map(PyrisMessageDTO::from).toList(); var userDTO = PyrisUserDTO.of(session.getUser()); // @formatter:off diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/session/IrisTextExerciseChatSessionService.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/session/IrisTextExerciseChatSessionService.java index 40b7629daa27..0c24593059b4 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/session/IrisTextExerciseChatSessionService.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/session/IrisTextExerciseChatSessionService.java @@ -83,7 +83,7 @@ public void requestAndHandleResponse(IrisTextExerciseChatSession irisSession) { throw new ConflictException("Iris is not enabled for this exercise", "Iris", "irisDisabled"); } var course = exercise.getCourseViaExerciseGroupOrCourseMember(); - var exerciseDTO = PyrisTextExerciseDTO.of(exercise); + var exerciseDTO = PyrisTextExerciseDTO.from(exercise); // TODO: Once we can receive client form data through the IrisMessageResource, we should use that instead of fetching the latest submission to get the text var participation = studentParticipationRepository.findWithEagerLegalSubmissionsByExerciseIdAndStudentLogin(exercise.getId(), session.getUser().getLogin()); var latestSubmission = participation.flatMap(p -> p.getSubmissions().stream().max(Comparator.comparingLong(Submission::getId))).orElse(null); @@ -94,7 +94,7 @@ public void requestAndHandleResponse(IrisTextExerciseChatSession irisSession) { else { latestSubmissionText = null; } - var conversation = session.getMessages().stream().map(PyrisMessageDTO::of).toList(); + var conversation = session.getMessages().stream().map(PyrisMessageDTO::from).toList(); // @formatter:off pyrisPipelineService.executePipeline( "text-exercise-chat", diff --git a/src/test/java/de/tum/cit/aet/artemis/atlas/competency/CompetencyJolIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/atlas/competency/CompetencyJolIntegrationTest.java index 214b743f5367..6b64e7ea51a2 100644 --- a/src/test/java/de/tum/cit/aet/artemis/atlas/competency/CompetencyJolIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/atlas/competency/CompetencyJolIntegrationTest.java @@ -181,7 +181,7 @@ void shouldReturnValues() throws Exception { final var jol1 = competencyUtilService.createJol(competency[1], student, (short) 8, ZonedDateTime.now(), 0.1, 0.2); final var jolMap = sendRequest(HttpStatus.OK); assertThat(jolMap).isNotNull(); - final var expectedMap = Map.of(competency[0].getId(), CompetencyJolPairDTO.of(jol01, jol00), competency[1].getId(), CompetencyJolPairDTO.of(jol1, null)); + final var expectedMap = Map.of(competency[0].getId(), CompetencyJolPairDTO.from(jol01, jol00), competency[1].getId(), CompetencyJolPairDTO.from(jol1, null)); expectedMap.forEach((expKey, expValue) -> { final var current = jolMap.get(expKey).current(); final var expCurrent = expValue.current(); From b66f06271f0f252a668eb0dee9bbf9050f953582 Mon Sep 17 00:00:00 2001 From: Michael Dyer Date: Wed, 16 Oct 2024 14:24:01 +0200 Subject: [PATCH 3/3] Refactor --- .../aet/artemis/iris/service/pyris/PyrisJobService.java | 5 +++-- .../artemis/iris/service/pyris/PyrisPipelineService.java | 2 +- .../service/pyris/dto/data/PyrisExtendedCourseDTO.java | 4 ++-- .../iris/service/pyris/dto/data/PyrisSubmissionDTO.java | 3 +++ .../artemis/iris/service/pyris/dto/data/PyrisUserDTO.java | 2 +- .../service/session/IrisCourseChatSessionService.java | 8 ++++---- .../service/session/IrisExerciseChatSessionService.java | 8 ++++---- 7 files changed, 18 insertions(+), 14 deletions(-) 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 ae83ff611465..9f5e504f1e3d 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 @@ -61,8 +61,9 @@ public void init() { } /** - * Creates a token for an arbitrary job, runs the provided function with the token as an argument, - * and stores the job in the job map. + * Creates a new job token, runs the provided function with the token as an argument, + * and registers the resulting job in Hazelcast. + * The job token is then returned for later reference. * * @param tokenToJobFunction the function to run with the token * @return the generated 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 c5c352d3dc5f..82178e3dfbda 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 @@ -52,7 +52,7 @@ public PyrisPipelineService(PyrisConnectorService pyrisConnectorService, PyrisJo * * @param name the name of the pipeline to be executed * @param variant the variant of the pipeline - * @param jobFunction a function from job ID to job. Creates a new {@code PyrisJob} which will be registered in Hazelcast + * @param jobFunction a function from job token to job. Creates a new {@code PyrisJob} which will be registered in Hazelcast * @param dtoMapper a function to create the concrete DTO type for this pipeline from the base execution DTO * @param statusUpdater a consumer of stages to send status updates while the pipeline is being prepared */ diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisExtendedCourseDTO.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisExtendedCourseDTO.java index 000160d0ce66..c4de9958cbb6 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisExtendedCourseDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisExtendedCourseDTO.java @@ -32,8 +32,8 @@ public record PyrisExtendedCourseDTO( List exercises, List exams, List competencies +// @formatter:on ) { - // @formatter:on /** * Convert a course to a PyrisExtendedCourseDTO. @@ -41,7 +41,7 @@ public record PyrisExtendedCourseDTO( * @param course The course to convert. * @return The converted course. */ - public static PyrisExtendedCourseDTO of(Course course) { + public static PyrisExtendedCourseDTO from(Course course) { List exercises = course.getExercises().stream().map(PyrisExerciseWithStudentSubmissionsDTO::from).toList(); List exams = course.getExams().stream().map(PyrisExamDTO::from).toList(); List competencies = course.getCompetencies().stream().map(PyrisCompetencyDTO::from).toList(); diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisSubmissionDTO.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisSubmissionDTO.java index e267b77a903b..8da233128c90 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisSubmissionDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisSubmissionDTO.java @@ -6,6 +6,9 @@ import com.fasterxml.jackson.annotation.JsonInclude; +/** + * Pyris DTO mapping for a {@code ProgrammingSubmission}. + */ // @formatter:off @JsonInclude(JsonInclude.Include.NON_EMPTY) public record PyrisSubmissionDTO( diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisUserDTO.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisUserDTO.java index 9a38ca50ffda..f28a49341e00 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisUserDTO.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/dto/data/PyrisUserDTO.java @@ -7,7 +7,7 @@ @JsonInclude(JsonInclude.Include.NON_EMPTY) public record PyrisUserDTO(long id, String firstName, String lastName) { - public static PyrisUserDTO of(User user) { + public static PyrisUserDTO from(User user) { return new PyrisUserDTO(user.getId(), user.getFirstName(), user.getLastName()); } } diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/session/IrisCourseChatSessionService.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/session/IrisCourseChatSessionService.java index af51b80fd506..a28c48a0a80e 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/session/IrisCourseChatSessionService.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/session/IrisCourseChatSessionService.java @@ -145,17 +145,17 @@ public void requestAndHandleResponse(IrisCourseChatSession session) { requestAndHandleResponse(session, variant, null); } - private void requestAndHandleResponse(IrisCourseChatSession _session, String variant, CompetencyJol competencyJol) { - var session = (IrisCourseChatSession) irisSessionRepository.findByIdWithMessagesAndContents(_session.getId()); + private void requestAndHandleResponse(IrisCourseChatSession irisSession, String variant, CompetencyJol competencyJol) { + var session = (IrisCourseChatSession) irisSessionRepository.findByIdWithMessagesAndContents(irisSession.getId()); var courseId = session.getCourse().getId(); var userId = session.getUser().getId(); - var courseDTO = PyrisExtendedCourseDTO.of(loadCourseWithParticipationOfStudent(courseId, userId)); + var courseDTO = PyrisExtendedCourseDTO.from(loadCourseWithParticipationOfStudent(courseId, userId)); var metricsDTO = learningMetricsService.getStudentCourseMetrics(userId, courseId); var competencyJolDTO = Optional.ofNullable(competencyJol).map(CompetencyJolDTO::from).orElse(null); var conversation = session.getMessages().stream().map(PyrisMessageDTO::from).toList(); - var userDTO = PyrisUserDTO.of(session.getUser()); + var userDTO = PyrisUserDTO.from(session.getUser()); // @formatter:off pyrisPipelineService.executePipeline( diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/session/IrisExerciseChatSessionService.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/session/IrisExerciseChatSessionService.java index 3c296dccc944..1aef20a0ab10 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/session/IrisExerciseChatSessionService.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/session/IrisExerciseChatSessionService.java @@ -142,11 +142,11 @@ public void checkRateLimit(User user) { * Sends all messages of the session to an LLM and handles the response by saving the message * and sending it to the student via the Websocket. * - * @param _irisSession The chat session to send to the LLM. The full session is loaded from the database by ID. + * @param irisSession The chat session to send to the LLM. The full session is loaded from the database by ID. */ @Override - public void requestAndHandleResponse(IrisExerciseChatSession _irisSession) { - var session = (IrisExerciseChatSession) irisSessionRepository.findByIdWithMessagesAndContents(_irisSession.getId()); + public void requestAndHandleResponse(IrisExerciseChatSession irisSession) { + var session = (IrisExerciseChatSession) irisSessionRepository.findByIdWithMessagesAndContents(irisSession.getId()); if (session.getExercise().isExamExercise()) { throw new ConflictException("Iris is not supported for exam exercises", "Iris", "irisExamExercise"); } @@ -158,7 +158,7 @@ public void requestAndHandleResponse(IrisExerciseChatSession _irisSession) { var course = exercise.getCourseViaExerciseGroupOrCourseMember(); var courseDTO = PyrisCourseDTO.from(course); var conversationDTO = session.getMessages().stream().map(PyrisMessageDTO::from).toList(); - var userDTO = PyrisUserDTO.of(session.getUser()); + var userDTO = PyrisUserDTO.from(session.getUser()); // @formatter:off pyrisPipelineService.executePipeline(