From b82e9e6c3ec129317df3ecf032b5166f154cae47 Mon Sep 17 00:00:00 2001 From: Sehmuel Wagner <111253615+sachmii@users.noreply.github.com> Date: Tue, 20 Jan 2026 11:31:53 +0100 Subject: [PATCH 1/4] refine user data export --- .../exportdata/ApplicantDataExportDTO.java | 3 +- .../exportdata/InterviewProcessExportDTO.java | 6 + .../exportdata/InterviewSlotExportDTO.java | 2 +- .../dto/exportdata/IntervieweeExportDTO.java | 7 + .../aet/core/dto/exportdata/StaffDataDTO.java | 4 +- .../core/service/UserDataExportService.java | 97 +++++++++++-- .../InterviewProcessRepository.java | 3 +- .../repository/InterviewSlotRepository.java | 16 +++ .../repository/IntervieweeRepository.java | 17 +++ .../core/web/UserDataExportResourceTest.java | 127 +++++++++++++++++- 10 files changed, 268 insertions(+), 14 deletions(-) create mode 100644 src/main/java/de/tum/cit/aet/core/dto/exportdata/InterviewProcessExportDTO.java create mode 100644 src/main/java/de/tum/cit/aet/core/dto/exportdata/IntervieweeExportDTO.java diff --git a/src/main/java/de/tum/cit/aet/core/dto/exportdata/ApplicantDataExportDTO.java b/src/main/java/de/tum/cit/aet/core/dto/exportdata/ApplicantDataExportDTO.java index f5c02fc8b4..f83e17a14b 100644 --- a/src/main/java/de/tum/cit/aet/core/dto/exportdata/ApplicantDataExportDTO.java +++ b/src/main/java/de/tum/cit/aet/core/dto/exportdata/ApplicantDataExportDTO.java @@ -21,5 +21,6 @@ public record ApplicantDataExportDTO( String masterGrade, String masterUniversity, Set documents, - List applications + List applications, + List interviewees ) {} diff --git a/src/main/java/de/tum/cit/aet/core/dto/exportdata/InterviewProcessExportDTO.java b/src/main/java/de/tum/cit/aet/core/dto/exportdata/InterviewProcessExportDTO.java new file mode 100644 index 0000000000..15cc944ea8 --- /dev/null +++ b/src/main/java/de/tum/cit/aet/core/dto/exportdata/InterviewProcessExportDTO.java @@ -0,0 +1,6 @@ +package de.tum.cit.aet.core.dto.exportdata; + +import com.fasterxml.jackson.annotation.JsonInclude; + +@JsonInclude(JsonInclude.Include.NON_NULL) +public record InterviewProcessExportDTO(String jobTitle) {} diff --git a/src/main/java/de/tum/cit/aet/core/dto/exportdata/InterviewSlotExportDTO.java b/src/main/java/de/tum/cit/aet/core/dto/exportdata/InterviewSlotExportDTO.java index 46cb02bfc9..3f3264edf3 100644 --- a/src/main/java/de/tum/cit/aet/core/dto/exportdata/InterviewSlotExportDTO.java +++ b/src/main/java/de/tum/cit/aet/core/dto/exportdata/InterviewSlotExportDTO.java @@ -4,4 +4,4 @@ import java.time.Instant; @JsonInclude(JsonInclude.Include.NON_NULL) -public record InterviewSlotExportDTO(Instant start, Instant end) {} +public record InterviewSlotExportDTO(String jobTitle, Instant start, Instant end, String location, String streamLink, Boolean isBooked) {} diff --git a/src/main/java/de/tum/cit/aet/core/dto/exportdata/IntervieweeExportDTO.java b/src/main/java/de/tum/cit/aet/core/dto/exportdata/IntervieweeExportDTO.java new file mode 100644 index 0000000000..f952b29235 --- /dev/null +++ b/src/main/java/de/tum/cit/aet/core/dto/exportdata/IntervieweeExportDTO.java @@ -0,0 +1,7 @@ +package de.tum.cit.aet.core.dto.exportdata; + +import com.fasterxml.jackson.annotation.JsonInclude; +import java.time.Instant; + +@JsonInclude(JsonInclude.Include.NON_NULL) +public record IntervieweeExportDTO(String jobTitle, Instant lastInvited) {} diff --git a/src/main/java/de/tum/cit/aet/core/dto/exportdata/StaffDataDTO.java b/src/main/java/de/tum/cit/aet/core/dto/exportdata/StaffDataDTO.java index c744897029..14ef178a70 100644 --- a/src/main/java/de/tum/cit/aet/core/dto/exportdata/StaffDataDTO.java +++ b/src/main/java/de/tum/cit/aet/core/dto/exportdata/StaffDataDTO.java @@ -9,5 +9,7 @@ public record StaffDataDTO( List researchGroupRoles, List reviews, List comments, - List ratings + List ratings, + List interviewProcesses, + List interviewSlots ) {} diff --git a/src/main/java/de/tum/cit/aet/core/service/UserDataExportService.java b/src/main/java/de/tum/cit/aet/core/service/UserDataExportService.java index f7f87c505c..fa8b75988b 100644 --- a/src/main/java/de/tum/cit/aet/core/service/UserDataExportService.java +++ b/src/main/java/de/tum/cit/aet/core/service/UserDataExportService.java @@ -8,6 +8,9 @@ import de.tum.cit.aet.core.dto.exportdata.ApplicationReviewExportDTO; import de.tum.cit.aet.core.dto.exportdata.DocumentExportDTO; import de.tum.cit.aet.core.dto.exportdata.InternalCommentExportDTO; +import de.tum.cit.aet.core.dto.exportdata.InterviewProcessExportDTO; +import de.tum.cit.aet.core.dto.exportdata.InterviewSlotExportDTO; +import de.tum.cit.aet.core.dto.exportdata.IntervieweeExportDTO; import de.tum.cit.aet.core.dto.exportdata.RatingExportDTO; import de.tum.cit.aet.core.dto.exportdata.ResearchGroupRoleExportDTO; import de.tum.cit.aet.core.dto.exportdata.StaffDataDTO; @@ -21,6 +24,12 @@ import de.tum.cit.aet.evaluation.repository.ApplicationReviewRepository; import de.tum.cit.aet.evaluation.repository.InternalCommentRepository; import de.tum.cit.aet.evaluation.repository.RatingRepository; +import de.tum.cit.aet.interview.domain.InterviewProcess; +import de.tum.cit.aet.interview.domain.InterviewSlot; +import de.tum.cit.aet.interview.domain.Interviewee; +import de.tum.cit.aet.interview.repository.InterviewProcessRepository; +import de.tum.cit.aet.interview.repository.InterviewSlotRepository; +import de.tum.cit.aet.interview.repository.IntervieweeRepository; import de.tum.cit.aet.job.domain.Job; import de.tum.cit.aet.notification.dto.EmailSettingDTO; import de.tum.cit.aet.notification.repository.EmailSettingRepository; @@ -50,17 +59,21 @@ @RequiredArgsConstructor public class UserDataExportService { - private final UserRepository userRepository; private final ApplicantRepository applicantRepository; - private final UserSettingRepository userSettingRepository; - private final EmailSettingRepository emailSettingRepository; private final ApplicationRepository applicationRepository; - private final DocumentDictionaryRepository documentDictionaryRepository; - private final UserResearchGroupRoleRepository userResearchGroupRoleRepository; private final ApplicationReviewRepository applicationReviewRepository; + private final DocumentDictionaryRepository documentDictionaryRepository; private final InternalCommentRepository internalCommentRepository; - private final RatingRepository ratingRepository; + private final IntervieweeRepository intervieweeRepository; + private final InterviewProcessRepository interviewProcessRepository; + private final InterviewSlotRepository interviewSlotRepository; private final DocumentRepository documentRepository; + private final EmailSettingRepository emailSettingRepository; + private final RatingRepository ratingRepository; + private final UserRepository userRepository; + private final UserResearchGroupRoleRepository userResearchGroupRoleRepository; + private final UserSettingRepository userSettingRepository; + private final ZipExportService zipExportService; private final ObjectMapper objectMapper; private final PlatformTransactionManager transactionManager; @@ -184,6 +197,8 @@ private ApplicantDataExportDTO getApplicantData(UUID userId) { ) .toList(); + List interviewees = getInterviewees(userId); + return new ApplicantDataExportDTO( applicant.getStreet(), applicant.getPostalCode(), @@ -200,7 +215,8 @@ private ApplicantDataExportDTO getApplicantData(UUID userId) { applicant.getMasterGrade(), applicant.getMasterUniversity(), documents, - applications + applications, + interviewees ); } @@ -211,12 +227,64 @@ private StaffDataDTO getStaffData(User user) { List reviews = getReviews(user); List comments = getComments(user); List ratings = getRatings(user); + List interviewProcessEntities = getInterviewProcesses(user); + List interviewProcesses = mapInterviewProcesses(interviewProcessEntities); + List interviewSlots = getInterviewSlots(interviewProcessEntities); - if (supervisedJobs.isEmpty() && researchGroupRoles.isEmpty() && reviews.isEmpty() && comments.isEmpty() && ratings.isEmpty()) { + if ( + supervisedJobs.isEmpty() && + researchGroupRoles.isEmpty() && + reviews.isEmpty() && + comments.isEmpty() && + ratings.isEmpty() && + interviewProcesses.isEmpty() && + interviewSlots.isEmpty() + ) { return null; } - return new StaffDataDTO(supervisedJobs, researchGroupRoles, reviews, comments, ratings); + return new StaffDataDTO(supervisedJobs, researchGroupRoles, reviews, comments, ratings, interviewProcesses, interviewSlots); + } + + private List getInterviewees(UUID userId) { + List interviewees = intervieweeRepository.findByApplicantUserIdWithDetails(userId); + if (interviewees == null || interviewees.isEmpty()) { + return List.of(); + } + + return interviewees + .stream() + .map(interviewee -> + new IntervieweeExportDTO(interviewee.getInterviewProcess().getJob().getTitle(), interviewee.getLastInvited()) + ) + .toList(); + } + + private List getInterviewProcesses(User user) { + List processes = interviewProcessRepository.findAllByProfessorId(user.getUserId()); + return processes == null ? List.of() : processes; + } + + private List mapInterviewProcesses(List processes) { + if (processes == null || processes.isEmpty()) { + return List.of(); + } + return processes + .stream() + .map(process -> new InterviewProcessExportDTO(process.getJob().getTitle())) + .toList(); + } + + private List getInterviewSlots(List interviewProcesses) { + if (interviewProcesses == null || interviewProcesses.isEmpty()) { + return List.of(); + } + List processIds = interviewProcesses.stream().map(InterviewProcess::getId).toList(); + List slots = interviewSlotRepository.findByInterviewProcessIdInWithJob(processIds); + if (slots == null || slots.isEmpty()) { + return List.of(); + } + return slots.stream().map(this::mapInterviewSlot).toList(); } private List getResearchGroupRoles(User user) { @@ -284,6 +352,17 @@ private List getRatings(User user) { .toList(); } + private InterviewSlotExportDTO mapInterviewSlot(InterviewSlot slot) { + return new InterviewSlotExportDTO( + slot.getInterviewProcess().getJob().getTitle(), + slot.getStartDateTime(), + slot.getEndDateTime(), + slot.getLocation(), + slot.getStreamLink(), + slot.getIsBooked() + ); + } + private void addDocumentToZip(ZipOutputStream zipOut, UUID documentId, String entryPath) { try { Document document = documentRepository diff --git a/src/main/java/de/tum/cit/aet/interview/repository/InterviewProcessRepository.java b/src/main/java/de/tum/cit/aet/interview/repository/InterviewProcessRepository.java index e3f903cccf..77ec217b6d 100644 --- a/src/main/java/de/tum/cit/aet/interview/repository/InterviewProcessRepository.java +++ b/src/main/java/de/tum/cit/aet/interview/repository/InterviewProcessRepository.java @@ -21,7 +21,8 @@ public interface InterviewProcessRepository extends JpaRepository findAllByProfessorId(@Param("professorId") UUID professorId); diff --git a/src/main/java/de/tum/cit/aet/interview/repository/InterviewSlotRepository.java b/src/main/java/de/tum/cit/aet/interview/repository/InterviewSlotRepository.java index cec4492894..76480c3065 100644 --- a/src/main/java/de/tum/cit/aet/interview/repository/InterviewSlotRepository.java +++ b/src/main/java/de/tum/cit/aet/interview/repository/InterviewSlotRepository.java @@ -45,6 +45,22 @@ public interface InterviewSlotRepository extends JpaRepository findByIdWithJob(@Param("slotId") UUID slotId); + /** + * Finds all slots for the given interview process ids with job data. + * + * @param processIds interview process ids + * @return list of slots ordered by start time + */ + @EntityGraph(attributePaths = { "interviewProcess", "interviewProcess.job" }) + @Query( + """ + SELECT s FROM InterviewSlot s + WHERE s.interviewProcess.id IN :processIds + ORDER BY s.startDateTime ASC + """ + ) + List findByInterviewProcessIdInWithJob(@Param("processIds") List processIds); + /** * Counts all interview slots associated with a specific interview process. * Finds all interview slots of a given professor that overlap with a specified diff --git a/src/main/java/de/tum/cit/aet/interview/repository/IntervieweeRepository.java b/src/main/java/de/tum/cit/aet/interview/repository/IntervieweeRepository.java index 09287a5815..a39009d4ff 100644 --- a/src/main/java/de/tum/cit/aet/interview/repository/IntervieweeRepository.java +++ b/src/main/java/de/tum/cit/aet/interview/repository/IntervieweeRepository.java @@ -95,6 +95,23 @@ public interface IntervieweeRepository extends TumApplyJpaRepository findByInterviewProcessIdInWithSlots(@Param("processIds") List processIds); + /** + * Finds all interviewees for a given applicant user id with process, job and slot data. + * + * @param userId the applicant user id + * @return list of interviewees with process/job and slot data + */ + @Query( + """ + SELECT DISTINCT i FROM Interviewee i + JOIN FETCH i.interviewProcess ip + JOIN FETCH ip.job j + LEFT JOIN FETCH i.slots + WHERE i.application.applicant.user.userId = :userId + """ + ) + List findByApplicantUserIdWithDetails(@Param("userId") UUID userId); + /** * Finds a single interviewee by ID within a process. * diff --git a/src/test/java/de/tum/cit/aet/core/web/UserDataExportResourceTest.java b/src/test/java/de/tum/cit/aet/core/web/UserDataExportResourceTest.java index 5d4e790825..d86b4afaae 100644 --- a/src/test/java/de/tum/cit/aet/core/web/UserDataExportResourceTest.java +++ b/src/test/java/de/tum/cit/aet/core/web/UserDataExportResourceTest.java @@ -9,6 +9,12 @@ import de.tum.cit.aet.core.constants.DocumentType; import de.tum.cit.aet.core.repository.DocumentDictionaryRepository; import de.tum.cit.aet.core.repository.DocumentRepository; +import de.tum.cit.aet.interview.domain.InterviewProcess; +import de.tum.cit.aet.interview.domain.InterviewSlot; +import de.tum.cit.aet.interview.domain.Interviewee; +import de.tum.cit.aet.interview.repository.InterviewProcessRepository; +import de.tum.cit.aet.interview.repository.InterviewSlotRepository; +import de.tum.cit.aet.interview.repository.IntervieweeRepository; import de.tum.cit.aet.job.constants.JobState; import de.tum.cit.aet.job.domain.Job; import de.tum.cit.aet.job.repository.JobRepository; @@ -30,6 +36,8 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.nio.charset.StandardCharsets; +import java.time.Instant; +import java.time.LocalDate; import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -71,6 +79,15 @@ public class UserDataExportResourceTest extends AbstractResourceTest { @Autowired DocumentDictionaryRepository documentDictionaryRepository; + @Autowired + InterviewProcessRepository interviewProcessRepository; + + @Autowired + InterviewSlotRepository interviewSlotRepository; + + @Autowired + IntervieweeRepository intervieweeRepository; + @Autowired DatabaseCleaner databaseCleaner; @@ -151,7 +168,7 @@ void exportIncludesApplicantDocumentsInZip() throws Exception { researchGroup, "Published Role", JobState.PUBLISHED, - java.time.LocalDate.now().plusDays(7) + LocalDate.now().plusDays(7) ); Application application = ApplicationTestData.saved(applicationRepository, publishedJob, applicant, ApplicationState.SENT); @@ -190,6 +207,97 @@ void exportIncludesApplicantDocumentsInZip() throws Exception { assertThat(entries).contains("documents/" + expectedSanitized); } + @Test + void exportIncludesIntervieweesForApplicant() throws Exception { + ResearchGroup researchGroup = ResearchGroupTestData.saved(researchGroupRepository); + User professor = UserTestData.savedProfessor(userRepository, researchGroup); + + Applicant applicant = ApplicantTestData.savedWithNewUser(applicantRepository); + User applicantUser = applicant.getUser(); + + Job job = JobTestData.saved( + jobRepository, + professor, + researchGroup, + "Interview Role", + JobState.PUBLISHED, + LocalDate.now().plusDays(7) + ); + Application application = ApplicationTestData.savedSent(applicationRepository, job, applicant); + + InterviewProcess process = new InterviewProcess(); + process.setJob(job); + process = interviewProcessRepository.save(process); + + Interviewee interviewee = new Interviewee(); + interviewee.setInterviewProcess(process); + interviewee.setApplication(application); + interviewee.setLastInvited(Instant.parse("2025-01-01T10:00:00Z")); + interviewee = intervieweeRepository.save(interviewee); + + InterviewSlot slot = new InterviewSlot(); + slot.setInterviewProcess(process); + slot.setInterviewee(interviewee); + slot.setStartDateTime(Instant.now().plusSeconds(3600)); + slot.setEndDateTime(Instant.now().plusSeconds(7200)); + slot.setLocation("Room 101"); + slot.setStreamLink("https://stream.test/slot"); + slot.setIsBooked(true); + interviewSlotRepository.save(slot); + + byte[] zipBytes = api + .with(JwtPostProcessors.jwtUser(applicantUser.getUserId(), "ROLE_APPLICANT")) + .getAndReturnBytes(API_URL, Map.of(), 200, MediaType.valueOf("application/zip")); + + String summaryJson = extractSummaryJson(zipBytes); + + assertThat(summaryJson).contains("\"interviewees\"").contains("\"Interview Role\"").contains("\"lastInvited\""); + assertThat(summaryJson).doesNotContain("\"interviewProcessId\"").doesNotContain("\"interviewSlotId\"").doesNotContain("\"jobId\""); + } + + @Test + void exportIncludesInterviewProcessesAndSlotsForStaff() throws Exception { + ResearchGroup researchGroup = ResearchGroupTestData.saved(researchGroupRepository); + User professor = UserTestData.savedProfessor(userRepository, researchGroup); + + Job job = JobTestData.saved( + jobRepository, + professor, + researchGroup, + "Staff Interview Role", + JobState.PUBLISHED, + LocalDate.now().plusDays(7) + ); + + InterviewProcess process = new InterviewProcess(); + process.setJob(job); + process = interviewProcessRepository.save(process); + + InterviewSlot slot = new InterviewSlot(); + slot.setInterviewProcess(process); + slot.setStartDateTime(Instant.now().plusSeconds(3600)); + slot.setEndDateTime(Instant.now().plusSeconds(7200)); + slot.setLocation("Room 202"); + slot.setStreamLink("https://stream.test/staff-slot"); + slot.setIsBooked(false); + interviewSlotRepository.save(slot); + + byte[] zipBytes = api + .with(JwtPostProcessors.jwtUser(professor.getUserId(), "ROLE_PROFESSOR")) + .getAndReturnBytes(API_URL, Map.of(), 200, MediaType.valueOf("application/zip")); + + String summaryJson = extractSummaryJson(zipBytes); + + assertThat(summaryJson) + .contains("\"interviewProcesses\"") + .contains("\"Staff Interview Role\"") + .contains("\"interviewSlots\"") + .contains("\"Room 202\"") + .doesNotContain("\"interviewProcessId\"") + .doesNotContain("\"interviewSlotId\"") + .doesNotContain("\"jobId\""); + } + @Test void exportReturns500WhenUserDoesNotExist() { UUID missingUserId = UUID.randomUUID(); @@ -201,4 +309,21 @@ void exportReturns500WhenUserDoesNotExist() { String bodyString = new String(body, StandardCharsets.UTF_8); assertThat(bodyString).contains("User data export failed"); } + + private String extractSummaryJson(byte[] zipBytes) throws Exception { + String summaryJson = null; + try (ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(zipBytes))) { + ZipEntry entry; + while ((entry = zis.getNextEntry()) != null) { + if ("user_data_summary.json".equals(entry.getName())) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + zis.transferTo(out); + summaryJson = out.toString(StandardCharsets.UTF_8); + } + zis.closeEntry(); + } + } + assertThat(summaryJson).isNotNull(); + return summaryJson; + } } From 99855d67cf66e9e013969a4e22471a2b05207680 Mon Sep 17 00:00:00 2001 From: Sehmuel Wagner <111253615+sachmii@users.noreply.github.com> Date: Tue, 20 Jan 2026 11:42:35 +0100 Subject: [PATCH 2/4] classify user --- .../core/service/UserDataExportService.java | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/main/java/de/tum/cit/aet/core/service/UserDataExportService.java b/src/main/java/de/tum/cit/aet/core/service/UserDataExportService.java index fa8b75988b..618cb2eab3 100644 --- a/src/main/java/de/tum/cit/aet/core/service/UserDataExportService.java +++ b/src/main/java/de/tum/cit/aet/core/service/UserDataExportService.java @@ -33,8 +33,10 @@ import de.tum.cit.aet.job.domain.Job; import de.tum.cit.aet.notification.dto.EmailSettingDTO; import de.tum.cit.aet.notification.repository.EmailSettingRepository; +import de.tum.cit.aet.usermanagement.constants.UserRole; import de.tum.cit.aet.usermanagement.domain.Applicant; import de.tum.cit.aet.usermanagement.domain.User; +import de.tum.cit.aet.usermanagement.domain.UserResearchGroupRole; import de.tum.cit.aet.usermanagement.repository.ApplicantRepository; import de.tum.cit.aet.usermanagement.repository.UserRepository; import de.tum.cit.aet.usermanagement.repository.UserResearchGroupRoleRepository; @@ -124,19 +126,29 @@ public void exportUserData(UUID userId, HttpServletResponse response) throws IOE } } + // ------------------------------------ Private helper methods ------------------------------------ + private UserDataExportDTO collectUserData(UUID userId) { User user = userRepository.findById(userId).orElseThrow(() -> new IllegalArgumentException("User not found")); + boolean hasApplicantRole = hasRole(user, UserRole.APPLICANT); + boolean hasStaffRole = hasRole(user, UserRole.PROFESSOR) || hasRole(user, UserRole.EMPLOYEE) || hasRole(user, UserRole.ADMIN); + UserProfileExportDTO profile = getUserProfile(user); List settings = getUserSettings(userId); List emailSettings = getEmailSettings(user); - ApplicantDataExportDTO applicantData = applicantRepository.existsById(userId) ? getApplicantData(userId) : null; - StaffDataDTO staffData = getStaffData(user); + ApplicantDataExportDTO applicantData = hasApplicantRole && applicantRepository.existsById(userId) ? getApplicantData(userId) : null; + StaffDataDTO staffData = hasStaffRole ? getStaffData(user) : null; return new UserDataExportDTO(profile, settings, emailSettings, applicantData, staffData); } - // Private helper methods + private boolean hasRole(User user, UserRole role) { + if (user.getResearchGroupRoles() == null || user.getResearchGroupRoles().isEmpty()) { + return false; + } + return user.getResearchGroupRoles().stream().map(UserResearchGroupRole::getRole).anyMatch(role::equals); + } private UserProfileExportDTO getUserProfile(User user) { return new UserProfileExportDTO( From f784f1ebf628940cfc8740263f422d8a3ea337be Mon Sep 17 00:00:00 2001 From: Sehmuel Wagner <111253615+sachmii@users.noreply.github.com> Date: Tue, 20 Jan 2026 11:43:47 +0100 Subject: [PATCH 3/4] add doc --- .../tum/cit/aet/core/service/UserDataExportService.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/de/tum/cit/aet/core/service/UserDataExportService.java b/src/main/java/de/tum/cit/aet/core/service/UserDataExportService.java index 618cb2eab3..4228e1f386 100644 --- a/src/main/java/de/tum/cit/aet/core/service/UserDataExportService.java +++ b/src/main/java/de/tum/cit/aet/core/service/UserDataExportService.java @@ -128,6 +128,14 @@ public void exportUserData(UUID userId, HttpServletResponse response) throws IOE // ------------------------------------ Private helper methods ------------------------------------ + /** + * Collects all exportable data for the given user, including profile, settings, email settings, + * and role-specific data (applicant or staff) when applicable. + * + * @param userId the ID of the user whose data should be collected + * @return a {@link UserDataExportDTO} containing all available export data for the user + * @throws IllegalArgumentException if the user cannot be found + */ private UserDataExportDTO collectUserData(UUID userId) { User user = userRepository.findById(userId).orElseThrow(() -> new IllegalArgumentException("User not found")); From 2d063395fef2aa04ff3c6ae1fe68076e3edf00c2 Mon Sep 17 00:00:00 2001 From: Sehmuel Wagner <111253615+sachmii@users.noreply.github.com> Date: Thu, 22 Jan 2026 18:50:02 +0100 Subject: [PATCH 4/4] fix include --- .../cit/aet/core/dto/exportdata/InterviewProcessExportDTO.java | 2 +- .../tum/cit/aet/core/dto/exportdata/InterviewSlotExportDTO.java | 2 +- .../tum/cit/aet/core/dto/exportdata/IntervieweeExportDTO.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/de/tum/cit/aet/core/dto/exportdata/InterviewProcessExportDTO.java b/src/main/java/de/tum/cit/aet/core/dto/exportdata/InterviewProcessExportDTO.java index 15cc944ea8..cda23e4db6 100644 --- a/src/main/java/de/tum/cit/aet/core/dto/exportdata/InterviewProcessExportDTO.java +++ b/src/main/java/de/tum/cit/aet/core/dto/exportdata/InterviewProcessExportDTO.java @@ -2,5 +2,5 @@ import com.fasterxml.jackson.annotation.JsonInclude; -@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonInclude(JsonInclude.Include.NON_EMPTY) public record InterviewProcessExportDTO(String jobTitle) {} diff --git a/src/main/java/de/tum/cit/aet/core/dto/exportdata/InterviewSlotExportDTO.java b/src/main/java/de/tum/cit/aet/core/dto/exportdata/InterviewSlotExportDTO.java index 3f3264edf3..90bffb695d 100644 --- a/src/main/java/de/tum/cit/aet/core/dto/exportdata/InterviewSlotExportDTO.java +++ b/src/main/java/de/tum/cit/aet/core/dto/exportdata/InterviewSlotExportDTO.java @@ -3,5 +3,5 @@ import com.fasterxml.jackson.annotation.JsonInclude; import java.time.Instant; -@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonInclude(JsonInclude.Include.NON_EMPTY) public record InterviewSlotExportDTO(String jobTitle, Instant start, Instant end, String location, String streamLink, Boolean isBooked) {} diff --git a/src/main/java/de/tum/cit/aet/core/dto/exportdata/IntervieweeExportDTO.java b/src/main/java/de/tum/cit/aet/core/dto/exportdata/IntervieweeExportDTO.java index f952b29235..7ed257e2ab 100644 --- a/src/main/java/de/tum/cit/aet/core/dto/exportdata/IntervieweeExportDTO.java +++ b/src/main/java/de/tum/cit/aet/core/dto/exportdata/IntervieweeExportDTO.java @@ -3,5 +3,5 @@ import com.fasterxml.jackson.annotation.JsonInclude; import java.time.Instant; -@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonInclude(JsonInclude.Include.NON_EMPTY) public record IntervieweeExportDTO(String jobTitle, Instant lastInvited) {}