Skip to content

Commit

Permalink
Merge branch 'develop' into chore/remove-deprecated-file-upload
Browse files Browse the repository at this point in the history
  • Loading branch information
julian-christl authored Oct 27, 2023
2 parents c786044 + 0e6ae84 commit 4bf9968
Show file tree
Hide file tree
Showing 74 changed files with 1,243 additions and 127 deletions.
14 changes: 14 additions & 0 deletions src/main/java/de/tum/in/www1/artemis/domain/Exercise.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import de.tum.in.www1.artemis.domain.participation.StudentParticipation;
import de.tum.in.www1.artemis.domain.participation.TutorParticipation;
import de.tum.in.www1.artemis.domain.plagiarism.PlagiarismCase;
import de.tum.in.www1.artemis.domain.plagiarism.PlagiarismDetectionConfig;
import de.tum.in.www1.artemis.domain.quiz.QuizExercise;
import de.tum.in.www1.artemis.domain.view.QuizView;
import de.tum.in.www1.artemis.service.ExerciseDateService;
Expand Down Expand Up @@ -143,6 +144,11 @@ public abstract class Exercise extends BaseExercise implements LearningObject {
@JsonIncludeProperties({ "id" })
private Set<PlagiarismCase> plagiarismCases = new HashSet<>();

@OneToOne(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
@JoinColumn(name = "plagiarism_detection_config_id")
@JsonIgnoreProperties("exercise")
private PlagiarismDetectionConfig plagiarismDetectionConfig;

// NOTE: Helpers variable names must be different from Getter name, so that Jackson ignores the @Transient annotation, but Hibernate still respects it
@Transient
private DueDateStat numberOfSubmissionsTransient;
Expand Down Expand Up @@ -400,6 +406,14 @@ public void setPlagiarismCases(Set<PlagiarismCase> plagiarismCases) {
this.plagiarismCases = plagiarismCases;
}

public PlagiarismDetectionConfig getPlagiarismDetectionConfig() {
return plagiarismDetectionConfig;
}

public void setPlagiarismDetectionConfig(PlagiarismDetectionConfig plagiarismDetectionConfig) {
this.plagiarismDetectionConfig = plagiarismDetectionConfig;
}

// jhipster-needle-entity-add-getters-setters - JHipster will add getters and setters here, do not remove

public Set<Competency> getCompetencies() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ public enum NotificationType {
EXERCISE_SUBMISSION_ASSESSED, ATTACHMENT_CHANGE, EXERCISE_RELEASED, EXERCISE_PRACTICE, QUIZ_EXERCISE_STARTED, EXERCISE_UPDATED, NEW_REPLY_FOR_EXERCISE_POST,
NEW_REPLY_FOR_LECTURE_POST, NEW_REPLY_FOR_COURSE_POST, NEW_EXERCISE_POST, NEW_LECTURE_POST, NEW_COURSE_POST, NEW_ANNOUNCEMENT_POST, FILE_SUBMISSION_SUCCESSFUL,
COURSE_ARCHIVE_STARTED, COURSE_ARCHIVE_FINISHED, COURSE_ARCHIVE_FAILED, PROGRAMMING_TEST_CASES_CHANGED, DUPLICATE_TEST_CASE, EXAM_ARCHIVE_STARTED, EXAM_ARCHIVE_FINISHED,
EXAM_ARCHIVE_FAILED, ILLEGAL_SUBMISSION, NEW_PLAGIARISM_CASE_STUDENT, PLAGIARISM_CASE_VERDICT_STUDENT, NEW_MANUAL_FEEDBACK_REQUEST, TUTORIAL_GROUP_REGISTRATION_STUDENT,
TUTORIAL_GROUP_DEREGISTRATION_STUDENT, TUTORIAL_GROUP_REGISTRATION_TUTOR, TUTORIAL_GROUP_MULTIPLE_REGISTRATION_TUTOR, TUTORIAL_GROUP_DEREGISTRATION_TUTOR,
TUTORIAL_GROUP_DELETED, TUTORIAL_GROUP_UPDATED, TUTORIAL_GROUP_ASSIGNED, TUTORIAL_GROUP_UNASSIGNED, CONVERSATION_NEW_MESSAGE, CONVERSATION_NEW_REPLY_MESSAGE,
CONVERSATION_CREATE_ONE_TO_ONE_CHAT, CONVERSATION_CREATE_GROUP_CHAT, CONVERSATION_ADD_USER_GROUP_CHAT, CONVERSATION_ADD_USER_CHANNEL, CONVERSATION_REMOVE_USER_GROUP_CHAT,
CONVERSATION_REMOVE_USER_CHANNEL, CONVERSATION_DELETE_CHANNEL, DATA_EXPORT_CREATED, DATA_EXPORT_FAILED
EXAM_ARCHIVE_FAILED, ILLEGAL_SUBMISSION, NEW_PLAGIARISM_CASE_STUDENT, NEW_CPC_PLAGIARISM_CASE_STUDENT, PLAGIARISM_CASE_VERDICT_STUDENT, NEW_MANUAL_FEEDBACK_REQUEST,
TUTORIAL_GROUP_REGISTRATION_STUDENT, TUTORIAL_GROUP_DEREGISTRATION_STUDENT, TUTORIAL_GROUP_REGISTRATION_TUTOR, TUTORIAL_GROUP_MULTIPLE_REGISTRATION_TUTOR,
TUTORIAL_GROUP_DEREGISTRATION_TUTOR, TUTORIAL_GROUP_DELETED, TUTORIAL_GROUP_UPDATED, TUTORIAL_GROUP_ASSIGNED, TUTORIAL_GROUP_UNASSIGNED, CONVERSATION_NEW_MESSAGE,
CONVERSATION_NEW_REPLY_MESSAGE, CONVERSATION_CREATE_ONE_TO_ONE_CHAT, CONVERSATION_CREATE_GROUP_CHAT, CONVERSATION_ADD_USER_GROUP_CHAT, CONVERSATION_ADD_USER_CHANNEL,
CONVERSATION_REMOVE_USER_GROUP_CHAT, CONVERSATION_REMOVE_USER_CHANNEL, CONVERSATION_DELETE_CHANNEL, DATA_EXPORT_CREATED, DATA_EXPORT_FAILED
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ public class NotificationConstants {

public static final String NEW_PLAGIARISM_CASE_STUDENT_TITLE = "artemisApp.singleUserNotification.title.newPlagiarismCaseStudent";

public static final String NEW_CPC_PLAGIARISM_CASE_STUDENT_TITLE = "artemisApp.singleUserNotification.title.newPlagiarismCaseStudentSignificantSimilarity";

public static final String PLAGIARISM_CASE_VERDICT_STUDENT_TITLE = "artemisApp.singleUserNotification.title.plagiarismCaseVerdictStudent";

public static final String TUTORIAL_GROUP_REGISTRATION_STUDENT_TITLE = "artemisApp.singleUserNotification.title.tutorialGroupRegistrationStudent";
Expand Down Expand Up @@ -153,6 +155,8 @@ public class NotificationConstants {

public static final String NEW_PLAGIARISM_CASE_STUDENT_TEXT = "artemisApp.singleUserNotification.text.newPlagiarismCaseStudent";

public static final String NEW_CPC_PLAGIARISM_CASE_STUDENT_TEXT = "artemisApp.singleUserNotification.text.newPlagiarismCaseStudentSignificantSimilarity";

public static final String PLAGIARISM_CASE_VERDICT_STUDENT_TEXT = "artemisApp.singleUserNotification.text.plagiarismCaseVerdictStudent";

public static final String TUTORIAL_GROUP_REGISTRATION_STUDENT_TEXT = "artemisApp.singleUserNotification.text.tutorialGroupRegistrationStudent";
Expand Down Expand Up @@ -225,8 +229,8 @@ public class NotificationConstants {
.put(COURSE_ARCHIVE_FINISHED, COURSE_ARCHIVE_FINISHED_TITLE).put(COURSE_ARCHIVE_FAILED, COURSE_ARCHIVE_FAILED_TITLE)
.put(EXAM_ARCHIVE_STARTED, EXAM_ARCHIVE_STARTED_TITLE).put(EXAM_ARCHIVE_FAILED, EXAM_ARCHIVE_FAILED_TITLE).put(EXAM_ARCHIVE_FINISHED, EXAM_ARCHIVE_FINISHED_TITLE)
.put(PROGRAMMING_TEST_CASES_CHANGED, PROGRAMMING_TEST_CASES_CHANGED_TITLE).put(NEW_MANUAL_FEEDBACK_REQUEST, NEW_MANUAL_FEEDBACK_REQUEST_TITLE)
.put(NEW_PLAGIARISM_CASE_STUDENT, NEW_PLAGIARISM_CASE_STUDENT_TITLE).put(PLAGIARISM_CASE_VERDICT_STUDENT, PLAGIARISM_CASE_VERDICT_STUDENT_TITLE)
.put(TUTORIAL_GROUP_REGISTRATION_STUDENT, TUTORIAL_GROUP_REGISTRATION_STUDENT_TITLE)
.put(NEW_PLAGIARISM_CASE_STUDENT, NEW_PLAGIARISM_CASE_STUDENT_TITLE).put(NEW_CPC_PLAGIARISM_CASE_STUDENT, NEW_CPC_PLAGIARISM_CASE_STUDENT_TITLE)
.put(PLAGIARISM_CASE_VERDICT_STUDENT, PLAGIARISM_CASE_VERDICT_STUDENT_TITLE).put(TUTORIAL_GROUP_REGISTRATION_STUDENT, TUTORIAL_GROUP_REGISTRATION_STUDENT_TITLE)
.put(TUTORIAL_GROUP_DEREGISTRATION_STUDENT, TUTORIAL_GROUP_DEREGISTRATION_STUDENT_TITLE).put(TUTORIAL_GROUP_REGISTRATION_TUTOR, TUTORIAL_GROUP_REGISTRATION_TUTOR_TITLE)
.put(TUTORIAL_GROUP_DEREGISTRATION_TUTOR, TUTORIAL_GROUP_DEREGISTRATION_TUTOR_TITLE).put(TUTORIAL_GROUP_DELETED, TUTORIAL_GROUP_DELETED_TITLE)
.put(TUTORIAL_GROUP_UPDATED, TUTORIAL_GROUP_UPDATED_TITLE).put(TUTORIAL_GROUP_MULTIPLE_REGISTRATION_TUTOR, TUTORIAL_GROUP_REGISTRATION_MULTIPLE_TUTOR_TITLE)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,12 @@ public static SingleUserNotification createNotification(PlagiarismCase plagiaris
placeholderValues = new String[] { affectedExercise.getCourseViaExerciseGroupOrCourseMember().getTitle(),
affectedExercise.getExerciseType().toString().toLowerCase(), affectedExercise.getTitle() };
}
case NEW_CPC_PLAGIARISM_CASE_STUDENT -> {
title = NEW_CPC_PLAGIARISM_CASE_STUDENT_TITLE;
notificationText = NEW_CPC_PLAGIARISM_CASE_STUDENT_TEXT;
placeholderValues = new String[] { affectedExercise.getCourseViaExerciseGroupOrCourseMember().getTitle(),
affectedExercise.getExerciseType().toString().toLowerCase(), affectedExercise.getTitle() };
}
case PLAGIARISM_CASE_VERDICT_STUDENT -> {
title = PLAGIARISM_CASE_VERDICT_STUDENT_TITLE;
notificationText = PLAGIARISM_CASE_VERDICT_STUDENT_TEXT;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ public class PlagiarismCase extends AbstractAuditingEntity {
@Column(name = "verdict_point_deduction")
private int verdictPointDeduction;

@Column(name = "created_by_continuous_plagiarism_control")
private boolean createdByContinuousPlagiarismControl;

public Exercise getExercise() {
return exercise;
}
Expand Down Expand Up @@ -151,6 +154,14 @@ public void setVerdictPointDeduction(int verdictPointDeduction) {
this.verdictPointDeduction = verdictPointDeduction;
}

public boolean isCreatedByContinuousPlagiarismControl() {
return createdByContinuousPlagiarismControl;
}

public void setCreatedByContinuousPlagiarismControl(boolean createdByContinuousPlagiarismControl) {
this.createdByContinuousPlagiarismControl = createdByContinuousPlagiarismControl;
}

@Override
public String toString() {
return "PlagiarismCase{" + "exercise=" + exercise + ", student=" + student + ", post=" + post + ", verdict=" + verdict + ", verdictMessage=" + verdictMessage
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,124 @@
package de.tum.in.www1.artemis.domain.plagiarism;

import java.util.Objects;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;

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

import com.fasterxml.jackson.annotation.JsonInclude;

import de.tum.in.www1.artemis.domain.DomainObject;

/**
* Stores configuration for plagiarism detection.
* Stores configuration for manual and continuous plagiarism control.
*/
public record PlagiarismDetectionConfig(float similarityThreshold, int minimumScore, int minimumSize) {
@Entity
@Table(name = "plagiarism_detection_config")
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class PlagiarismDetectionConfig extends DomainObject {

@Column(name = "continuous_plagiarism_control_enabled")
private boolean continuousPlagiarismControlEnabled = false;

@Column(name = "continuous_plagiarism_control_post_due_date_checks_enabled")
private boolean continuousPlagiarismControlPostDueDateChecksEnabled = false;

@Column(name = "similarity_threshold")
private int similarityThreshold;

@Column(name = "minimum_score")
private int minimumScore;

@Column(name = "minimum_size")
private int minimumSize;

public boolean isContinuousPlagiarismControlEnabled() {
return continuousPlagiarismControlEnabled;
}

public void setContinuousPlagiarismControlEnabled(boolean continuousPlagiarismControlEnabled) {
this.continuousPlagiarismControlEnabled = continuousPlagiarismControlEnabled;
}

public boolean isContinuousPlagiarismControlPostDueDateChecksEnabled() {
return continuousPlagiarismControlPostDueDateChecksEnabled;
}

public void setContinuousPlagiarismControlPostDueDateChecksEnabled(boolean continuousPlagiarismControlPostDueDateChecksEnabled) {
this.continuousPlagiarismControlPostDueDateChecksEnabled = continuousPlagiarismControlPostDueDateChecksEnabled;
}

public float getSimilarityThreshold() {
return similarityThreshold;
}

public int getMinimumScore() {
return minimumScore;
}

public int getMinimumSize() {
return minimumSize;
}

public void setSimilarityThreshold(int similarityThreshold) {
this.similarityThreshold = similarityThreshold;
}

public void setMinimumScore(int minimumScore) {
this.minimumScore = minimumScore;
}

public void setMinimumSize(int minimumSize) {
this.minimumSize = minimumSize;
}

/**
* Creates PlagiarismDetectionConfig with default data
*
* @return PlagiarismDetectionConfig with default values
*/
public static PlagiarismDetectionConfig createDefault() {
var config = new PlagiarismDetectionConfig();
config.setContinuousPlagiarismControlEnabled(false);
config.setContinuousPlagiarismControlPostDueDateChecksEnabled(false);
config.setSimilarityThreshold(90);
config.setMinimumScore(0);
config.setMinimumSize(50);
return config;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
if (!super.equals(o)) {
return false;
}
PlagiarismDetectionConfig that = (PlagiarismDetectionConfig) o;
return continuousPlagiarismControlEnabled == that.continuousPlagiarismControlEnabled
&& continuousPlagiarismControlPostDueDateChecksEnabled == that.continuousPlagiarismControlPostDueDateChecksEnabled
&& Float.compare(similarityThreshold, that.similarityThreshold) == 0 && minimumScore == that.minimumScore && minimumSize == that.minimumSize;
}

@Override
public int hashCode() {
return Objects.hash(super.hashCode(), continuousPlagiarismControlEnabled, continuousPlagiarismControlPostDueDateChecksEnabled, similarityThreshold, minimumScore,
minimumSize);
}

@Override
public String toString() {
return "PlagiarismDetectionConfig{continuousPlagiarismControlEnabled=" + continuousPlagiarismControlEnabled + ", continuousPlagiarismControlPostDueDateChecksEnabled="
+ continuousPlagiarismControlPostDueDateChecksEnabled + ", similarityThreshold=" + similarityThreshold + ", minimumScore=" + minimumScore + ", minimumSize="
+ minimumSize + '}';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,17 @@ GROUP BY TYPE(e)
List<ExerciseTypeMetricsEntry> countActiveStudentsInExercisesWithReleaseDateBetweenGroupByExerciseType(@Param("minDate") ZonedDateTime minDate,
@Param("maxDate") ZonedDateTime maxDate, @Param("activeUserLogins") List<String> activeUserLogins);

@Query("""
SELECT e FROM Exercise e
LEFT JOIN FETCH e.plagiarismDetectionConfig c
LEFT JOIN FETCH e.studentParticipations p
LEFT JOIN FETCH p.submissions s
LEFT JOIN FETCH s.results
WHERE e.dueDate >= :time
AND c.continuousPlagiarismControlEnabled = TRUE
""")
Set<Exercise> findAllExercisesWithDueDateOnOrAfterAndContinuousPlagiarismControlEnabledIsTrue(@Param("time") ZonedDateTime time);

@Query("""
SELECT e FROM Exercise e
WHERE e.course.testCourse = FALSE
Expand Down Expand Up @@ -458,6 +469,17 @@ default Set<Exercise> findAllExercisesWithCurrentOrUpcomingDueDate() {
return findAllExercisesWithCurrentOrUpcomingDueDate(ZonedDateTime.now().truncatedTo(ChronoUnit.DAYS));
}

/**
* Finds all exercises where the due date is yesterday, today or in the future and continuous plagiarism control is enabled
* (does not return exercises belonging to test courses).
*
* @return set of exercises
*/
default Set<Exercise> findAllExercisesWithDueDateOnOrAfterYesterdayAndContinuousPlagiarismControlEnabledIsTrue() {
var yesterday = ZonedDateTime.now().truncatedTo(ChronoUnit.DAYS).minusDays(1);
return findAllExercisesWithDueDateOnOrAfterAndContinuousPlagiarismControlEnabledIsTrue(yesterday);
}

/**
* Find exercise by exerciseId and load participations in this exercise.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,23 @@ public interface ModelingExerciseRepository extends JpaRepository<ModelingExerci
@EntityGraph(type = LOAD, attributePaths = { "exampleSubmissions", "teamAssignmentConfig", "categories", "competencies", "exampleSubmissions.submission.results" })
Optional<ModelingExercise> findWithEagerExampleSubmissionsAndCompetenciesById(@Param("exerciseId") Long exerciseId);

@Query("select modelingExercise from ModelingExercise modelingExercise left join fetch modelingExercise.exampleSubmissions exampleSubmissions left join fetch exampleSubmissions.submission submission left join fetch submission.results results left join fetch results.feedbacks left join fetch results.assessor left join fetch modelingExercise.teamAssignmentConfig where modelingExercise.id = :#{#exerciseId}")
Optional<ModelingExercise> findByIdWithExampleSubmissionsAndResults(@Param("exerciseId") Long exerciseId);
@EntityGraph(type = LOAD, attributePaths = { "exampleSubmissions", "teamAssignmentConfig", "categories", "competencies", "exampleSubmissions.submission.results",
"plagiarismDetectionConfig" })
Optional<ModelingExercise> findWithEagerExampleSubmissionsAndCompetenciesAndPlagiarismDetectionConfigById(@Param("exerciseId") Long exerciseId);

@Query("""
SELECT modelingExercise
FROM ModelingExercise modelingExercise
LEFT JOIN FETCH modelingExercise.exampleSubmissions exampleSubmissions
LEFT JOIN FETCH exampleSubmissions.submission submission
LEFT JOIN FETCH submission.results results
LEFT JOIN FETCH results.feedbacks
LEFT JOIN FETCH results.assessor
LEFT JOIN FETCH modelingExercise.teamAssignmentConfig
LEFT JOIN FETCH modelingExercise.plagiarismDetectionConfig
WHERE modelingExercise.id = :#{#exerciseId}
""")
Optional<ModelingExercise> findByIdWithExampleSubmissionsAndResultsAndPlagiarismDetectionConfig(@Param("exerciseId") Long exerciseId);

/**
* Get all modeling exercises that need to be scheduled: Those must satisfy one of the following requirements:
Expand Down Expand Up @@ -76,8 +91,14 @@ default ModelingExercise findWithEagerExampleSubmissionsAndCompetenciesByIdElseT
}

@NotNull
default ModelingExercise findByIdWithExampleSubmissionsAndResultsElseThrow(long exerciseId) {
return findByIdWithExampleSubmissionsAndResults(exerciseId).orElseThrow(() -> new EntityNotFoundException("Modeling Exercise", exerciseId));
default ModelingExercise findWithEagerExampleSubmissionsAndCompetenciesAndPlagiarismDetectionConfigByIdElseThrow(long exerciseId) {
return findWithEagerExampleSubmissionsAndCompetenciesAndPlagiarismDetectionConfigById(exerciseId)
.orElseThrow(() -> new EntityNotFoundException("Modeling Exercise", exerciseId));
}

@NotNull
default ModelingExercise findByIdWithExampleSubmissionsAndResultsAndPlagiarismDetectionConfigElseThrow(long exerciseId) {
return findByIdWithExampleSubmissionsAndResultsAndPlagiarismDetectionConfig(exerciseId).orElseThrow(() -> new EntityNotFoundException("Modeling Exercise", exerciseId));
}

@NotNull
Expand Down
Loading

0 comments on commit 4bf9968

Please sign in to comment.