From fa797d8be6e269850af8908e91319dae832d0af4 Mon Sep 17 00:00:00 2001 From: Kaan Cayli <38523756+kaancayli@users.noreply.github.com> Date: Sun, 15 Dec 2024 11:19:18 +0300 Subject: [PATCH 01/10] fix: Log info if an event is not activated instead of throwing an exception --- .../IrisExerciseChatSessionService.java | 10 +++- .../service/settings/IrisSettingsService.java | 52 +++++++------------ .../iris/PyrisEventSystemIntegrationTest.java | 11 ++-- 3 files changed, 34 insertions(+), 39 deletions(-) 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 d422970401e0..60c72e952b10 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 @@ -188,7 +188,10 @@ public void onBuildFailure(Result result) { } var exercise = validateExercise(participation.getExercise()); - irisSettingsService.isActivatedForElseThrow(IrisEventType.BUILD_FAILED, exercise); + if (!irisSettingsService.isActivatedFor(IrisEventType.BUILD_FAILED, exercise)) { + log.info("Build failure event is not activated for exercise {}", exercise.getId()); + return; + } var participant = studentParticipation.getParticipant(); if (participant instanceof User user) { @@ -215,7 +218,10 @@ public void onNewResult(Result result) { var exercise = validateExercise(participation.getExercise()); - irisSettingsService.isActivatedForElseThrow(IrisEventType.PROGRESS_STALLED, exercise); + if (!irisSettingsService.isActivatedFor(IrisEventType.PROGRESS_STALLED, exercise)) { + log.info("Progress stalled event is not activated for exercise {}", exercise.getId()); + return; + } var recentSubmissions = submissionRepository.findAllWithResultsByParticipationIdOrderBySubmissionDateAsc(studentParticipation.getId()); diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/settings/IrisSettingsService.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/settings/IrisSettingsService.java index 8ed823adee2c..591e78caee3c 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/settings/IrisSettingsService.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/settings/IrisSettingsService.java @@ -15,6 +15,8 @@ import java.util.TreeSet; import java.util.function.Supplier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.annotation.Profile; import org.springframework.context.event.EventListener; @@ -60,6 +62,8 @@ @Profile(PROFILE_IRIS) public class IrisSettingsService { + private static final Logger log = LoggerFactory.getLogger(IrisSettingsService.class); + private final IrisSettingsRepository irisSettingsRepository; private final IrisSubSettingsService irisSubSettingsService; @@ -476,38 +480,6 @@ public void isEnabledForElseThrow(IrisSubSettingsType type, Course course) { } } - /** - * Checks whether an Iris event is enabled for a course. - * Throws an exception if the chat feature is disabled. - * Throws an exception if the event is disabled. - * - * @param type The Iris event to check - * @param course The course to check - */ - public void isActivatedForElseThrow(IrisEventType type, Course course) { - isEnabledForElseThrow(IrisSubSettingsType.CHAT, course); - - if (!isActivatedFor(type, course)) { - throw new AccessForbiddenAlertException("The Iris " + type.name() + " event is disabled for this course.", "Iris", "iris." + type.name().toLowerCase() + "Disabled"); - } - } - - /** - * Checks whether an Iris event is enabled for an exercise. - * Throws an exception if the chat feature is disabled. - * Throws an exception if the event is disabled. - * - * @param type The Iris event to check - * @param exercise The exercise to check - */ - public void isActivatedForElseThrow(IrisEventType type, Exercise exercise) { - isEnabledForElseThrow(IrisSubSettingsType.CHAT, exercise); - - if (!isActivatedFor(type, exercise)) { - throw new AccessForbiddenAlertException("The Iris " + type.name() + " event is disabled for this exercise.", "Iris", "iris." + type.name().toLowerCase() + "Disabled"); - } - } - /** * Checks whether an Iris feature is enabled for a course. * @@ -540,6 +512,10 @@ public boolean isEnabledFor(IrisSubSettingsType type, Exercise exercise) { * @return Whether the Iris event is active for the course */ public boolean isActivatedFor(IrisEventType type, Course course) { + if (!isEnabledFor(IrisSubSettingsType.CHAT, course)) { + log.debug("Chat is disabled for course {}", course.getId()); + return false; + } var settings = getCombinedIrisSettingsFor(course, false); return isEventEnabledInSettings(settings, type); } @@ -552,6 +528,10 @@ public boolean isActivatedFor(IrisEventType type, Course course) { * @return Whether the Iris event is active for the exercise */ public boolean isActivatedFor(IrisEventType type, Exercise exercise) { + if (!isEnabledFor(IrisSubSettingsType.CHAT, exercise)) { + log.debug("Chat is disabled for exercise {}", exercise.getId()); + return false; + } var settings = getCombinedIrisSettingsFor(exercise, false); return isEventEnabledInSettings(settings, type); } @@ -760,7 +740,9 @@ private boolean isEventEnabledInSettings(IrisCombinedSettingsDTO settings, IrisE return switch (type) { case PROGRESS_STALLED -> { if (settings.irisChatSettings().disabledProactiveEvents() != null) { - yield !settings.irisChatSettings().disabledProactiveEvents().contains(IrisEventType.PROGRESS_STALLED.name().toLowerCase()); + var isEventEnabled = !settings.irisChatSettings().disabledProactiveEvents().contains(IrisEventType.PROGRESS_STALLED.name().toLowerCase()); + log.debug("Event PROGRESS_STALLED enabled: {}", isEventEnabled); + yield isEventEnabled; } else { yield true; @@ -768,7 +750,9 @@ private boolean isEventEnabledInSettings(IrisCombinedSettingsDTO settings, IrisE } case BUILD_FAILED -> { if (settings.irisChatSettings().disabledProactiveEvents() != null) { - yield !settings.irisChatSettings().disabledProactiveEvents().contains(IrisEventType.BUILD_FAILED.name().toLowerCase()); + var isEventEnabled = !settings.irisChatSettings().disabledProactiveEvents().contains(IrisEventType.BUILD_FAILED.name().toLowerCase()); + log.debug("Event BUILD_FAILED enabled: {}", isEventEnabled); + yield isEventEnabled; } else { yield true; diff --git a/src/test/java/de/tum/cit/aet/artemis/iris/PyrisEventSystemIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/iris/PyrisEventSystemIntegrationTest.java index 62bc019b38a8..dccb9eff49e9 100644 --- a/src/test/java/de/tum/cit/aet/artemis/iris/PyrisEventSystemIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/iris/PyrisEventSystemIntegrationTest.java @@ -30,7 +30,6 @@ import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyJol; 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.AccessForbiddenAlertException; import de.tum.cit.aet.artemis.core.user.util.UserUtilService; import de.tum.cit.aet.artemis.exercise.domain.SubmissionType; import de.tum.cit.aet.artemis.exercise.participation.util.ParticipationFactory; @@ -264,7 +263,10 @@ void testShouldNotFireProgressStalledEventWithEventDisabled() { createSubmissionWithScore(studentParticipation, 40); createSubmissionWithScore(studentParticipation, 40); var result = createSubmissionWithScore(studentParticipation, 40); - assertThatExceptionOfType(AccessForbiddenAlertException.class).isThrownBy(() -> pyrisEventService.trigger(new NewResultEvent(result))); + pyrisEventService.trigger(new NewResultEvent(result)); + + verify(irisExerciseChatSessionService, times(1)).onNewResult(any(Result.class)); + verify(pyrisPipelineService, times(0)).executeExerciseChatPipeline(any(), any(), any(), any(), any()); } @Test @@ -278,7 +280,10 @@ void testShouldNotFireBuildFailedEventWhenEventSettingDisabled() { irisExerciseChatSessionService.createChatSessionForProgrammingExercise(exercise, userUtilService.getUserByLogin(TEST_PREFIX + "student1")); // Create a failing submission for the student. var result = createFailingSubmission(studentParticipation); - assertThatExceptionOfType(AccessForbiddenAlertException.class).isThrownBy(() -> pyrisEventService.trigger(new NewResultEvent(result))); + pyrisEventService.trigger(new NewResultEvent(result)); + + verify(irisExerciseChatSessionService, times(1)).onBuildFailure(any(Result.class)); + verify(pyrisPipelineService, times(0)).executeExerciseChatPipeline(any(), any(), any(), any(), any()); } @Test From c4021edfa07d20a8b01bf7a97ff34985e163634f Mon Sep 17 00:00:00 2001 From: Kaan Cayli <38523756+kaancayli@users.noreply.github.com> Date: Sun, 15 Dec 2024 18:16:45 +0300 Subject: [PATCH 02/10] chore: Refactor Pyris Event handling --- .../competency/CompetencyJolService.java | 14 +-- .../service/pyris/PyrisEventPublisher.java | 106 ++++++++++++++++++ .../iris/service/pyris/PyrisEventService.java | 54 ++++----- .../pyris/event/CompetencyJolSetEvent.java | 25 +++-- .../service/pyris/event/NewResultEvent.java | 40 ++++--- .../iris/service/pyris/event/PyrisEvent.java | 22 +++- .../session/IrisCourseChatSessionService.java | 3 - .../IrisExerciseChatSessionService.java | 10 -- .../service/settings/IrisSettingsService.java | 11 +- .../service/ProgrammingMessagingService.java | 18 +-- .../iris/PyrisEventSystemIntegrationTest.java | 42 +++---- 11 files changed, 227 insertions(+), 118 deletions(-) create mode 100644 src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisEventPublisher.java 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 7fa11b6f69b9..3885f233b224 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 @@ -23,7 +23,7 @@ import de.tum.cit.aet.artemis.atlas.repository.CompetencyRepository; import de.tum.cit.aet.artemis.core.exception.BadRequestAlertException; import de.tum.cit.aet.artemis.core.repository.UserRepository; -import de.tum.cit.aet.artemis.iris.service.pyris.PyrisEventService; +import de.tum.cit.aet.artemis.iris.service.pyris.PyrisEventPublisher; import de.tum.cit.aet.artemis.iris.service.pyris.event.CompetencyJolSetEvent; /** @@ -45,15 +45,15 @@ public class CompetencyJolService { private final UserRepository userRepository; - private final Optional pyrisEventService; + private final Optional pyrisEventPublisher; public CompetencyJolService(CompetencyJolRepository competencyJolRepository, CompetencyRepository competencyRepository, - CompetencyProgressRepository competencyProgressRepository, UserRepository userRepository, Optional pyrisEventService) { + CompetencyProgressRepository competencyProgressRepository, UserRepository userRepository, Optional pyrisEventPublisher) { this.competencyJolRepository = competencyJolRepository; this.competencyRepository = competencyRepository; this.competencyProgressRepository = competencyProgressRepository; this.userRepository = userRepository; - this.pyrisEventService = pyrisEventService; + this.pyrisEventPublisher = pyrisEventPublisher; } /** @@ -84,10 +84,10 @@ public void setJudgementOfLearning(long competencyId, long userId, short jolValu final var jol = createCompetencyJol(competencyId, userId, jolValue, ZonedDateTime.now(), competencyProgress); competencyJolRepository.save(jol); - pyrisEventService.ifPresent(service -> { - // Inform Iris so it can send a message to the user + // Inform Iris so it can send a message to the user + pyrisEventPublisher.ifPresent(service -> { try { - service.trigger(new CompetencyJolSetEvent(jol)); + service.publishEvent(new CompetencyJolSetEvent(this, jol)); } catch (Exception e) { log.warn("Something went wrong while sending the judgement of learning to Iris", e); diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisEventPublisher.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisEventPublisher.java new file mode 100644 index 000000000000..ff0695af0698 --- /dev/null +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisEventPublisher.java @@ -0,0 +1,106 @@ +package de.tum.cit.aet.artemis.iris.service.pyris; + +import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_IRIS; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Service; + +import de.tum.cit.aet.artemis.iris.domain.settings.event.IrisEventType; +import de.tum.cit.aet.artemis.iris.service.pyris.event.CompetencyJolSetEvent; +import de.tum.cit.aet.artemis.iris.service.pyris.event.NewResultEvent; +import de.tum.cit.aet.artemis.iris.service.pyris.event.PyrisEvent; +import de.tum.cit.aet.artemis.iris.service.settings.IrisSettingsService; +import de.tum.cit.aet.artemis.programming.domain.ProgrammingSubmission; + +/** + * Service for publishing Pyris events. + */ +@Service +@Profile(PROFILE_IRIS) +public class PyrisEventPublisher { + + private static final Logger log = LoggerFactory.getLogger(PyrisEventPublisher.class); + + private final ApplicationEventPublisher eventPublisher; + + private final IrisSettingsService irisSettingsService; + + public PyrisEventPublisher(ApplicationEventPublisher eventPublisher, IrisSettingsService irisSettingsService) { + this.eventPublisher = eventPublisher; + this.irisSettingsService = irisSettingsService; + } + + /** + * Publishes the given event. + * + * @param event the event to publish + */ + public void publishEvent(PyrisEvent event) { + isEventSupportedElseThrow(event); + if (!isEventEnabled(event)) { + log.debug("Skipping event publication as conditions are not met: {}", event.getClass().getSimpleName()); + return; + } + try { + eventPublisher.publishEvent(event); + } + catch (Exception e) { + log.error("Failed to publish event: {}", event, e); + throw e; + } + } + + /** + * Checks if the given event is supported and throws an exception if it is not. + * + * @param event - the event to check + */ + private void isEventSupportedElseThrow(ApplicationEvent event) { + if (!isEventSupported(event)) { + throw new UnsupportedPyrisEventException("Event not supported: " + event); + } + } + + /** + * Checks if the given event is supported. + * + * @param event the event to publish + */ + private boolean isEventSupported(ApplicationEvent event) { + return event instanceof PyrisEvent; + } + + private boolean isEventEnabled(PyrisEvent event) { + return switch (event) { + case NewResultEvent newResultEvent -> { + var result = newResultEvent.getResult(); + var submission = result.getSubmission(); + if (submission instanceof ProgrammingSubmission programmingSubmission) { + var programmingExercise = programmingSubmission.getParticipation().getExercise(); + if (programmingSubmission.isBuildFailed()) { + yield irisSettingsService.isActivatedFor(IrisEventType.BUILD_FAILED, programmingExercise); + } + else { + yield irisSettingsService.isActivatedFor(IrisEventType.PROGRESS_STALLED, programmingExercise); + } + } + else { + yield false; + } + } + case CompetencyJolSetEvent competencyJolSetEvent -> { + var competencyJol = competencyJolSetEvent.getCompetencyJol(); + var course = competencyJol.getCompetency().getCourse(); + yield irisSettingsService.isActivatedFor(IrisEventType.JOL, course); + } + default -> { + log.warn("Unknown event type: {}", event.getClass().getSimpleName()); + yield false; + } + }; + } +} diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisEventService.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisEventService.java index 58f0b8a069b8..4cf7865d5d8d 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisEventService.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisEventService.java @@ -5,18 +5,17 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Profile; +import org.springframework.context.event.EventListener; import org.springframework.stereotype.Service; -import de.tum.cit.aet.artemis.iris.domain.session.IrisChatSession; import de.tum.cit.aet.artemis.iris.service.pyris.event.CompetencyJolSetEvent; import de.tum.cit.aet.artemis.iris.service.pyris.event.NewResultEvent; -import de.tum.cit.aet.artemis.iris.service.pyris.event.PyrisEvent; -import de.tum.cit.aet.artemis.iris.service.session.AbstractIrisChatSessionService; import de.tum.cit.aet.artemis.iris.service.session.IrisCourseChatSessionService; import de.tum.cit.aet.artemis.iris.service.session.IrisExerciseChatSessionService; +import de.tum.cit.aet.artemis.programming.domain.ProgrammingSubmission; /** - * Service to handle Pyris events. + * Service for handling Pyris events. */ @Service @Profile(PROFILE_IRIS) @@ -33,33 +32,38 @@ public PyrisEventService(IrisCourseChatSessionService irisCourseChatSessionServi this.irisExerciseChatSessionService = irisExerciseChatSessionService; } - /** - * Triggers a Pyris pipeline based on the received {@link PyrisEvent}. - * - * @param event The event object received to trigger the matching pipeline - * @throws UnsupportedPyrisEventException if the event is not supported - * - * @see PyrisEvent - */ - public void trigger(PyrisEvent, ?> event) { - log.debug("Starting to process event of type: {}", event.getClass().getSimpleName()); + @EventListener + public void handleCompetencyJolSetEvent(CompetencyJolSetEvent event) { + log.debug("Processing CompetencyJolSetEvent"); try { - switch (event) { - case CompetencyJolSetEvent competencyJolSetEvent -> { - log.info("Processing CompetencyJolSetEvent: {}", competencyJolSetEvent); - competencyJolSetEvent.handleEvent(irisCourseChatSessionService); - log.debug("Successfully processed CompetencyJolSetEvent"); + irisCourseChatSessionService.onJudgementOfLearningSet(event.getCompetencyJol()); + log.debug("Successfully processed CompetencyJolSetEvent"); + } + catch (Exception e) { + log.error("Failed to process CompetencyJolSetEvent: {}", event, e); + throw e; + } + } + + @EventListener + public void handleNewResultEvent(NewResultEvent event) { + log.debug("Processing NewResultEvent"); + try { + var result = event.getResult(); + var submission = result.getSubmission(); + + if (submission instanceof ProgrammingSubmission programmingSubmission) { + if (programmingSubmission.isBuildFailed()) { + irisExerciseChatSessionService.onBuildFailure(result); } - case NewResultEvent newResultEvent -> { - log.info("Processing NewResultEvent: {}", newResultEvent); - newResultEvent.handleEvent(irisExerciseChatSessionService); - log.debug("Successfully processed NewResultEvent"); + else { + irisExerciseChatSessionService.onNewResult(result); } - default -> throw new UnsupportedPyrisEventException("Unsupported event type: " + event.getClass().getSimpleName()); } + log.debug("Successfully processed NewResultEvent"); } catch (Exception e) { - log.error("Failed to process event: {}", event, e); + log.error("Failed to process NewResultEvent: {}", event, e); throw e; } } diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/event/CompetencyJolSetEvent.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/event/CompetencyJolSetEvent.java index 9ee7448811b4..8ad3710df44b 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/event/CompetencyJolSetEvent.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/event/CompetencyJolSetEvent.java @@ -1,21 +1,28 @@ package de.tum.cit.aet.artemis.iris.service.pyris.event; +import java.util.Optional; + import de.tum.cit.aet.artemis.atlas.domain.competency.CompetencyJol; -import de.tum.cit.aet.artemis.iris.service.session.IrisCourseChatSessionService; +import de.tum.cit.aet.artemis.core.domain.User; -public class CompetencyJolSetEvent extends PyrisEvent { +public class CompetencyJolSetEvent extends PyrisEvent { - private final CompetencyJol eventObject; + private final CompetencyJol competencyJol; - public CompetencyJolSetEvent(CompetencyJol eventObject) { - if (eventObject == null) { - throw new IllegalArgumentException("Event object cannot be null"); + public CompetencyJolSetEvent(Object source, CompetencyJol competencyJol) { + super(source); + if (competencyJol == null) { + throw new IllegalArgumentException("CompetencyJol cannot be null"); } - this.eventObject = eventObject; + this.competencyJol = competencyJol; + } + + public CompetencyJol getCompetencyJol() { + return competencyJol; } @Override - public void handleEvent(IrisCourseChatSessionService service) { - service.onJudgementOfLearningSet(eventObject); + public Optional getUser() { + return Optional.ofNullable(competencyJol.getUser()); } } diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/event/NewResultEvent.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/event/NewResultEvent.java index 27516dff283a..1917f755a2f3 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/event/NewResultEvent.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/event/NewResultEvent.java @@ -1,34 +1,32 @@ package de.tum.cit.aet.artemis.iris.service.pyris.event; +import java.util.Optional; + import de.tum.cit.aet.artemis.assessment.domain.Result; -import de.tum.cit.aet.artemis.iris.service.session.IrisExerciseChatSessionService; -import de.tum.cit.aet.artemis.programming.domain.ProgrammingSubmission; +import de.tum.cit.aet.artemis.core.domain.User; +import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseStudentParticipation; -public class NewResultEvent extends PyrisEvent { +public class NewResultEvent extends PyrisEvent { - private final Result eventObject; + private final Result result; - public NewResultEvent(Result eventObject) { - if (eventObject == null) { - throw new IllegalArgumentException("Event object cannot be null"); + public NewResultEvent(Object source, Result result) { + super(source); + if (result == null) { + throw new IllegalArgumentException("Result cannot be null"); } - this.eventObject = eventObject; + this.result = result; + } + + public Result getResult() { + return result; } @Override - public void handleEvent(IrisExerciseChatSessionService service) { - if (service == null) { - throw new IllegalArgumentException("Service cannot be null"); - } - var submission = eventObject.getSubmission(); - // We only care about programming submissions - if (submission instanceof ProgrammingSubmission programmingSubmission) { - if (programmingSubmission.isBuildFailed()) { - service.onBuildFailure(eventObject); - } - else { - service.onNewResult(eventObject); - } + public Optional getUser() { + if (result.getSubmission().getParticipation() instanceof ProgrammingExerciseStudentParticipation studentParticipation) { + return studentParticipation.getStudent(); } + return Optional.empty(); } } diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/event/PyrisEvent.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/event/PyrisEvent.java index 0f4a723653d6..ba6d4d5b94e0 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/event/PyrisEvent.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/event/PyrisEvent.java @@ -1,14 +1,24 @@ package de.tum.cit.aet.artemis.iris.service.pyris.event; -import de.tum.cit.aet.artemis.iris.domain.session.IrisChatSession; -import de.tum.cit.aet.artemis.iris.service.session.AbstractIrisChatSessionService; +import java.util.Optional; -public abstract class PyrisEvent, T> { +import org.springframework.context.ApplicationEvent; + +import de.tum.cit.aet.artemis.core.domain.User; + +/** + * Base class for Pyris events. + */ +public abstract class PyrisEvent extends ApplicationEvent { + + public PyrisEvent(Object source) { + super(source); + } /** - * Handles the event using the given service. + * Returns the user associated with this event. * - * @param service The service to handle the event for + * @return the user */ - public abstract void handleEvent(S service); + public abstract Optional getUser(); } 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 f6a97190142c..00b5379f1496 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 @@ -133,9 +133,6 @@ protected void setLLMTokenUsageParameters(LLMTokenUsageService.LLMTokenUsageBuil */ public void onJudgementOfLearningSet(CompetencyJol competencyJol) { var course = competencyJol.getCompetency().getCourse(); - if (!irisSettingsService.isEnabledFor(IrisSubSettingsType.COURSE_CHAT, course)) { - return; - } var user = competencyJol.getUser(); user.hasAcceptedIrisElseThrow(); var session = getCurrentSessionOrCreateIfNotExistsInternal(course, user, false); 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 60c72e952b10..91b7c643258f 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 @@ -188,11 +188,6 @@ public void onBuildFailure(Result result) { } var exercise = validateExercise(participation.getExercise()); - if (!irisSettingsService.isActivatedFor(IrisEventType.BUILD_FAILED, exercise)) { - log.info("Build failure event is not activated for exercise {}", exercise.getId()); - return; - } - var participant = studentParticipation.getParticipant(); if (participant instanceof User user) { var session = getCurrentSessionOrCreateIfNotExistsInternal(exercise, user, false); @@ -218,11 +213,6 @@ public void onNewResult(Result result) { var exercise = validateExercise(participation.getExercise()); - if (!irisSettingsService.isActivatedFor(IrisEventType.PROGRESS_STALLED, exercise)) { - log.info("Progress stalled event is not activated for exercise {}", exercise.getId()); - return; - } - var recentSubmissions = submissionRepository.findAllWithResultsByParticipationIdOrderBySubmissionDateAsc(studentParticipation.getId()); double successThreshold = 100.0; // TODO: Retrieve configuration from Iris settings diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/settings/IrisSettingsService.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/settings/IrisSettingsService.java index 591e78caee3c..ff5891a219c6 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/settings/IrisSettingsService.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/settings/IrisSettingsService.java @@ -758,7 +758,16 @@ private boolean isEventEnabledInSettings(IrisCombinedSettingsDTO settings, IrisE yield true; } } - default -> throw new IllegalStateException("Unexpected value: " + type); // TODO: Add JOL event, once Course Chat Settings are implemented + case JOL -> { + if (settings.irisChatSettings().disabledProactiveEvents() != null) { + var isEventEnabled = !settings.irisChatSettings().disabledProactiveEvents().contains(IrisEventType.JOL.name().toLowerCase()); + log.debug("Event JOL enabled: {}", isEventEnabled); + yield isEventEnabled; + } + else { + yield true; + } + } }; } } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingMessagingService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingMessagingService.java index 8c73f1b25850..e25ebc1930f9 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingMessagingService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingMessagingService.java @@ -25,7 +25,7 @@ import de.tum.cit.aet.artemis.exercise.domain.participation.StudentParticipation; import de.tum.cit.aet.artemis.exercise.dto.SubmissionDTO; import de.tum.cit.aet.artemis.exercise.repository.TeamRepository; -import de.tum.cit.aet.artemis.iris.service.pyris.PyrisEventService; +import de.tum.cit.aet.artemis.iris.service.pyris.PyrisEventPublisher; import de.tum.cit.aet.artemis.iris.service.pyris.event.NewResultEvent; import de.tum.cit.aet.artemis.lti.service.LtiNewResultService; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; @@ -51,17 +51,17 @@ public class ProgrammingMessagingService { private final TeamRepository teamRepository; - private final Optional pyrisEventService; + private final Optional pyrisEventPublisher; public ProgrammingMessagingService(GroupNotificationService groupNotificationService, WebsocketMessagingService websocketMessagingService, ResultWebsocketService resultWebsocketService, Optional ltiNewResultService, TeamRepository teamRepository, - Optional pyrisEventService) { + Optional pyrisEventPublisher) { this.groupNotificationService = groupNotificationService; this.websocketMessagingService = websocketMessagingService; this.resultWebsocketService = resultWebsocketService; this.ltiNewResultService = ltiNewResultService; this.teamRepository = teamRepository; - this.pyrisEventService = pyrisEventService; + this.pyrisEventPublisher = pyrisEventPublisher; } private static String getExerciseTopicForTAAndAbove(long exerciseId) { @@ -170,7 +170,8 @@ public void notifyInstructorGroupAboutIllegalSubmissionsForExercise(ProgrammingE * @param participation the participation for which the result was created. */ public void notifyUserAboutNewResult(Result result, ProgrammingExerciseParticipation participation) { - log.debug("Send result to client over websocket. Result: {}, Submission: {}, Participation: {}", result, result.getSubmission(), result.getParticipation()); + var submission = result.getSubmission(); + log.debug("Send result to client over websocket. Result: {}, Submission: {}, Participation: {}", result, submission, submission.getParticipation()); // notify user via websocket resultWebsocketService.broadcastNewResult((Participation) participation, result); @@ -193,13 +194,12 @@ public void notifyUserAboutNewResult(Result result, ProgrammingExerciseParticipa */ private void notifyIrisAboutSubmissionStatus(Result result, ProgrammingExerciseStudentParticipation studentParticipation) { if (studentParticipation.getParticipant() instanceof User) { - pyrisEventService.ifPresent(eventService -> { - // Inform event service about the new result + pyrisEventPublisher.ifPresent(service -> { try { - eventService.trigger(new NewResultEvent(result)); + service.publishEvent(new NewResultEvent(this, result)); } catch (Exception e) { - log.error("Could not trigger service for result {}", result.getId(), e); + log.error("Could not publish event for result {}", result.getId(), e); } }); } diff --git a/src/test/java/de/tum/cit/aet/artemis/iris/PyrisEventSystemIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/iris/PyrisEventSystemIntegrationTest.java index dccb9eff49e9..760fa1409415 100644 --- a/src/test/java/de/tum/cit/aet/artemis/iris/PyrisEventSystemIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/iris/PyrisEventSystemIntegrationTest.java @@ -39,13 +39,11 @@ import de.tum.cit.aet.artemis.iris.domain.settings.event.IrisEventType; import de.tum.cit.aet.artemis.iris.repository.IrisSettingsRepository; import de.tum.cit.aet.artemis.iris.service.pyris.PyrisEventProcessingException; +import de.tum.cit.aet.artemis.iris.service.pyris.PyrisEventPublisher; import de.tum.cit.aet.artemis.iris.service.pyris.PyrisEventService; import de.tum.cit.aet.artemis.iris.service.pyris.PyrisJobService; import de.tum.cit.aet.artemis.iris.service.pyris.PyrisStatusUpdateService; -import de.tum.cit.aet.artemis.iris.service.pyris.UnsupportedPyrisEventException; import de.tum.cit.aet.artemis.iris.service.pyris.event.NewResultEvent; -import de.tum.cit.aet.artemis.iris.service.pyris.event.PyrisEvent; -import de.tum.cit.aet.artemis.iris.service.session.IrisExerciseChatSessionService; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseStudentParticipation; import de.tum.cit.aet.artemis.programming.domain.ProgrammingSubmission; @@ -64,6 +62,9 @@ class PyrisEventSystemIntegrationTest extends AbstractIrisIntegrationTest { @Autowired protected PyrisJobService pyrisJobService; + @Autowired + private PyrisEventService pyrisEventService; + @Autowired protected IrisSettingsRepository irisSettingsRepository; @@ -74,7 +75,7 @@ class PyrisEventSystemIntegrationTest extends AbstractIrisIntegrationTest { private SubmissionTestRepository submissionRepository; @Autowired - private PyrisEventService pyrisEventService; + private PyrisEventPublisher pyrisEventPublisher; @Autowired private ParticipationUtilService participationUtilService; @@ -192,7 +193,7 @@ void testShouldFireProgressStalledEvent() { pipelineDone.set(true); }); - pyrisEventService.trigger(new NewResultEvent(result)); + pyrisEventPublisher.publishEvent(new NewResultEvent(this, result)); verify(irisExerciseChatSessionService, times(1)).onNewResult(eq(result)); await().atMost(2, TimeUnit.SECONDS).until(() -> pipelineDone.get()); @@ -212,7 +213,7 @@ void testShouldFireBuildFailedEvent() { pipelineDone.set(true); }); - pyrisEventService.trigger(new NewResultEvent(result)); + pyrisEventPublisher.publishEvent(new NewResultEvent(this, result)); verify(irisExerciseChatSessionService, times(1)).onBuildFailure(eq(result)); await().atMost(2, TimeUnit.SECONDS).until(() -> pipelineDone.get()); @@ -239,19 +240,6 @@ void testShouldFireJolEvent() { } - @Test - @WithMockUser(username = TEST_PREFIX + "student1", roles = "USER") - void testShouldThrowUnsupportedEventException() { - assertThatExceptionOfType(UnsupportedPyrisEventException.class).isThrownBy(() -> pyrisEventService.trigger(new PyrisEvent() { - - @Override - public void handleEvent(IrisExerciseChatSessionService service) { - // Do nothing - } - })).withMessageStartingWith("Unsupported event"); - - } - @Test() @WithMockUser(username = TEST_PREFIX + "student1", roles = "USER") void testShouldNotFireProgressStalledEventWithEventDisabled() { @@ -263,9 +251,9 @@ void testShouldNotFireProgressStalledEventWithEventDisabled() { createSubmissionWithScore(studentParticipation, 40); createSubmissionWithScore(studentParticipation, 40); var result = createSubmissionWithScore(studentParticipation, 40); - pyrisEventService.trigger(new NewResultEvent(result)); + pyrisEventPublisher.publishEvent(new NewResultEvent(this, result)); - verify(irisExerciseChatSessionService, times(1)).onNewResult(any(Result.class)); + verify(irisExerciseChatSessionService, times(0)).onNewResult(any(Result.class)); verify(pyrisPipelineService, times(0)).executeExerciseChatPipeline(any(), any(), any(), any(), any()); } @@ -280,9 +268,9 @@ void testShouldNotFireBuildFailedEventWhenEventSettingDisabled() { irisExerciseChatSessionService.createChatSessionForProgrammingExercise(exercise, userUtilService.getUserByLogin(TEST_PREFIX + "student1")); // Create a failing submission for the student. var result = createFailingSubmission(studentParticipation); - pyrisEventService.trigger(new NewResultEvent(result)); + pyrisEventPublisher.publishEvent(new NewResultEvent(this, result)); - verify(irisExerciseChatSessionService, times(1)).onBuildFailure(any(Result.class)); + verify(irisExerciseChatSessionService, times(0)).onBuildFailure(any(Result.class)); verify(pyrisPipelineService, times(0)).executeExerciseChatPipeline(any(), any(), any(), any(), any()); } @@ -297,13 +285,13 @@ void testShouldShouldNotFireProgressStalledEventWithExistingSuccessfulSubmission createSubmissionWithScore(studentParticipation, 100); var result = createSubmissionWithScore(studentParticipation, 50); - pyrisEventService.trigger(new NewResultEvent(result)); + pyrisEventPublisher.publishEvent(new NewResultEvent(this, result)); await().atMost(2, TimeUnit.SECONDS); result = createSubmissionWithScore(studentParticipation, 50); - pyrisEventService.trigger(new NewResultEvent(result)); + pyrisEventPublisher.publishEvent(new NewResultEvent(this, result)); await().atMost(2, TimeUnit.SECONDS); verify(irisExerciseChatSessionService, times(2)).onNewResult(any(Result.class)); @@ -318,7 +306,7 @@ void testShouldNotFireProgressStalledEventWithLessThanThreeSubmissions() { createSubmissionWithScore(studentParticipation, 20); var result = createSubmissionWithScore(studentParticipation, 20); - pyrisEventService.trigger(new NewResultEvent(result)); + pyrisEventPublisher.publishEvent(new NewResultEvent(this, result)); verify(irisExerciseChatSessionService, times(1)).onNewResult(any(Result.class)); verify(pyrisPipelineService, times(0)).executeExerciseChatPipeline(any(), any(), any(), any(), any()); @@ -333,7 +321,7 @@ void testShouldNotFireProgressStalledEventWithIncreasingScores() { createSubmissionWithScore(studentParticipation, 30); var result = createSubmissionWithScore(studentParticipation, 40); - pyrisEventService.trigger(new NewResultEvent(result)); + pyrisEventPublisher.publishEvent(new NewResultEvent(this, result)); verify(irisExerciseChatSessionService, times(1)).onNewResult(any(Result.class)); verify(pyrisPipelineService, times(0)).executeExerciseChatPipeline(any(), any(), any(), any(), any()); From c269687936fb91fec3d007d5650e4b48333a6866 Mon Sep 17 00:00:00 2001 From: Kaan Cayli <38523756+kaancayli@users.noreply.github.com> Date: Sun, 15 Dec 2024 18:23:47 +0300 Subject: [PATCH 03/10] chore: Remove unnecessary function --- .../service/pyris/PyrisEventPublisher.java | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisEventPublisher.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisEventPublisher.java index ff0695af0698..f272a05d272f 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisEventPublisher.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisEventPublisher.java @@ -4,7 +4,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; @@ -40,7 +39,6 @@ public PyrisEventPublisher(ApplicationEventPublisher eventPublisher, IrisSetting * @param event the event to publish */ public void publishEvent(PyrisEvent event) { - isEventSupportedElseThrow(event); if (!isEventEnabled(event)) { log.debug("Skipping event publication as conditions are not met: {}", event.getClass().getSimpleName()); return; @@ -54,26 +52,6 @@ public void publishEvent(PyrisEvent event) { } } - /** - * Checks if the given event is supported and throws an exception if it is not. - * - * @param event - the event to check - */ - private void isEventSupportedElseThrow(ApplicationEvent event) { - if (!isEventSupported(event)) { - throw new UnsupportedPyrisEventException("Event not supported: " + event); - } - } - - /** - * Checks if the given event is supported. - * - * @param event the event to publish - */ - private boolean isEventSupported(ApplicationEvent event) { - return event instanceof PyrisEvent; - } - private boolean isEventEnabled(PyrisEvent event) { return switch (event) { case NewResultEvent newResultEvent -> { From 77a10be1528c105aeb06efc3df34c389f59fffef Mon Sep 17 00:00:00 2001 From: Kaan Cayli <38523756+kaancayli@users.noreply.github.com> Date: Mon, 16 Dec 2024 20:48:17 +0300 Subject: [PATCH 04/10] chore: Rename to highlight service functionality --- .../competency/CompetencyJolService.java | 6 +++--- ...r.java => PyrisEventPublisherService.java} | 6 +++--- .../service/ProgrammingMessagingService.java | 6 +++--- .../iris/PyrisEventSystemIntegrationTest.java | 20 +++++++++---------- 4 files changed, 19 insertions(+), 19 deletions(-) rename src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/{PyrisEventPublisher.java => PyrisEventPublisherService.java} (94%) 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 3885f233b224..60238c811ddd 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 @@ -23,7 +23,7 @@ import de.tum.cit.aet.artemis.atlas.repository.CompetencyRepository; import de.tum.cit.aet.artemis.core.exception.BadRequestAlertException; import de.tum.cit.aet.artemis.core.repository.UserRepository; -import de.tum.cit.aet.artemis.iris.service.pyris.PyrisEventPublisher; +import de.tum.cit.aet.artemis.iris.service.pyris.PyrisEventPublisherService; import de.tum.cit.aet.artemis.iris.service.pyris.event.CompetencyJolSetEvent; /** @@ -45,10 +45,10 @@ public class CompetencyJolService { private final UserRepository userRepository; - private final Optional pyrisEventPublisher; + private final Optional pyrisEventPublisher; public CompetencyJolService(CompetencyJolRepository competencyJolRepository, CompetencyRepository competencyRepository, - CompetencyProgressRepository competencyProgressRepository, UserRepository userRepository, Optional pyrisEventPublisher) { + CompetencyProgressRepository competencyProgressRepository, UserRepository userRepository, Optional pyrisEventPublisher) { this.competencyJolRepository = competencyJolRepository; this.competencyRepository = competencyRepository; this.competencyProgressRepository = competencyProgressRepository; diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisEventPublisher.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisEventPublisherService.java similarity index 94% rename from src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisEventPublisher.java rename to src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisEventPublisherService.java index f272a05d272f..df15e26872c0 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisEventPublisher.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisEventPublisherService.java @@ -20,15 +20,15 @@ */ @Service @Profile(PROFILE_IRIS) -public class PyrisEventPublisher { +public class PyrisEventPublisherService { - private static final Logger log = LoggerFactory.getLogger(PyrisEventPublisher.class); + private static final Logger log = LoggerFactory.getLogger(PyrisEventPublisherService.class); private final ApplicationEventPublisher eventPublisher; private final IrisSettingsService irisSettingsService; - public PyrisEventPublisher(ApplicationEventPublisher eventPublisher, IrisSettingsService irisSettingsService) { + public PyrisEventPublisherService(ApplicationEventPublisher eventPublisher, IrisSettingsService irisSettingsService) { this.eventPublisher = eventPublisher; this.irisSettingsService = irisSettingsService; } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingMessagingService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingMessagingService.java index e25ebc1930f9..8aa4dbfbdde4 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingMessagingService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingMessagingService.java @@ -25,7 +25,7 @@ import de.tum.cit.aet.artemis.exercise.domain.participation.StudentParticipation; import de.tum.cit.aet.artemis.exercise.dto.SubmissionDTO; import de.tum.cit.aet.artemis.exercise.repository.TeamRepository; -import de.tum.cit.aet.artemis.iris.service.pyris.PyrisEventPublisher; +import de.tum.cit.aet.artemis.iris.service.pyris.PyrisEventPublisherService; import de.tum.cit.aet.artemis.iris.service.pyris.event.NewResultEvent; import de.tum.cit.aet.artemis.lti.service.LtiNewResultService; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; @@ -51,11 +51,11 @@ public class ProgrammingMessagingService { private final TeamRepository teamRepository; - private final Optional pyrisEventPublisher; + private final Optional pyrisEventPublisher; public ProgrammingMessagingService(GroupNotificationService groupNotificationService, WebsocketMessagingService websocketMessagingService, ResultWebsocketService resultWebsocketService, Optional ltiNewResultService, TeamRepository teamRepository, - Optional pyrisEventPublisher) { + Optional pyrisEventPublisher) { this.groupNotificationService = groupNotificationService; this.websocketMessagingService = websocketMessagingService; this.resultWebsocketService = resultWebsocketService; diff --git a/src/test/java/de/tum/cit/aet/artemis/iris/PyrisEventSystemIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/iris/PyrisEventSystemIntegrationTest.java index 760fa1409415..2fbb90999893 100644 --- a/src/test/java/de/tum/cit/aet/artemis/iris/PyrisEventSystemIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/iris/PyrisEventSystemIntegrationTest.java @@ -39,7 +39,7 @@ import de.tum.cit.aet.artemis.iris.domain.settings.event.IrisEventType; import de.tum.cit.aet.artemis.iris.repository.IrisSettingsRepository; import de.tum.cit.aet.artemis.iris.service.pyris.PyrisEventProcessingException; -import de.tum.cit.aet.artemis.iris.service.pyris.PyrisEventPublisher; +import de.tum.cit.aet.artemis.iris.service.pyris.PyrisEventPublisherService; import de.tum.cit.aet.artemis.iris.service.pyris.PyrisEventService; import de.tum.cit.aet.artemis.iris.service.pyris.PyrisJobService; import de.tum.cit.aet.artemis.iris.service.pyris.PyrisStatusUpdateService; @@ -75,7 +75,7 @@ class PyrisEventSystemIntegrationTest extends AbstractIrisIntegrationTest { private SubmissionTestRepository submissionRepository; @Autowired - private PyrisEventPublisher pyrisEventPublisher; + private PyrisEventPublisherService pyrisEventPublisherService; @Autowired private ParticipationUtilService participationUtilService; @@ -193,7 +193,7 @@ void testShouldFireProgressStalledEvent() { pipelineDone.set(true); }); - pyrisEventPublisher.publishEvent(new NewResultEvent(this, result)); + pyrisEventPublisherService.publishEvent(new NewResultEvent(this, result)); verify(irisExerciseChatSessionService, times(1)).onNewResult(eq(result)); await().atMost(2, TimeUnit.SECONDS).until(() -> pipelineDone.get()); @@ -213,7 +213,7 @@ void testShouldFireBuildFailedEvent() { pipelineDone.set(true); }); - pyrisEventPublisher.publishEvent(new NewResultEvent(this, result)); + pyrisEventPublisherService.publishEvent(new NewResultEvent(this, result)); verify(irisExerciseChatSessionService, times(1)).onBuildFailure(eq(result)); await().atMost(2, TimeUnit.SECONDS).until(() -> pipelineDone.get()); @@ -251,7 +251,7 @@ void testShouldNotFireProgressStalledEventWithEventDisabled() { createSubmissionWithScore(studentParticipation, 40); createSubmissionWithScore(studentParticipation, 40); var result = createSubmissionWithScore(studentParticipation, 40); - pyrisEventPublisher.publishEvent(new NewResultEvent(this, result)); + pyrisEventPublisherService.publishEvent(new NewResultEvent(this, result)); verify(irisExerciseChatSessionService, times(0)).onNewResult(any(Result.class)); verify(pyrisPipelineService, times(0)).executeExerciseChatPipeline(any(), any(), any(), any(), any()); @@ -268,7 +268,7 @@ void testShouldNotFireBuildFailedEventWhenEventSettingDisabled() { irisExerciseChatSessionService.createChatSessionForProgrammingExercise(exercise, userUtilService.getUserByLogin(TEST_PREFIX + "student1")); // Create a failing submission for the student. var result = createFailingSubmission(studentParticipation); - pyrisEventPublisher.publishEvent(new NewResultEvent(this, result)); + pyrisEventPublisherService.publishEvent(new NewResultEvent(this, result)); verify(irisExerciseChatSessionService, times(0)).onBuildFailure(any(Result.class)); verify(pyrisPipelineService, times(0)).executeExerciseChatPipeline(any(), any(), any(), any(), any()); @@ -285,13 +285,13 @@ void testShouldShouldNotFireProgressStalledEventWithExistingSuccessfulSubmission createSubmissionWithScore(studentParticipation, 100); var result = createSubmissionWithScore(studentParticipation, 50); - pyrisEventPublisher.publishEvent(new NewResultEvent(this, result)); + pyrisEventPublisherService.publishEvent(new NewResultEvent(this, result)); await().atMost(2, TimeUnit.SECONDS); result = createSubmissionWithScore(studentParticipation, 50); - pyrisEventPublisher.publishEvent(new NewResultEvent(this, result)); + pyrisEventPublisherService.publishEvent(new NewResultEvent(this, result)); await().atMost(2, TimeUnit.SECONDS); verify(irisExerciseChatSessionService, times(2)).onNewResult(any(Result.class)); @@ -306,7 +306,7 @@ void testShouldNotFireProgressStalledEventWithLessThanThreeSubmissions() { createSubmissionWithScore(studentParticipation, 20); var result = createSubmissionWithScore(studentParticipation, 20); - pyrisEventPublisher.publishEvent(new NewResultEvent(this, result)); + pyrisEventPublisherService.publishEvent(new NewResultEvent(this, result)); verify(irisExerciseChatSessionService, times(1)).onNewResult(any(Result.class)); verify(pyrisPipelineService, times(0)).executeExerciseChatPipeline(any(), any(), any(), any(), any()); @@ -321,7 +321,7 @@ void testShouldNotFireProgressStalledEventWithIncreasingScores() { createSubmissionWithScore(studentParticipation, 30); var result = createSubmissionWithScore(studentParticipation, 40); - pyrisEventPublisher.publishEvent(new NewResultEvent(this, result)); + pyrisEventPublisherService.publishEvent(new NewResultEvent(this, result)); verify(irisExerciseChatSessionService, times(1)).onNewResult(any(Result.class)); verify(pyrisPipelineService, times(0)).executeExerciseChatPipeline(any(), any(), any(), any(), any()); From bc5d3db009c7e9e5ecdb380a904c2d77d4617e57 Mon Sep 17 00:00:00 2001 From: Kaan Cayli <38523756+kaancayli@users.noreply.github.com> Date: Mon, 16 Dec 2024 20:55:55 +0300 Subject: [PATCH 05/10] chore: Add deleted text --- .../service/pyris/PyrisEventPublisherService.java | 3 +-- .../iris/PyrisEventSystemIntegrationTest.java | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisEventPublisherService.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisEventPublisherService.java index df15e26872c0..327cd4d6e5c9 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisEventPublisherService.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisEventPublisherService.java @@ -76,8 +76,7 @@ private boolean isEventEnabled(PyrisEvent event) { yield irisSettingsService.isActivatedFor(IrisEventType.JOL, course); } default -> { - log.warn("Unknown event type: {}", event.getClass().getSimpleName()); - yield false; + throw new UnsupportedPyrisEventException("Unsupported Pyris event: " + event.getClass().getSimpleName()); } }; } diff --git a/src/test/java/de/tum/cit/aet/artemis/iris/PyrisEventSystemIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/iris/PyrisEventSystemIntegrationTest.java index 2fbb90999893..bf939cff1ddf 100644 --- a/src/test/java/de/tum/cit/aet/artemis/iris/PyrisEventSystemIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/iris/PyrisEventSystemIntegrationTest.java @@ -43,7 +43,9 @@ import de.tum.cit.aet.artemis.iris.service.pyris.PyrisEventService; import de.tum.cit.aet.artemis.iris.service.pyris.PyrisJobService; import de.tum.cit.aet.artemis.iris.service.pyris.PyrisStatusUpdateService; +import de.tum.cit.aet.artemis.iris.service.pyris.UnsupportedPyrisEventException; import de.tum.cit.aet.artemis.iris.service.pyris.event.NewResultEvent; +import de.tum.cit.aet.artemis.iris.service.pyris.event.PyrisEvent; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseStudentParticipation; import de.tum.cit.aet.artemis.programming.domain.ProgrammingSubmission; @@ -240,6 +242,19 @@ void testShouldFireJolEvent() { } + @Test + @WithMockUser(username = TEST_PREFIX + "student1", roles = "USER") + void testShouldThrowUnsupportedEventException() { + assertThatExceptionOfType(UnsupportedPyrisEventException.class).isThrownBy(() -> pyrisEventPublisherService.publishEvent(new PyrisEvent(this) { + + @Override + public Optional getUser() { + return Optional.empty(); + } + })).withMessageStartingWith("Unsupported Pyris event"); + + } + @Test() @WithMockUser(username = TEST_PREFIX + "student1", roles = "USER") void testShouldNotFireProgressStalledEventWithEventDisabled() { From 462cf0a556337ed7de38c5c62462c5fd582960c1 Mon Sep 17 00:00:00 2001 From: Kaan Cayli <38523756+kaancayli@users.noreply.github.com> Date: Mon, 16 Dec 2024 21:19:14 +0300 Subject: [PATCH 06/10] fix: Add missing javadoc --- .../iris/service/pyris/PyrisEventService.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisEventService.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisEventService.java index 4cf7865d5d8d..3ed043c627c7 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisEventService.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisEventService.java @@ -32,6 +32,13 @@ public PyrisEventService(IrisCourseChatSessionService irisCourseChatSessionServi this.irisExerciseChatSessionService = irisExerciseChatSessionService; } + /** + * Handles a CompetencyJolSetEvent. The event is passed to the {@link de.tum.cit.aet.artemis.iris.service.session.IrisCourseChatSessionService} to handle the judgement of + * learning set. + * + * @see IrisCourseChatSessionService#onJudgementOfLearningSet + * @param event the {@link CompetencyJolSetEvent} to handle + */ @EventListener public void handleCompetencyJolSetEvent(CompetencyJolSetEvent event) { log.debug("Processing CompetencyJolSetEvent"); @@ -45,6 +52,15 @@ public void handleCompetencyJolSetEvent(CompetencyJolSetEvent event) { } } + /** + * Handles a NewResultEvent. A new result represents a new submission result. + * Depending on whether there was a build failure or not, the result is passed to the appropriate handler method inside the + * {@link de.tum.cit.aet.artemis.iris.service.session.IrisExerciseChatSessionService}. + * + * @see IrisExerciseChatSessionService#onBuildFailure + * @see IrisExerciseChatSessionService#onNewResult + * @param event the {@link NewResultEvent} to handle + */ @EventListener public void handleNewResultEvent(NewResultEvent event) { log.debug("Processing NewResultEvent"); From 097e2ed53537184e38cebad141b89f854f374901 Mon Sep 17 00:00:00 2001 From: Kaan Cayli <38523756+kaancayli@users.noreply.github.com> Date: Mon, 16 Dec 2024 21:34:11 +0300 Subject: [PATCH 07/10] fix: Add null-checks --- .../pyris/PyrisEventPublisherService.java | 37 ++++++++++++++++--- .../service/pyris/event/NewResultEvent.java | 2 +- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisEventPublisherService.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisEventPublisherService.java index 327cd4d6e5c9..bbefb26846fe 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisEventPublisherService.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisEventPublisherService.java @@ -52,13 +52,32 @@ public void publishEvent(PyrisEvent event) { } } + /** + * Checks if the given event is enabled. + * + * @param event the event to check + * @return true if the event is enabled and conditions are met, false otherwise + */ private boolean isEventEnabled(PyrisEvent event) { return switch (event) { case NewResultEvent newResultEvent -> { var result = newResultEvent.getResult(); + if (result == null) { + yield false; + } var submission = result.getSubmission(); + if (submission == null) { + yield false; + } if (submission instanceof ProgrammingSubmission programmingSubmission) { - var programmingExercise = programmingSubmission.getParticipation().getExercise(); + var participation = programmingSubmission.getParticipation(); + if (participation == null) { + yield false; + } + var programmingExercise = participation.getExercise(); + if (programmingExercise == null) { + yield false; + } if (programmingSubmission.isBuildFailed()) { yield irisSettingsService.isActivatedFor(IrisEventType.BUILD_FAILED, programmingExercise); } @@ -72,12 +91,20 @@ private boolean isEventEnabled(PyrisEvent event) { } case CompetencyJolSetEvent competencyJolSetEvent -> { var competencyJol = competencyJolSetEvent.getCompetencyJol(); - var course = competencyJol.getCompetency().getCourse(); + if (competencyJol == null) { + yield false; + } + var competency = competencyJol.getCompetency(); + if (competency == null) { + yield false; + } + var course = competency.getCourse(); + if (course == null) { + yield false; + } yield irisSettingsService.isActivatedFor(IrisEventType.JOL, course); } - default -> { - throw new UnsupportedPyrisEventException("Unsupported Pyris event: " + event.getClass().getSimpleName()); - } + default -> throw new UnsupportedPyrisEventException("Unsupported Pyris event: " + event.getClass().getSimpleName()); }; } } diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/event/NewResultEvent.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/event/NewResultEvent.java index 1917f755a2f3..a2b010b5d76b 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/event/NewResultEvent.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/event/NewResultEvent.java @@ -24,7 +24,7 @@ public Result getResult() { @Override public Optional getUser() { - if (result.getSubmission().getParticipation() instanceof ProgrammingExerciseStudentParticipation studentParticipation) { + if (result.getSubmission() != null && result.getSubmission().getParticipation() instanceof ProgrammingExerciseStudentParticipation studentParticipation) { return studentParticipation.getStudent(); } return Optional.empty(); From 2afaa3be4af4f5c90f8914e06ed57f2c4857f9ee Mon Sep 17 00:00:00 2001 From: Kaan Cayli <38523756+kaancayli@users.noreply.github.com> Date: Tue, 7 Jan 2025 10:27:04 +0100 Subject: [PATCH 08/10] chore: Make publish method async --- .../artemis/iris/service/pyris/PyrisEventPublisherService.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisEventPublisherService.java b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisEventPublisherService.java index bbefb26846fe..f31fa6e960d7 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisEventPublisherService.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/service/pyris/PyrisEventPublisherService.java @@ -6,6 +6,7 @@ import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.annotation.Profile; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import de.tum.cit.aet.artemis.iris.domain.settings.event.IrisEventType; @@ -38,6 +39,7 @@ public PyrisEventPublisherService(ApplicationEventPublisher eventPublisher, Iris * * @param event the event to publish */ + @Async public void publishEvent(PyrisEvent event) { if (!isEventEnabled(event)) { log.debug("Skipping event publication as conditions are not met: {}", event.getClass().getSimpleName()); From 8f4ee18ccda1e17e90b6b069ab3a23d2e41eb1ad Mon Sep 17 00:00:00 2001 From: Kaan Cayli <38523756+kaancayli@users.noreply.github.com> Date: Tue, 7 Jan 2025 11:09:56 +0100 Subject: [PATCH 09/10] fix: Exercise enabled check --- .../artemis/iris/repository/IrisExerciseSettingsRepository.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/repository/IrisExerciseSettingsRepository.java b/src/main/java/de/tum/cit/aet/artemis/iris/repository/IrisExerciseSettingsRepository.java index 74b3e5abf57c..94610fc73ffe 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/repository/IrisExerciseSettingsRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/repository/IrisExerciseSettingsRepository.java @@ -18,7 +18,7 @@ public interface IrisExerciseSettingsRepository extends ArtemisJpaRepository 0 FROM IrisExerciseSettings s WHERE s.exercise.id = :exerciseId - AND s.irisTextExerciseChatSettings.enabled = TRUE + AND (s.irisTextExerciseChatSettings.enabled = TRUE OR s.irisChatSettings.enabled = TRUE) """) boolean isExerciseChatEnabled(@Param("exerciseId") long exerciseId); } From 56dfb89b527a52a529cabe731c453a21bbb3c9a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kaan=20=C3=87ayl=C4=B1?= <38523756+kaancayli@users.noreply.github.com> Date: Tue, 7 Jan 2025 11:31:02 +0100 Subject: [PATCH 10/10] chore: Apply coderabbit suggestion Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .../iris/repository/IrisExerciseSettingsRepository.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/iris/repository/IrisExerciseSettingsRepository.java b/src/main/java/de/tum/cit/aet/artemis/iris/repository/IrisExerciseSettingsRepository.java index 94610fc73ffe..ee1e1863e5ee 100644 --- a/src/main/java/de/tum/cit/aet/artemis/iris/repository/IrisExerciseSettingsRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/iris/repository/IrisExerciseSettingsRepository.java @@ -18,7 +18,8 @@ public interface IrisExerciseSettingsRepository extends ArtemisJpaRepository 0 FROM IrisExerciseSettings s WHERE s.exercise.id = :exerciseId - AND (s.irisTextExerciseChatSettings.enabled = TRUE OR s.irisChatSettings.enabled = TRUE) + AND ((s.irisTextExerciseChatSettings IS NOT NULL AND s.irisTextExerciseChatSettings.enabled = TRUE) + OR (s.irisChatSettings IS NOT NULL AND s.irisChatSettings.enabled = TRUE)) """) boolean isExerciseChatEnabled(@Param("exerciseId") long exerciseId); }