Skip to content

Commit

Permalink
chore: Refactor proactivity settings according to iris settings v3
Browse files Browse the repository at this point in the history
  • Loading branch information
kaancayli committed Oct 21, 2024
1 parent 3621f12 commit 2b4a287
Show file tree
Hide file tree
Showing 25 changed files with 195 additions and 182 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,43 @@
import java.util.Set;

import jakarta.persistence.CascadeType;
import jakarta.persistence.DiscriminatorValue;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;

import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;

import com.fasterxml.jackson.annotation.JsonInclude;

import de.tum.cit.aet.artemis.core.domain.DomainObject;
import de.tum.cit.aet.artemis.iris.domain.settings.event.IrisEventSettings;

/**
* Represents the specific ingestion sub-settings of lectures for Iris.
* This class extends {@link IrisSubSettings} to provide settings required for lecture data ingestion.
* Represents the proactivity sub settings for Iris.
*/
@Entity
@DiscriminatorValue("PROACTIVITY")
@Table(name = "iris_proactivity_settings")
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class IrisProactivitySubSettings extends IrisSubSettings {
public class IrisProactivitySubSettings extends DomainObject implements IrisToggleableSetting {

@Column(name = "enabled")
private boolean enabled = false;

@OneToMany(mappedBy = "proactivitySubSettings", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
private Set<IrisEventSettings> eventSettings = new HashSet<>();

public boolean isEnabled() {
return enabled;
}

public void setEnabled(boolean enabled) {
this.enabled = enabled;
}

public Set<IrisEventSettings> getEventSettings() {
return eventSettings;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,11 @@
@JsonSubTypes.Type(value = IrisChatSubSettings.class, name = "chat"),
@JsonSubTypes.Type(value = IrisTextExerciseChatSubSettings.class, name = "text-exercise-chat"),
@JsonSubTypes.Type(value = IrisLectureIngestionSubSettings.class, name = "lecture-ingestion"),
@JsonSubTypes.Type(value = IrisCompetencyGenerationSubSettings.class, name = "competency-generation"),
@JsonSubTypes.Type(value = IrisProactivitySubSettings.class, name = "proactivity")
@JsonSubTypes.Type(value = IrisCompetencyGenerationSubSettings.class, name = "competency-generation")
})
// @formatter:on
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public abstract class IrisSubSettings extends DomainObject {
public abstract class IrisSubSettings extends DomainObject implements IrisToggleableSetting {

@Column(name = "enabled")
private boolean enabled = false;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package de.tum.cit.aet.artemis.iris.domain.settings;

/**
* Represents an Iris setting that can be toggled on or off.
*/
public interface IrisToggleableSetting {

boolean isEnabled();

void setEnabled(boolean enabled);
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@
public class IrisBuildFailedEventSettings extends IrisEventSettings {

@Override
public IrisEventTarget getDefaultLevel() {
return IrisEventTarget.EXERCISE;
public IrisEventSessionType getDefaultSessionType() {
return IrisEventSessionType.EXERCISE;
}

@Override
public String getDefaultPipelineVariant() {
public String getDefaultSelectedEventVariant() {
return "build_failed";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
/**
* The target session type of Iris event. Currently, only EXERCISE and COURSE are supported.
*/
public enum IrisEventTarget {
public enum IrisEventSessionType {
EXERCISE, COURSE
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package de.tum.cit.aet.artemis.iris.domain.settings.event;

import java.util.SortedSet;
import java.util.TreeSet;

import jakarta.persistence.Column;
import jakarta.persistence.Convert;
import jakarta.persistence.DiscriminatorColumn;
import jakarta.persistence.DiscriminatorType;
import jakarta.persistence.Entity;
Expand All @@ -25,8 +29,18 @@
import com.fasterxml.jackson.annotation.JsonTypeInfo;

import de.tum.cit.aet.artemis.core.domain.DomainObject;
import de.tum.cit.aet.artemis.iris.domain.settings.IrisListConverter;
import de.tum.cit.aet.artemis.iris.domain.settings.IrisProactivitySubSettings;

/**
* IrisEventSettings is an abstract super class for the specific sub event settings types.
* Sub Event Settings are settings for a proactive event of Iris.
* {@link IrisProgressStalledEventSettings} are used to specify settings for the progress stalled event.
* {@link IrisBuildFailedEventSettings} are used to specify settings for the build failed event.
* {@link IrisJolEventSettings} are used to specify settings for the JOL event.
* <p>
* Also see {@link de.tum.cit.aet.artemis.iris.service.settings.IrisSettingsService} for more information.
*/
@Entity
@Table(name = "iris_event_settings")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
Expand All @@ -41,19 +55,22 @@
})
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public abstract class IrisEventSettings extends DomainObject {
// Is event active
@Column(name = "is_active", nullable = false)
private boolean isActive;
@Column(name = "enabled", nullable = false)
private boolean enabled;

@Column(name = "allowed_event_variants", nullable = false)
@Convert(converter = IrisListConverter.class)
private SortedSet<String> allowedEventVariants = new TreeSet<>();

// The variant of the pipeline the event is associated with
@Column(name = "pipeline_variant", nullable = false)
private String pipelineVariant;
// The selected event variant of the pipeline the event is associated with
@Column(name = "selected_event_variant", nullable = false)
private String selectedEventVariant;

// The level of the event which type of session the event will be triggered in
// The session type of the event which type of session the event will be triggered in
@Nullable
@Enumerated(EnumType.STRING)
@Column(name = "target")
private IrisEventTarget target;
@Column(name = "session_type", nullable = false)
private IrisEventSessionType sessionType;

@JsonIgnore
@ManyToOne
Expand All @@ -63,11 +80,11 @@ public abstract class IrisEventSettings extends DomainObject {
@PrePersist
@PreUpdate
protected void onCreate() {
if (target == null) {
target = getDefaultLevel();
if (sessionType == null) {
sessionType = getDefaultSessionType();
}
if (pipelineVariant == null) {
pipelineVariant = getDefaultPipelineVariant();
if (selectedEventVariant == null) {
selectedEventVariant = getDefaultSelectedEventVariant();
}
}

Expand All @@ -79,29 +96,38 @@ public void setProactivitySubSettings(IrisProactivitySubSettings proactivitySubS
this.proactivitySubSettings = proactivitySubSettings;
}

public boolean isActive() {
return isActive;
public boolean isEnabled() {
return enabled;
}

public void setEnabled(boolean active) {
enabled = active;
}

public void setActive(boolean active) {
isActive = active;
public IrisEventSessionType getSessionType() {
return sessionType;
}

public String getPipelineVariant() {
return pipelineVariant;
public SortedSet<String> getAllowedEventVariants() {
return allowedEventVariants;
}

public void setPipelineVariant(String pipelineVariant) {
this.pipelineVariant = pipelineVariant;
public void setAllowedEventVariants(SortedSet<String> allowedEventVariants) {
this.allowedEventVariants = allowedEventVariants;
}

@Nullable
public String getSelectedEventVariant() {
return selectedEventVariant;
}

public IrisEventTarget getTarget() {
return target;
public void setSelectedEventVariant(@Nullable String selectedVariant) {
this.selectedEventVariant = selectedVariant;
}

@JsonIgnore
protected abstract IrisEventTarget getDefaultLevel();
protected abstract IrisEventSessionType getDefaultSessionType();

@JsonIgnore
protected abstract String getDefaultPipelineVariant();
protected abstract String getDefaultSelectedEventVariant();
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@
public class IrisJolEventSettings extends IrisEventSettings {

@Override
public IrisEventTarget getDefaultLevel() {
return IrisEventTarget.COURSE;
public IrisEventSessionType getDefaultSessionType() {
return IrisEventSessionType.COURSE;
}

@Override
public String getDefaultPipelineVariant() {
public String getDefaultSelectedEventVariant() {
return "jol";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@
public class IrisProgressStalledEventSettings extends IrisEventSettings {

@Override
public IrisEventTarget getDefaultLevel() {
return IrisEventTarget.EXERCISE;
public IrisEventSessionType getDefaultSessionType() {
return IrisEventSessionType.EXERCISE;
}

@Override
public String getDefaultPipelineVariant() {
public String getDefaultSelectedEventVariant() {
return "progress_stalled";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@

import com.fasterxml.jackson.annotation.JsonInclude;

import de.tum.cit.aet.artemis.iris.domain.settings.event.IrisEventSessionType;
import de.tum.cit.aet.artemis.iris.domain.settings.event.IrisEventSettings;
import de.tum.cit.aet.artemis.iris.domain.settings.event.IrisEventTarget;

@JsonInclude(JsonInclude.Include.NON_EMPTY)
public record IrisCombinedEventSettingsDTO(boolean isActive, String pipelineVariant, @Nullable IrisEventTarget target) {
public record IrisCombinedEventSettingsDTO(boolean enabled, String selectedEventVariant, @Nullable IrisEventSessionType sessionType) {

public static IrisCombinedEventSettingsDTO of(IrisEventSettings eventSettings) {
return new IrisCombinedEventSettingsDTO(eventSettings.isActive(), eventSettings.getPipelineVariant(), eventSettings.getTarget());
return new IrisCombinedEventSettingsDTO(eventSettings.isEnabled(), eventSettings.getSelectedEventVariant(), eventSettings.getSessionType());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_IRIS;

import java.util.Optional;

import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;

Expand Down Expand Up @@ -48,6 +50,7 @@ public void executeCompetencyExtractionPipeline(User user, Course course, String
pyrisPipelineService.executePipeline(
"competency-extraction",
"default",
Optional.empty(),
pyrisJobService.createTokenForJob(token -> new CompetencyExtractionJob(token, 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))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import java.util.Arrays;
import java.util.List;
import java.util.Optional;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -60,13 +61,13 @@ public List<PyrisVariantDTO> getOfferedVariants(IrisSubSettingsType feature) thr
try {
var response = restTemplate.getForEntity(pyrisUrl + "/api/v1/pipelines/" + feature.name() + "/variants", PyrisVariantDTO[].class);
if (!response.getStatusCode().is2xxSuccessful() || !response.hasBody()) {
throw new PyrisConnectorException("Could not fetch offered models");
throw new PyrisConnectorException("Could not fetch offered variants");
}
return Arrays.asList(response.getBody());
}
catch (HttpStatusCodeException e) {
log.error("Failed to fetch offered models from Pyris", e);
throw new PyrisConnectorException("Could not fetch offered models");
log.error("Failed to fetch offered variants from Pyris", e);
throw new PyrisConnectorException("Could not fetch offered variants");
}
}

Expand All @@ -77,8 +78,10 @@ public List<PyrisVariantDTO> getOfferedVariants(IrisSubSettingsType feature) thr
* @param variant The variant of the feature to execute
* @param executionDTO The DTO sent as a body for the execution
*/
public void executePipeline(String feature, String variant, Object executionDTO) {
public void executePipeline(String feature, String variant, Object executionDTO, Optional<String> event) {
var endpoint = "/api/v1/pipelines/" + feature + "/" + variant + "/run";
// Add event query parameter if present
endpoint += event.map(e -> "?event=" + e).orElse("");
try {
restTemplate.postForEntity(pyrisUrl + endpoint, objectMapper.valueToTree(executionDTO), Void.class);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ public PyrisPipelineService(PyrisConnectorService pyrisConnectorService, PyrisJo
* @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
*/
public void executePipeline(String name, String variant, String jobToken, Function<PyrisPipelineExecutionDTO, Object> dtoMapper, Consumer<List<PyrisStageDTO>> statusUpdater) {
public void executePipeline(String name, String variant, Optional<String> event, String jobToken, Function<PyrisPipelineExecutionDTO, Object> dtoMapper,
Consumer<List<PyrisStageDTO>> 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);
Expand All @@ -116,7 +117,7 @@ public void executePipeline(String name, String variant, String jobToken, Functi

try {
// Execute the pipeline using the connector service
pyrisConnectorService.executePipeline(name, variant, pipelineDto);
pyrisConnectorService.executePipeline(name, variant, pipelineDto, event);
}
catch (PyrisConnectorException | IrisException e) {
log.error("Failed to execute {} pipeline", name, e);
Expand All @@ -143,11 +144,13 @@ public void executePipeline(String name, String variant, String jobToken, Functi
* @param session the chat session
* @see PyrisPipelineService#executePipeline for more details on the pipeline execution process.
*/
public void executeExerciseChatPipeline(String variant, Optional<ProgrammingSubmission> latestSubmission, ProgrammingExercise exercise, IrisExerciseChatSession session) {
public void executeExerciseChatPipeline(String variant, Optional<ProgrammingSubmission> latestSubmission, ProgrammingExercise exercise, IrisExerciseChatSession session,
Optional<String> eventVariant) {
// @formatter:off
executePipeline(
"tutor-chat", // TODO: Rename this to 'exercise-chat' with next breaking Pyris version
variant,
eventVariant,
pyrisJobService.addExerciseChatJob(exercise.getCourseViaExerciseGroupOrCourseMember().getId(), exercise.getId(), session.getId()),
executionDto -> {
var course = exercise.getCourseViaExerciseGroupOrCourseMember();
Expand Down Expand Up @@ -180,10 +183,10 @@ public void executeExerciseChatPipeline(String variant, Optional<ProgrammingSubm
* @param <T> the type of the object
* @param <U> the type of the DTO
*/
private <T, U> void executeCourseChatPipeline(String variant, IrisCourseChatSession session, T eventObject, Class<U> eventDtoClass) {
private <T, U> void executeCourseChatPipeline(String variant, IrisCourseChatSession session, T eventObject, Class<U> eventDtoClass, Optional<String> eventVariant) {
var courseId = session.getCourse().getId();
var studentId = session.getUser().getId();
executePipeline("course-chat", variant, pyrisJobService.addCourseChatJob(courseId, session.getId()), executionDto -> {
executePipeline("course-chat", variant, eventVariant, pyrisJobService.addCourseChatJob(courseId, session.getId()), executionDto -> {
var fullCourse = loadCourseWithParticipationOfStudent(courseId, studentId);
return new PyrisCourseChatPipelineExecutionDTO(PyrisExtendedCourseDTO.of(fullCourse),
learningMetricsService.getStudentCourseMetrics(session.getUser().getId(), courseId), generateEventPayloadFromObjectType(eventDtoClass, eventObject),
Expand All @@ -209,9 +212,9 @@ private <T, U> void executeCourseChatPipeline(String variant, IrisCourseChatSess
public void executeCourseChatPipeline(String variant, IrisCourseChatSession session, Object object) {
log.debug("Executing course chat pipeline variant {} with object {}", variant, object);
switch (object) {
case null -> executeCourseChatPipeline(variant, session, null, null);
case CompetencyJol competencyJol -> executeCourseChatPipeline(variant, session, competencyJol, CompetencyJolDTO.class);
case Exercise exercise -> executeCourseChatPipeline(variant, session, exercise, PyrisExerciseWithStudentSubmissionsDTO.class);
case null -> executeCourseChatPipeline(variant, session, null, null, Optional.empty());
case CompetencyJol competencyJol -> executeCourseChatPipeline(variant, session, competencyJol, CompetencyJolDTO.class, Optional.of("jol"));
case Exercise exercise -> executeCourseChatPipeline(variant, session, exercise, PyrisExerciseWithStudentSubmissionsDTO.class, Optional.empty());
default -> throw new UnsupportedOperationException("Unsupported Pyris event payload type: " + object);
}
}
Expand Down
Loading

0 comments on commit 2b4a287

Please sign in to comment.