Skip to content

Commit

Permalink
Development: Convert DTOs to records (#9385)
Browse files Browse the repository at this point in the history
  • Loading branch information
krusche authored Nov 10, 2024
1 parent 36a182b commit 110d047
Show file tree
Hide file tree
Showing 20 changed files with 372 additions and 555 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,6 @@ SELECT COUNT(c)
@EntityGraph(type = LOAD, attributePaths = { "result.participation", "result.submission", "result.assessor" })
List<Complaint> getAllByResult_Assessor_IdAndResult_Participation_Exercise_Course_Id(Long assessorId, Long courseId);

// Valid JPQL syntax. Only SCA fails to properly detect the types.
/**
* Get the number of Complaints for all tutors of a course
*
Expand All @@ -267,8 +266,8 @@ SELECT COUNT(c)
SELECT new de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardComplaintsDTO(
r.assessor.id,
COUNT(c),
SUM( CASE WHEN (c.accepted = TRUE ) THEN 1L ELSE 0L END),
SUM( CASE WHEN (c.accepted = TRUE) THEN e.maxPoints ELSE 0.0 END)
SUM(CASE WHEN c.accepted = TRUE THEN 1L ELSE 0L END),
CAST(SUM(CASE WHEN c.accepted = TRUE THEN e.maxPoints ELSE 0.0 END) AS double)
)
FROM Complaint c
JOIN c.result r
Expand All @@ -282,7 +281,6 @@ SELECT COUNT(c)
""")
List<TutorLeaderboardComplaintsDTO> findTutorLeaderboardComplaintsByCourseId(@Param("courseId") long courseId);

// Valid JPQL syntax. Only SCA fails to properly detect the types.
/**
* Get the number of Complaints for all tutors of an exercise
*
Expand All @@ -293,8 +291,8 @@ SELECT COUNT(c)
SELECT new de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardComplaintsDTO(
r.assessor.id,
COUNT(c),
SUM( CASE WHEN (c.accepted = TRUE ) THEN 1L ELSE 0L END),
SUM( CASE WHEN (c.accepted = TRUE) THEN e.maxPoints ELSE 0.0 END)
SUM(CASE WHEN c.accepted = TRUE THEN 1L ELSE 0L END),
CAST(SUM(CASE WHEN c.accepted = TRUE THEN e.maxPoints ELSE 0.0 END) AS double)
)
FROM Complaint c
JOIN c.result r
Expand All @@ -308,7 +306,6 @@ SELECT COUNT(c)
""")
List<TutorLeaderboardComplaintsDTO> findTutorLeaderboardComplaintsByExerciseId(@Param("exerciseId") long exerciseId);

// Valid JPQL syntax. Only SCA fails to properly detect the types.
/**
* Get the number of Complaints for all tutors of an exam
*
Expand All @@ -319,8 +316,8 @@ SELECT COUNT(c)
SELECT new de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardComplaintsDTO(
r.assessor.id,
COUNT(c),
SUM( CASE WHEN (c.accepted = TRUE ) THEN 1L ELSE 0L END),
SUM( CASE WHEN (c.accepted = TRUE) THEN e.maxPoints ELSE 0.0 END)
SUM(CASE WHEN c.accepted = TRUE THEN 1L ELSE 0L END),
CAST(SUM(CASE WHEN c.accepted = TRUE THEN e.maxPoints ELSE 0.0 END) AS double)
)
FROM Complaint c
JOIN c.result r
Expand All @@ -345,7 +342,7 @@ SELECT COUNT(c)
SELECT new de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardComplaintResponsesDTO(
cr.reviewer.id,
COUNT(c),
SUM(e.maxPoints)
SUM(CAST(e.maxPoints AS double))
)
FROM Complaint c
JOIN c.complaintResponse cr
Expand Down Expand Up @@ -422,8 +419,8 @@ SELECT COUNT(c)
SELECT new de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardMoreFeedbackRequestsDTO(
r.assessor.id,
COUNT(c),
SUM( CASE WHEN (c.accepted IS NULL) THEN 1L ELSE 0L END),
SUM( CASE WHEN (c.accepted IS NULL) THEN e.maxPoints ELSE 0.0 END)
SUM(CASE WHEN c.accepted IS NULL THEN 1L ELSE 0L END),
CAST(SUM(CASE WHEN c.accepted IS NULL THEN e.maxPoints ELSE 0.0 END) AS double)
)
FROM Complaint c
JOIN c.result r
Expand All @@ -447,8 +444,8 @@ SELECT COUNT(c)
SELECT new de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardMoreFeedbackRequestsDTO(
r.assessor.id,
COUNT(c),
SUM( CASE WHEN (c.accepted IS NULL) THEN 1L ELSE 0L END),
SUM( CASE WHEN (c.accepted IS NULL) THEN e.maxPoints ELSE 0.0 END)
SUM(CASE WHEN c.accepted IS NULL THEN 1L ELSE 0L END),
CAST(SUM(CASE WHEN c.accepted IS NULL THEN e.maxPoints ELSE 0.0 END) AS double)
)
FROM Complaint c
JOIN c.result r
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -618,7 +618,6 @@ else if (Boolean.FALSE.equals(ratedCount.rated())) {
""")
List<TutorLeaderboardAssessmentsDTO> findTutorLeaderboardAssessmentByExerciseId(@Param("exerciseId") long exerciseId);

// Valid JPQL syntax, only SCA is not able to parse it due to mixing primitive and object types
@Query("""
SELECT new de.tum.cit.aet.artemis.assessment.dto.tutor.TutorLeaderboardAssessmentsDTO(
r.assessor.id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,8 @@ public File exportRepository(long exerciseId, Long submissionId, RepositoryType
var programmingExercise = programmingExerciseRepository.findByIdElseThrow(exerciseId);
checkFeedbackSuggestionsOrAutomaticFeedbackEnabledElseThrow(programmingExercise);

var exportOptions = new RepositoryExportOptionsDTO();
exportOptions.setAnonymizeRepository(true);
exportOptions.setExportAllParticipants(false);
exportOptions.setFilterLateSubmissions(true);
exportOptions.setFilterLateSubmissionsDate(programmingExercise.getDueDate());
exportOptions.setFilterLateSubmissionsIndividualDueDate(false); // Athena currently does not support individual due dates
// Athena currently does not support individual due dates
var exportOptions = new RepositoryExportOptionsDTO(false, true, false, programmingExercise.getDueDate(), false, false, false, true, false);

if (!Files.exists(repoDownloadClonePath)) {
Files.createDirectories(repoDownloadClonePath);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,96 +8,21 @@
* This is a dto for the repository export options.
*/
@JsonInclude(JsonInclude.Include.NON_EMPTY)
// TODO: convert into a Record
public class RepositoryExportOptionsDTO {
public record RepositoryExportOptionsDTO(boolean exportAllParticipants, boolean filterLateSubmissions, boolean filterLateSubmissionsIndividualDueDate,
ZonedDateTime filterLateSubmissionsDate, boolean excludePracticeSubmissions, boolean addParticipantName, boolean combineStudentCommits, boolean anonymizeRepository,
boolean normalizeCodeStyle) {

private boolean exportAllParticipants;

private boolean filterLateSubmissions;

private boolean filterLateSubmissionsIndividualDueDate;

private ZonedDateTime filterLateSubmissionsDate;

private boolean excludePracticeSubmissions;

private boolean addParticipantName;

private boolean combineStudentCommits;

private boolean anonymizeRepository;

private boolean normalizeCodeStyle;

public boolean isExportAllParticipants() {
return exportAllParticipants;
}

public void setExportAllParticipants(boolean exportAllParticipants) {
this.exportAllParticipants = exportAllParticipants;
}

public boolean isFilterLateSubmissions() {
return filterLateSubmissions;
}

public void setFilterLateSubmissions(boolean filterLateSubmissions) {
this.filterLateSubmissions = filterLateSubmissions;
}

public boolean isFilterLateSubmissionsIndividualDueDate() {
return filterLateSubmissionsIndividualDueDate;
}

public void setFilterLateSubmissionsIndividualDueDate(boolean filterLateSubmissionsIndividualDueDate) {
this.filterLateSubmissionsIndividualDueDate = filterLateSubmissionsIndividualDueDate;
}

public ZonedDateTime getFilterLateSubmissionsDate() {
return filterLateSubmissionsDate;
}

public void setFilterLateSubmissionsDate(ZonedDateTime filterLateSubmissionsDate) {
this.filterLateSubmissionsDate = filterLateSubmissionsDate;
}

public boolean isExcludePracticeSubmissions() {
return excludePracticeSubmissions;
}

public void setExcludePracticeSubmissions(boolean excludePracticeSubmissions) {
this.excludePracticeSubmissions = excludePracticeSubmissions;
}

public boolean isAddParticipantName() {
return addParticipantName;
}

public void setAddParticipantName(boolean addParticipantName) {
this.addParticipantName = addParticipantName;
}

public boolean isCombineStudentCommits() {
return combineStudentCommits;
}

public void setCombineStudentCommits(boolean combineStudentCommits) {
this.combineStudentCommits = combineStudentCommits;
}

public boolean isAnonymizeRepository() {
return anonymizeRepository;
}

public void setAnonymizeRepository(boolean anonymizeRepository) {
this.anonymizeRepository = anonymizeRepository;
public RepositoryExportOptionsDTO() {
this(false, false, false, null, false, false, false, false, false);
}

public boolean isNormalizeCodeStyle() {
return normalizeCodeStyle;
public RepositoryExportOptionsDTO copyWith(boolean filterLateSubmissionsIndividualDueDate, ZonedDateTime filterLateSubmissionsDate) {
return new RepositoryExportOptionsDTO(exportAllParticipants, filterLateSubmissions, filterLateSubmissionsIndividualDueDate, filterLateSubmissionsDate,
excludePracticeSubmissions, addParticipantName, combineStudentCommits, anonymizeRepository, normalizeCodeStyle);
}

public void setNormalizeCodeStyle(boolean normalizeCodeStyle) {
this.normalizeCodeStyle = normalizeCodeStyle;
public RepositoryExportOptionsDTO copyWithAnonymizeRepository(boolean anonymizeRepository) {
return new RepositoryExportOptionsDTO(exportAllParticipants, filterLateSubmissions, filterLateSubmissionsIndividualDueDate, filterLateSubmissionsDate,
excludePracticeSubmissions, addParticipantName, combineStudentCommits, anonymizeRepository, normalizeCodeStyle);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,25 +72,26 @@ public void createDataExportsAndDeleteOldOnes() throws InterruptedException {
log.info("Creating data exports and deleting old ones");
Set<DataExport> successfulDataExports = Collections.synchronizedSet(new HashSet<>());
var dataExportsToBeCreated = dataExportRepository.findAllToBeCreated();
ExecutorService executor = Executors.newFixedThreadPool(10);
dataExportsToBeCreated.forEach(dataExport -> executor.execute(() -> createDataExport(dataExport, successfulDataExports)));
executor.shutdown();
ZonedDateTime thresholdDate = ZonedDateTime.now().minusDays(7);
var dataExportsToBeDeleted = dataExportRepository.findAllToBeDeleted(thresholdDate);
dataExportsToBeDeleted.forEach(this::deleteDataExport);
Optional<User> admin = userService.findInternalAdminUser();
if (admin.isEmpty()) {
log.warn("No internal admin user found. Cannot send email to admin about successful creation of data exports.");
return;
}
// This job runs at 4 am by default and the next scheduled job runs at 5 am, so we should allow 60 minutes for the creation.
// If the creation doesn't finish within 60 minutes, all pending exports will be picked up when the job runs the next time.
if (!executor.awaitTermination(60, java.util.concurrent.TimeUnit.MINUTES)) {
log.info("Not all pending data exports could be created within 60 minutes.");
executor.shutdownNow();
}
if (!successfulDataExports.isEmpty()) {
mailService.sendSuccessfulDataExportsEmailToAdmin(admin.get(), successfulDataExports);
try (ExecutorService executor = Executors.newFixedThreadPool(10)) {
dataExportsToBeCreated.forEach(dataExport -> executor.execute(() -> createDataExport(dataExport, successfulDataExports)));
executor.shutdown();
ZonedDateTime thresholdDate = ZonedDateTime.now().minusDays(7);
var dataExportsToBeDeleted = dataExportRepository.findAllToBeDeleted(thresholdDate);
dataExportsToBeDeleted.forEach(this::deleteDataExport);
Optional<User> admin = userService.findInternalAdminUser();
if (admin.isEmpty()) {
log.warn("No internal admin user found. Cannot send email to admin about successful creation of data exports.");
return;
}
// This job runs at 4 am by default and the next scheduled job runs at 5 am, so we should allow 60 minutes for the creation.
// If the creation doesn't finish within 60 minutes, all pending exports will be picked up when the job runs the next time.
if (!executor.awaitTermination(60, java.util.concurrent.TimeUnit.MINUTES)) {
log.info("Not all pending data exports could be created within 60 minutes.");
executor.shutdownNow();
}
if (!successfulDataExports.isEmpty()) {
mailService.sendSuccessfulDataExportsEmailToAdmin(admin.get(), successfulDataExports);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,13 +155,6 @@ public void createProgrammingExerciseExport(ProgrammingExercise programmingExerc
}
createSubmissionsResultsExport(programmingExercise, exerciseDir, user);
RepositoryExportOptionsDTO repositoryExportOptions = new RepositoryExportOptionsDTO();
repositoryExportOptions.setExportAllParticipants(false);
repositoryExportOptions.setAnonymizeRepository(false);
repositoryExportOptions.setFilterLateSubmissions(false);
repositoryExportOptions.setCombineStudentCommits(false);
repositoryExportOptions.setFilterLateSubmissionsIndividualDueDate(false);
repositoryExportOptions.setExcludePracticeSubmissions(false);
repositoryExportOptions.setNormalizeCodeStyle(false);
var listOfProgrammingExerciseParticipations = programmingExercise.getStudentParticipations().stream()
.filter(studentParticipation -> studentParticipation instanceof ProgrammingExerciseStudentParticipation)
.map(studentParticipation -> (ProgrammingExerciseStudentParticipation) studentParticipation).toList();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import java.util.Set;
import java.util.stream.Collectors;

import jakarta.annotation.Nullable;
import jakarta.validation.constraints.NotNull;

import org.springframework.context.annotation.Profile;
Expand Down Expand Up @@ -176,6 +177,7 @@ public Set<ZonedDateTime> getAllIndividualExamEndDates(Long examId) {
* @param exam the exam
* @return a set of all end dates. May return an empty set, if the exam has no start/end date or student exams cannot be found.
*/
@Nullable
public Set<ZonedDateTime> getAllIndividualExamEndDates(Exam exam) {
if (exam.getStartDate() == null) {
return null;
Expand All @@ -184,13 +186,24 @@ public Set<ZonedDateTime> getAllIndividualExamEndDates(Exam exam) {
return workingTimes.stream().map(timeInSeconds -> exam.getStartDate().plusSeconds(timeInSeconds)).collect(Collectors.toSet());
}

/**
* Returns the unlock date for the exam programming exercise.
* <p>
* The unlock date is the exam start date minus a certain amount of time to ensure that the exam is unlocked before the start date.
*
* @param exercise the programming exercise
* @return the unlock date or <code>null</code> if the exercise is not an exam exercise
*/
@Nullable
public static ZonedDateTime getExamProgrammingExerciseUnlockDate(ProgrammingExercise exercise) {
// TODO: can we guarantee that this is an exam exercise to avoid the null check and return?
if (!exercise.isExamExercise()) {
return null;
}
return getExamProgrammingExerciseUnlockDate(exercise.getExerciseGroup().getExam());
}

@NotNull
public static ZonedDateTime getExamProgrammingExerciseUnlockDate(Exam exam) {
// using start date minus 5 minutes here because unlocking will take some time.
return exam.getStartDate().minusMinutes(EXAM_START_WAIT_TIME_MINUTES);
Expand Down
Loading

0 comments on commit 110d047

Please sign in to comment.