diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a901d12fcb45897f87ed1fd4158b051c913efe29..2cb75d911af0d35dab1bcd0e151dfb3b6d66e82e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -15,9 +15,6 @@ variables: DOCKER_DRIVER: overlay2 DOCKER_TLS_CERTDIR: "/certs" - SAST_DISABLE_DIND: "false" - SAST_DEFAULT_ANALYZERS: "spotbugs" - # Configure mysql environment variables MYSQL_DATABASE: "$DB_NAME" MYSQL_USER: "$DB_NAME" diff --git a/CHANGELOG.md b/CHANGELOG.md index 389d0529602c73d12aefc4cc452e0b44489e1166..8ab34b8e162766436643accdfbc70ae8b870e1d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,8 +8,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added + ### Changed + +### Fixed + +## [2.1.1] + +### Added + +### Changed +* Job offers page now has a programme select before the user sees any job offers @rwbackx + ### Fixed +* Offer positions now works again @rwbackx +* FlexDelft export is now in the correct format @rwbackx ## [2.1.0] @@ -79,6 +92,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Job offer import now allows for creating editions @rbackx * Renamed declarations to contract requests @toberhuber * Hiring message visible when accepted and offered @toberhuber +* TA Training and Raise Requests only show programme specific requests @toberhuber +* All applications and all contract requests pages only show programme specific applications/declarations (including exports) @toberhuber +* Renames "Manage Defaults" to "Manage Programme" @toberhuber ### Fixed * Header icons now stack and align nicely as per wireframes @toberhuber @@ -106,6 +122,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Offering positions to people with open applications @toberhuber * EditionID being used as CohortID in FlexDelft exports @toberhuber * Notification of unhandled declarations didn't include rejected declarations @toberhuber +* Creating new editions when looking at the "all courses" page @toberhuber ### Deprecated * Removed person note model @toberhuber diff --git a/build.gradle.kts b/build.gradle.kts index b172033245934c977382496ca12031af92e8963e..7f1b1af85da08bf1d64388210ae0f150cbdce7fa 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,12 +4,12 @@ import nl.javadude.gradle.plugins.license.DownloadLicensesExtension import nl.javadude.gradle.plugins.license.LicenseExtension group = "nl.tudelft.tam" -version = "2.1.0" +version = "2.1.1" val javaVersion = JavaVersion.VERSION_17 -val labradoorVersion = "1.3.4" -val libradorVersion = "1.0.3-SNAPSHOT6" +val labradoorVersion = "1.3.5" +val libradorVersion = "1.0.3-SNAPSHOT7" val guavaVersion = "31.1-jre" val modelMapperVersion = "3.1.0" val jQueryVersion = "3.6.2" diff --git a/src/main/java/nl/tudelft/tam/DevDatabaseLoader.java b/src/main/java/nl/tudelft/tam/DevDatabaseLoader.java index 9b182377fcf9e50bfd86a3bfb694131b27fc063f..37ef3180ee406683a92d07e2e46a2b040544132f 100644 --- a/src/main/java/nl/tudelft/tam/DevDatabaseLoader.java +++ b/src/main/java/nl/tudelft/tam/DevDatabaseLoader.java @@ -91,6 +91,7 @@ public class DevDatabaseLoader { ExtraWork sqt_now_ew_grading; nl.tudelft.tam.model.Profile cseteacher1; + nl.tudelft.tam.model.Profile cseteacher2; nl.tudelft.tam.model.Profile csestudent1; nl.tudelft.tam.model.Profile csestudent2; @@ -221,13 +222,17 @@ public class DevDatabaseLoader { private void fetchPeople() { cseteacher1 = profileRepository.save(nl.tudelft.tam.model.Profile.builder() .id(3L).emailNotifications(false).build()); + cseteacher2 = profileRepository.save(nl.tudelft.tam.model.Profile.builder() + .id(4L).emailNotifications(false).build()); csestudent1 = profileRepository.save(nl.tudelft.tam.model.Profile.builder() .id(9L).emailNotifications(false).build()); csestudent2 = profileRepository.save(nl.tudelft.tam.model.Profile.builder() .id(10L).emailNotifications(false).build()); + // Teacher 1 => Coordinator CSE coordinatorRepository.save(Coordinator.builder().personId(cseteacher1.getId()).programId(1L).build()); - coordinatorRepository.save(Coordinator.builder().personId(cseteacher1.getId()).programId(2L).build()); + // Teacher 2 => Coordinator EE + coordinatorRepository.save(Coordinator.builder().personId(cseteacher2.getId()).programId(2L).build()); } private void initApplications() { diff --git a/src/main/java/nl/tudelft/tam/controller/ApplicationController.java b/src/main/java/nl/tudelft/tam/controller/ApplicationController.java index daeef31d2416aff6ddd159c23e159b80b682ffc2..eeb1d9d2f7ce8ae44e48cbbe0535f8c925d29a59 100644 --- a/src/main/java/nl/tudelft/tam/controller/ApplicationController.java +++ b/src/main/java/nl/tudelft/tam/controller/ApplicationController.java @@ -19,7 +19,9 @@ package nl.tudelft.tam.controller; import java.io.IOException; import java.time.LocalDateTime; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; import nl.tudelft.labracore.api.dto.ProgramDetailsDTO; @@ -125,9 +127,10 @@ public class ApplicationController { */ @PostMapping("submit/{id}") public String submitApplication(@AuthenticatedPerson Person person, @PathVariable Long id, - @RequestParam(required = false) String page) { + @RequestParam(required = false) String page, @RequestParam(required = false) Long program) { applicationService.submit(person.getId(), id); - return "redirect:/" + (page != null ? page : "job-offer/open"); + return "redirect:/" + + (page != null ? page : "job-offer/open" + (program == null ? "" : "?program=" + program)); } /** @@ -142,10 +145,21 @@ public class ApplicationController { public String retractApplication(@AuthenticatedPerson Person person, @PathVariable Long id, @RequestParam(required = false) String page, @RequestParam(required = false) String q, - @RequestParam(required = false) String tab) { + @RequestParam(required = false) String tab, + @RequestParam(required = false) String program) { applicationService.retract(person.getId(), id); - return "redirect:/" + (page != null ? page : "job-offer/open") + (q != null ? "?q=" + q : "") - + (tab != null ? "&tab=" + tab : ""); + + String url = page == null ? "job-offer/open" : page; + + Map<String, String> params = new HashMap<>(); + if (q != null) + params.put("q", q); + if (tab != null) + params.put("tab", tab); + if (program != null) + params.put("program", program); + + return "redirect:/" + url + getURLParamsString(params); } /** @@ -218,9 +232,29 @@ public class ApplicationController { @PreAuthorize("@authorisationService.offerExistsFor(#id)") public String acceptApplication(@AuthenticatedPerson Person person, @PathVariable Long id, @RequestParam(required = false) String q, - @RequestParam(required = false) String tab) { + @RequestParam(required = false) String tab, + @RequestParam(required = false) String program) { applicationService.accept(person.getId(), id); - return "redirect:/job-offer/open" + (q != null ? "?q=" + q : "") + (tab != null ? "&tab=" + tab : ""); + + Map<String, String> params = new HashMap<>(); + if (q != null) + params.put("q", q); + if (tab != null) + params.put("tab", tab); + if (program != null) + params.put("program", program); + + return "redirect:/job-offer/open" + getURLParamsString(params); + } + + private static String getURLParamsString(Map<String, String> params) { + StringBuilder result = new StringBuilder(); + char sep = '?'; + for (String param : params.keySet()) { + result.append(sep).append(param).append("=").append(params.get(param)); + sep = '&'; + } + return result.toString(); } // endregion diff --git a/src/main/java/nl/tudelft/tam/controller/JobOfferController.java b/src/main/java/nl/tudelft/tam/controller/JobOfferController.java index 2096ffca974015cc01aef14e167cfd1cf78fcb9f..cafd9a5b31482aa3d486f03eca880464d66d734d 100644 --- a/src/main/java/nl/tudelft/tam/controller/JobOfferController.java +++ b/src/main/java/nl/tudelft/tam/controller/JobOfferController.java @@ -25,9 +25,7 @@ import java.util.stream.Collectors; import javax.validation.Valid; -import nl.tudelft.labracore.api.dto.EditionDetailsDTO; -import nl.tudelft.labracore.api.dto.PersonIdDTO; -import nl.tudelft.labracore.api.dto.RoleEditionDetailsDTO; +import nl.tudelft.labracore.api.dto.*; import nl.tudelft.labracore.lib.security.user.AuthenticatedPerson; import nl.tudelft.labracore.lib.security.user.Person; import nl.tudelft.tam.dto.create.JobOfferCreateDTO; @@ -38,7 +36,6 @@ import nl.tudelft.tam.dto.view.details.EditionDetailsOffersWorkDTO; import nl.tudelft.tam.dto.view.details.JobOfferDetailsDTO; import nl.tudelft.tam.dto.view.summary.ApplicationStatusSummaryDTO; import nl.tudelft.tam.dto.view.summary.JobOfferSummaryDTO; -import nl.tudelft.tam.model.Coordinator; import nl.tudelft.tam.security.AuthorisationService; import nl.tudelft.tam.service.*; @@ -62,37 +59,40 @@ public class JobOfferController { // region class attributes @Autowired - JobOfferService jobOfferService; + private JobOfferService jobOfferService; @Autowired - ApplicationService applicationService; + private ApplicationService applicationService; @Autowired - EditionService editionService; + private EditionService editionService; @Autowired - CourseService courseService; + private CourseService courseService; @Autowired - CohortService cohortService; + private CohortService cohortService; @Autowired - CSVService csvService; + private CSVService csvService; @Autowired - PersonService personService; + private PersonService personService; @Autowired - AuthorisationService authorisationService; + private AuthorisationService authorisationService; @Autowired - CoordinatorService coordinatorService; + private CoordinatorService coordinatorService; @Autowired - CoordinatorDefaultService coordinatorDefaultService; + private CoordinatorDefaultService coordinatorDefaultService; @Autowired - ModelMapper mapper; + private ProgramService programService; + + @Autowired + private ModelMapper mapper; // endregion @@ -125,47 +125,35 @@ public class JobOfferController { */ @GetMapping("open") public String getOpenJobOffers(@AuthenticatedPerson Person person, + @RequestParam(required = false) Long program, @RequestParam(required = false) String tab, @RequestParam(required = false) String q, Model model) { List<ApplicationDetailsJobOfferDTO> applications = applicationService .getFilteredApplicationsForPerson(person.getId(), q); - List<JobOfferSummaryDTO> offers = jobOfferService.getFilteredOpen(q).stream() - .map(x -> (JobOfferSummaryDTO) x).collect(Collectors.toList()); - - // Also include closed jobs, but where the student can accept an offer! - // for (ApplicationDetailsJobOfferDTO app : applications) { - // if (app.getStatus().equals(Status.OFFERED)) { - // JobOfferSummaryDTO appsOffer = mapper.map(app.getJobOffer(), JobOfferSummaryDTO.class); - // if (!offers.contains(appsOffer)) { - // offers.add(appsOffer); - // } - // } - // } - - List<EditionDetailsOffersWorkDTO> editions = editionService - .combineOffersAndWorkWithEditions(offers, new ArrayList<>()); - - // Sort editions based on Cohort, exclude masters and add them to the end. - Comparator<EditionDetailsOffersWorkDTO> editionComparator = Comparator - .comparing(o -> o.getCohort().getName()); - editionComparator = editionComparator.reversed().thenComparing(EditionDetailsDTO::getStartDate) - .thenComparing(EditionDetailsDTO::getName); - - List<EditionDetailsOffersWorkDTO> sortedEditions = editions.stream() - .sorted(editionComparator) - .collect(Collectors.toList()); - - // Add all job offers to list for appForOffer to work correctly - for (ApplicationDetailsJobOfferDTO app : applications) { - JobOfferDetailsDTO jo = app.getJobOffer(); - offers.add(jo); + List<JobOfferDetailsDTO> offerDetails = jobOfferService.getFilteredOpen(q); + List<EditionDetailsOffersWorkDTO> editions = Collections.emptyList(); + if (program != null) { + Set<Long> coursesInProgram = courseService.getAllCoursesInProgram(program).stream() + .map(CourseSummaryDTO::getId).collect(Collectors.toSet()); + List<JobOfferSummaryDTO> offers = offerDetails.stream() + .filter(o -> coursesInProgram.contains(o.getEdition().getCourse().getId())) + .map(x -> (JobOfferSummaryDTO) x).collect(Collectors.toList()); + + // Sort editions based on Cohort, exclude masters and add them to the end. + Comparator<EditionDetailsOffersWorkDTO> editionComparator = Comparator + .<EditionDetailsOffersWorkDTO, String>comparing(o -> o.getCohort().getName()) + .reversed().thenComparing(EditionDetailsDTO::getStartDate) + .thenComparing(EditionDetailsDTO::getName); + editions = editionService + .combineOffersAndWorkWithEditions(offers, new ArrayList<>()) + .stream().sorted(editionComparator).toList(); } Map<Long, ApplicationStatusSummaryDTO> appForOffer = applicationService .getMapWithStatusAppOpenByIdForPerson( person.getId(), - offers.stream().map(JobOfferSummaryDTO::getId).collect(Collectors.toList())); + offerDetails.stream().map(JobOfferDetailsDTO::getId).collect(Collectors.toList())); if (tab == null || !List.of("offers", "applications").contains(tab)) { tab = "offers"; @@ -173,7 +161,9 @@ public class JobOfferController { model.addAttribute("tab", tab); model.addAttribute("applications", applications); - model.addAttribute("editions", sortedEditions); + model.addAttribute("programs", programService.getAllPrograms().stream() + .sorted(Comparator.comparing(ProgramSummaryDTO::getName)).toList()); + model.addAttribute("editions", editions); model.addAttribute("userApplications", appForOffer); return "job_offer/view_many"; } @@ -193,13 +183,21 @@ public class JobOfferController { @RequestParam(required = false) Boolean hidePrev, @RequestParam(required = false) String q, Model model) { - List<Long> editionIds = personService.getRolesForPerson(person.getId()) - .stream().filter(r -> r.getType().equals(RoleEditionDetailsDTO.TypeEnum.TEACHER)) - .map(r -> Objects.requireNonNull(r.getEdition()).getId()).collect(Collectors.toList()); + List<Long> managedEditionIds = editionService.getAllEditionIdsThatPersonIsAMangerOf(person.getId()); + List<CourseSummaryDTO> managedCourses = courseService + .getManagingCourses(new PersonIdDTO().id(person.getId())); + List<Long> programIdsOfManagedCourses = courseService.getCoursesById(managedCourses.stream() + .map(CourseSummaryDTO::getId) + .collect(Collectors.toList())).stream() + .map(x -> x.getProgram().getId()) + .collect(Collectors.toList()); + + // Filter by the search query List<EditionDetailsOffersWorkDTO> editions = editionService - .filter(editionService.getEditionsWithJobsAndWork(editionIds), q); + .filter(editionService.getEditionsWithJobsAndWork(managedEditionIds), q); + // Add the coordinator defaults to the edition objects coordinatorDefaultService.addDefaultsToEditionsWithJobsAndWork(editions); if (hidePrev == null || hidePrev) { @@ -212,9 +210,8 @@ public class JobOfferController { model.addAttribute("editions", editions); model.addAttribute("showAll", false); - model.addAttribute("managed", - courseService.getManagingCourses(new PersonIdDTO().id(person.getId()))); - model.addAttribute("cohorts", cohortService.getAllCohorts()); + model.addAttribute("managed", managedCourses); + model.addAttribute("cohorts", cohortService.getCohortsByProgrammeIds(programIdsOfManagedCourses)); return "job_offer/manage"; } @@ -234,20 +231,30 @@ public class JobOfferController { @RequestParam(required = false) Boolean hidePrev, @RequestParam(required = false) String q, Model model) { - Set<Long> coordinating = coordinatorService.findAllByPersonId(person.getId()).stream() - .map(Coordinator::getProgramId).collect(Collectors.toSet()); - List<EditionDetailsOffersWorkDTO> editions = editionService.filter( - editionService.getEditionsWithJobsAndWork( - editionService.getAllEditionIds()) - .stream() - .filter(e -> coordinating - .contains( - Objects.requireNonNull( - courseService.getOrThrow(e.getCourse().getId()).getProgram()) - .getId())) - .collect(Collectors.toList()), - q); + Set<Long> coordinatingProgramIds = coordinatorService.getProgramIdsByPersonId(person.getId()); + List<CourseSummaryDTO> coordinatingCourses = new ArrayList<>(); + List<Long> coordinatingEditions = new ArrayList<>(); + + for (Long id : coordinatingProgramIds) { + List<CourseSummaryDTO> coursesInProgram = courseService.getAllCoursesInProgram(id); + coordinatingCourses.addAll(coursesInProgram); + } + + for (CourseSummaryDTO course : coordinatingCourses) { + coordinatingEditions.addAll(editionService.getEditionsForCourse(course.getId())); + } + + List<CohortSummaryDTO> coordinatingCohorts = cohortService + .getCohortsByProgrammeIds(coordinatingProgramIds.stream().toList()); + + List<EditionDetailsOffersWorkDTO> editions = editionService + .getEditionsWithJobsAndWork(coordinatingEditions); + + // Filter by the search query + editions = editionService.filter(editions, q); + + // Add the coordinator defaults to the edition objects coordinatorDefaultService.addDefaultsToEditionsWithJobsAndWork(editions); if (hidePrev == null || hidePrev) { @@ -260,6 +267,8 @@ public class JobOfferController { model.addAttribute("editions", editions); model.addAttribute("showAll", true); + model.addAttribute("managed", coordinatingCourses); + model.addAttribute("cohorts", coordinatingCohorts); return "job_offer/manage"; } diff --git a/src/main/java/nl/tudelft/tam/controller/RaiseRequestController.java b/src/main/java/nl/tudelft/tam/controller/RaiseRequestController.java index ccd0639333e78269546a6a53ef814aa081e0f52a..92c842ec60c8629cbcebe3046c8e081d41df4c29 100644 --- a/src/main/java/nl/tudelft/tam/controller/RaiseRequestController.java +++ b/src/main/java/nl/tudelft/tam/controller/RaiseRequestController.java @@ -19,6 +19,7 @@ package nl.tudelft.tam.controller; import java.io.IOException; import java.util.AbstractMap; +import java.util.ArrayList; import java.util.List; import javax.transaction.Transactional; @@ -32,6 +33,7 @@ import nl.tudelft.tam.dto.view.details.RaiseRequestDetailsDTO; import nl.tudelft.tam.exception.FileHandlingException; import nl.tudelft.tam.exception.InvalidDatabaseEntryException; import nl.tudelft.tam.model.Profile; +import nl.tudelft.tam.service.CoordinatorService; import nl.tudelft.tam.service.RaiseRequestService; import org.springframework.beans.factory.annotation.Autowired; @@ -53,6 +55,9 @@ public class RaiseRequestController { @Autowired private RaiseRequestService raiseRequestService; + @Autowired + private CoordinatorService coordinatorService; + /** * Shows a list of open raise requests. * @@ -62,9 +67,14 @@ public class RaiseRequestController { */ @GetMapping("all") @PreAuthorize("@authorisationService.canEditPayScale()") - public String getSubmittedRaiseRequests(Model model) throws InvalidDatabaseEntryException { + public String getSubmittedRaiseRequests(Model model, @AuthenticatedPerson Person person) + throws InvalidDatabaseEntryException { + + List<Long> coordinatingProgramIds = new ArrayList<>( + coordinatorService.getProgramIdsByPersonId(person.getId())); - List<RaiseRequestDetailsDTO> raiseRequests = raiseRequestService.getAllSubmitted(); + List<RaiseRequestDetailsDTO> raiseRequests = raiseRequestService + .getAllSubmitted(coordinatingProgramIds); model.addAttribute("raiseRequests", raiseRequests); return "raise_request/submitted"; diff --git a/src/main/java/nl/tudelft/tam/controller/TrainingApprovalRequestController.java b/src/main/java/nl/tudelft/tam/controller/TrainingApprovalRequestController.java index 85d6440edc15cf1f7703e0545737b79eff16e177..9f33986222bef0fb3c8498c1c83a353509777665 100644 --- a/src/main/java/nl/tudelft/tam/controller/TrainingApprovalRequestController.java +++ b/src/main/java/nl/tudelft/tam/controller/TrainingApprovalRequestController.java @@ -18,6 +18,7 @@ package nl.tudelft.tam.controller; import java.util.AbstractMap; +import java.util.ArrayList; import java.util.List; import javax.transaction.Transactional; @@ -30,6 +31,7 @@ import nl.tudelft.tam.dto.id.ProfileIdDTO; import nl.tudelft.tam.dto.view.details.TrainingApprovalRequestDetailsDTO; import nl.tudelft.tam.exception.InvalidDatabaseEntryException; import nl.tudelft.tam.model.Profile; +import nl.tudelft.tam.service.CoordinatorService; import nl.tudelft.tam.service.TrainingApprovalRequestService; import org.springframework.beans.factory.annotation.Autowired; @@ -46,6 +48,9 @@ public class TrainingApprovalRequestController { @Autowired private TrainingApprovalRequestService trainingApprovalRequestService; + @Autowired + private CoordinatorService coordinatorService; + /** * Shows a list of open training approval requests. * @@ -55,10 +60,14 @@ public class TrainingApprovalRequestController { */ @GetMapping("all") @PreAuthorize("@authorisationService.canEditTaTraining()") - public String getSubmittedTrainingApprovalRequests(Model model) throws InvalidDatabaseEntryException { + public String getSubmittedTrainingApprovalRequests(Model model, @AuthenticatedPerson Person person) + throws InvalidDatabaseEntryException { + + List<Long> coordinatingProgramIds = new ArrayList<>( + coordinatorService.getProgramIdsByPersonId(person.getId())); List<TrainingApprovalRequestDetailsDTO> trainingApprovalRequests = trainingApprovalRequestService - .getAllSubmitted(); + .getAllSubmitted(coordinatingProgramIds); model.addAttribute("trainingApprovalRequests", trainingApprovalRequests); return "training_approval_request/submitted"; diff --git a/src/main/java/nl/tudelft/tam/dto/view/details/RaiseRequestDetailsDTO.java b/src/main/java/nl/tudelft/tam/dto/view/details/RaiseRequestDetailsDTO.java index ce483aa011bdc5935dc0b855c546ec4d504700d4..305fbc5cff1f01e875c33f1289b3324c59d105ca 100644 --- a/src/main/java/nl/tudelft/tam/dto/view/details/RaiseRequestDetailsDTO.java +++ b/src/main/java/nl/tudelft/tam/dto/view/details/RaiseRequestDetailsDTO.java @@ -17,6 +17,8 @@ */ package nl.tudelft.tam.dto.view.details; +import java.util.List; + import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @@ -33,4 +35,6 @@ public class RaiseRequestDetailsDTO extends RaiseRequestSummaryDTO { private PayScale currentPayScale; + private List<Long> relevantProgramIds; + } diff --git a/src/main/java/nl/tudelft/tam/dto/view/details/TrainingApprovalRequestDetailsDTO.java b/src/main/java/nl/tudelft/tam/dto/view/details/TrainingApprovalRequestDetailsDTO.java index 3a7bcdf01e33805ff5672d41e58df07a426ca3d4..ed072bac7c4c12d80e340d648c5ae52f9cf6f95f 100644 --- a/src/main/java/nl/tudelft/tam/dto/view/details/TrainingApprovalRequestDetailsDTO.java +++ b/src/main/java/nl/tudelft/tam/dto/view/details/TrainingApprovalRequestDetailsDTO.java @@ -17,6 +17,8 @@ */ package nl.tudelft.tam.dto.view.details; +import java.util.List; + import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @@ -30,4 +32,6 @@ public class TrainingApprovalRequestDetailsDTO extends TrainingApprovalRequestSu private PersonSummaryDTO person; + private List<Long> relevantProgramIds; + } diff --git a/src/main/java/nl/tudelft/tam/model/records/FlexDelftExport.java b/src/main/java/nl/tudelft/tam/model/records/FlexDelftExport.java index 36fc1edf9441b3ec64da1890bf1de9ef57371c9a..bf9fae6d8d62cc81553541c5c0782f522409f9b5 100644 --- a/src/main/java/nl/tudelft/tam/model/records/FlexDelftExport.java +++ b/src/main/java/nl/tudelft/tam/model/records/FlexDelftExport.java @@ -17,6 +17,8 @@ */ package nl.tudelft.tam.model.records; +import java.time.format.DateTimeFormatter; + import nl.tudelft.labracore.api.dto.EditionDetailsDTO; import nl.tudelft.labracore.api.dto.PersonSummaryDTO; import nl.tudelft.tam.model.*; @@ -39,6 +41,8 @@ public record FlexDelftExport(PersonSummaryDTO applicant, Profile applicantProfi edition, coordinatorDefault, approver, batch, remarks); } + private static final DateTimeFormatter exportDateFormatter = DateTimeFormatter.ofPattern("dd-MM-yyyy"); + /** * Generates the export array * @@ -55,8 +59,8 @@ public record FlexDelftExport(PersonSummaryDTO applicant, Profile applicantProfi coordinatorDefault.getCoordinatorContractName(), String.valueOf(jobOffer.getMaxHours()), applicantProfile.getPayScale().toString(), - String.valueOf(jobOffer.getContractStartDate()), - String.valueOf(jobOffer.getContractEndDate()), + jobOffer.getContractStartDate().format(exportDateFormatter), + jobOffer.getContractEndDate().format(exportDateFormatter), batch, remarks }; diff --git a/src/main/java/nl/tudelft/tam/repository/CoordinatorRepository.java b/src/main/java/nl/tudelft/tam/repository/CoordinatorRepository.java index a6c5006500c02b3d5a8d60c6cebd06180552a13c..437d65a5aebc358ca686e03604d8dfd481a5c046 100644 --- a/src/main/java/nl/tudelft/tam/repository/CoordinatorRepository.java +++ b/src/main/java/nl/tudelft/tam/repository/CoordinatorRepository.java @@ -63,4 +63,12 @@ public interface CoordinatorRepository extends JpaRepository<Coordinator, Long> */ boolean existsByPersonIdAndProgramId(Long personId, Long programId); + /** + * Gets all the coordinators for a given programme. + * + * @param programId The id of the programme + * @return The programme's coordinators + */ + List<Coordinator> findAllByProgramId(Long programId); + } diff --git a/src/main/java/nl/tudelft/tam/security/AuthorisationService.java b/src/main/java/nl/tudelft/tam/security/AuthorisationService.java index 47a8d772e274246e0e79f88cb271fdcf6604d734..98ff2e8aaa242fe41436e3b300935b315f17a554 100644 --- a/src/main/java/nl/tudelft/tam/security/AuthorisationService.java +++ b/src/main/java/nl/tudelft/tam/security/AuthorisationService.java @@ -224,7 +224,16 @@ public class AuthorisationService { * @return true if the user is authorised */ public boolean canEditTaTraining() { - return isCoordinatorOfAny(); // TODO make this programme specific + return isCoordinatorOfAny(); + } + + /** + * Checks whether the user is authorised to approve TA training. + * + * @return true if the user is authorised + */ + public boolean canEditTaTrainingForProgram(Long programId) { + return isCoordinatorForProgram(programId); } /** @@ -233,7 +242,16 @@ public class AuthorisationService { * @return true if the user is authorised */ public boolean canEditPayScale() { - return isCoordinatorOfAny(); // TODO make this programme specific + return isCoordinatorOfAny(); + } + + /** + * Checks whether the user is authorised to edit a RaiseRequest claim. + * + * @return true if the user is authorised + */ + public boolean canEditPayScaleForProgram(Long programId) { + return isCoordinatorForProgram(programId); } /** diff --git a/src/main/java/nl/tudelft/tam/service/ApplicationService.java b/src/main/java/nl/tudelft/tam/service/ApplicationService.java index 1d3120f4be75ce8f8f63eaac3b92ebda0f189eb6..703b92bc5a15262ae31fcd0947426cee1befca4f 100644 --- a/src/main/java/nl/tudelft/tam/service/ApplicationService.java +++ b/src/main/java/nl/tudelft/tam/service/ApplicationService.java @@ -624,13 +624,18 @@ public class ApplicationService { .getEditionsById( applications.stream().map(a -> a.getJobOffer().getEditionId()).distinct().toList()) .stream().collect(Collectors.toMap(EditionDetailsDTO::getId, Function.identity())); - Map<Long, CourseDetailsDTO> courses = courseService - .getCoursesById( - editions.values().stream().map(e -> e.getCourse().getId()).distinct().toList()) - .stream().collect(Collectors.toMap(CourseDetailsDTO::getId, Function.identity())); - return applications.stream().filter(a -> programId.equals(courses - .get(editions.get(a.getJobOffer().getEditionId()).getCourse().getId()).getProgram().getId())) - .toList(); + if (!applications.isEmpty()) { + Map<Long, CourseDetailsDTO> courses = courseService + .getCoursesById( + editions.values().stream().map(e -> e.getCourse().getId()).distinct().toList()) + .stream().collect(Collectors.toMap(CourseDetailsDTO::getId, Function.identity())); + return applications.stream().filter(a -> programId.equals(courses + .get(editions.get(a.getJobOffer().getEditionId()).getCourse().getId()).getProgram() + .getId())) + .toList(); + } else { + return List.of(); + } } /** diff --git a/src/main/java/nl/tudelft/tam/service/CohortService.java b/src/main/java/nl/tudelft/tam/service/CohortService.java index 672347fffebc1d1eea6dc1f86fd73b661ec95b0b..0877bc4d0c05378626fae5645e15ed7a3cfcb8e5 100644 --- a/src/main/java/nl/tudelft/tam/service/CohortService.java +++ b/src/main/java/nl/tudelft/tam/service/CohortService.java @@ -17,7 +17,9 @@ */ package nl.tudelft.tam.service; +import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.stream.Collectors; import nl.tudelft.labracore.api.CohortControllerApi; @@ -75,4 +77,19 @@ public class CohortService { ids = ids.stream().distinct().collect(Collectors.toList()); return cohortCache.get(ids); } + + /** + * Gets all the cohorts for a list of programmes + * + * @param programmeIds The list of programme ids + * @return The list of cohorts + */ + public List<CohortSummaryDTO> getCohortsByProgrammeIds(List<Long> programmeIds) { + List<CohortSummaryDTO> result = new ArrayList<>(); + for (Long id : programmeIds) { + result.addAll(Objects + .requireNonNull(cohortControllerApi.getAllCohortsInProgram(id).collectList().block())); + } + return result; + } } diff --git a/src/main/java/nl/tudelft/tam/service/CoordinatorService.java b/src/main/java/nl/tudelft/tam/service/CoordinatorService.java index b0a0bf830b161a0bfed93953feef7811eaf7cd74..41bba00c11d95e84d4692f7cb63097f998337ea8 100644 --- a/src/main/java/nl/tudelft/tam/service/CoordinatorService.java +++ b/src/main/java/nl/tudelft/tam/service/CoordinatorService.java @@ -18,6 +18,8 @@ package nl.tudelft.tam.service; import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; import nl.tudelft.tam.model.Coordinator; import nl.tudelft.tam.repository.CoordinatorRepository; @@ -31,6 +33,12 @@ public class CoordinatorService { @Autowired CoordinatorRepository coordinatorRepository; + @Autowired + EditionService editionService; + + @Autowired + CourseService courseService; + /** * Finds all coordinator objects by person id * @@ -61,4 +69,27 @@ public class CoordinatorService { public boolean existsByPersonIdAndProgramId(Long personId, Long programId) { return coordinatorRepository.existsByPersonIdAndProgramId(personId, programId); } + + /** + * Finds all program ids by person id + * + * @param personId The person id to search by + * @return The list of program ids + */ + public Set<Long> getProgramIdsByPersonId(Long personId) { + return coordinatorRepository.findAllByPersonId(personId).stream() + .map(Coordinator::getProgramId).collect(Collectors.toSet()); + } + + /** + * Finds all coordinators by edition id + * + * @param editionId The edition id to search by + * @return The list of coordinators + */ + public List<Coordinator> getCoordinatorsByEditionId(Long editionId) { + Long courseId = editionService.getEditionById(editionId).getCourse().getId(); + Long programId = courseService.getOrThrow(courseId).getProgram().getId(); + return coordinatorRepository.findAllByProgramId(programId); + } } diff --git a/src/main/java/nl/tudelft/tam/service/CourseService.java b/src/main/java/nl/tudelft/tam/service/CourseService.java index 22ae0dfade5938b7320a2b7ab8f09ac7d6229246..68413987b608ad70f94d0183be2a2cd59ad7d46d 100644 --- a/src/main/java/nl/tudelft/tam/service/CourseService.java +++ b/src/main/java/nl/tudelft/tam/service/CourseService.java @@ -78,4 +78,14 @@ public class CourseService { public List<CourseSummaryDTO> getAllCourses() { return courseApi.getAllCourses().collectList().block(); } + + /** + * Gets all courses in a program. + * + * @param programId The id of the program + * @return The list of course summaries + */ + public List<CourseSummaryDTO> getAllCoursesInProgram(Long programId) { + return courseApi.getAllCoursesByProgram(programId).collectList().block(); + } } diff --git a/src/main/java/nl/tudelft/tam/service/EditionService.java b/src/main/java/nl/tudelft/tam/service/EditionService.java index d64ee8960a80054ab0e1031332c679d969309025..fe90aa7d9bee4d748b0510b877ab7131b561a303 100644 --- a/src/main/java/nl/tudelft/tam/service/EditionService.java +++ b/src/main/java/nl/tudelft/tam/service/EditionService.java @@ -27,6 +27,7 @@ import nl.tudelft.labracore.api.EditionControllerApi; import nl.tudelft.labracore.api.dto.EditionCreateDTO; import nl.tudelft.labracore.api.dto.EditionDetailsDTO; import nl.tudelft.labracore.api.dto.EditionSummaryDTO; +import nl.tudelft.labracore.api.dto.RoleEditionDetailsDTO; import nl.tudelft.tam.cache.EditionCacheManager; import nl.tudelft.tam.dto.view.details.EditionDetailsOffersWorkDTO; import nl.tudelft.tam.dto.view.summary.ExtraWorkSummaryDTO; @@ -50,6 +51,9 @@ public class EditionService { @Autowired ExtraWorkService extraWorkService; + @Autowired + PersonService personService; + @Autowired EditionControllerApi editionControllerApi; @@ -242,5 +246,28 @@ public class EditionService { return editionControllerApi.getAllEditionsActiveAtDate(time).toStream().toList(); } + /** + * Gets a list of edition ids that a person is the manager of + * + * @param personId The person to get the ids for + * @return The list of edition ids + */ + public List<Long> getAllEditionIdsThatPersonIsAMangerOf(Long personId) { + return personService.getRolesForPerson(personId) + .stream().filter(r -> r.getType().equals(RoleEditionDetailsDTO.TypeEnum.TEACHER)) + .map(r -> Objects.requireNonNull(r.getEdition()).getId()).collect(Collectors.toList()); + } + + /** + * Get all edition ids for a course + * + * @param courseId The course id + * @return The list of edition ids + */ + public List<Long> getEditionsForCourse(Long courseId) { + return editionControllerApi.getAllEditionsByCourse(courseId).map(EditionDetailsDTO::getId) + .collect(Collectors.toList()).block(); + } + // endregion } diff --git a/src/main/java/nl/tudelft/tam/service/EmailService.java b/src/main/java/nl/tudelft/tam/service/EmailService.java index 021a845d5e6946f2ea0dc5b9b119da65e70110a8..66c4bb0e86f7603cd350085bf2077c25aa74dce9 100644 --- a/src/main/java/nl/tudelft/tam/service/EmailService.java +++ b/src/main/java/nl/tudelft/tam/service/EmailService.java @@ -80,7 +80,6 @@ public class EmailService { } catch (MessagingException e) { e.printStackTrace(); - // TODO what to do in this case } return CompletableFuture.completedFuture(null); diff --git a/src/main/java/nl/tudelft/tam/service/ExtraWorkService.java b/src/main/java/nl/tudelft/tam/service/ExtraWorkService.java index 9ca897f0466c73f8dd606ccfb5f6beb4f67cf52d..dd360049ab32898c1d628113b88c1042b346d8f4 100644 --- a/src/main/java/nl/tudelft/tam/service/ExtraWorkService.java +++ b/src/main/java/nl/tudelft/tam/service/ExtraWorkService.java @@ -70,6 +70,9 @@ public class ExtraWorkService { @Autowired CourseService courseService; + @Autowired + CohortService cohortService; + @Autowired CSVService csvService; @@ -290,8 +293,6 @@ public class ExtraWorkService { List<EditionSummaryDTO> activeEditions = editionService.getAllActiveEditions(LocalDateTime.now()); List<EditionDetailsDTO> editions = editionService.getEditionsById(activeEditions.stream() .filter(x -> !x.getIsArchived()).map(EditionSummaryDTO::getId).toList()); - List<CourseDetailsDTO> courses = courseService - .getCoursesById(editions.stream().map(e -> e.getCourse().getId()).distinct().toList()); List<ExtraWork> result = new ArrayList<>(); @@ -301,9 +302,12 @@ public class ExtraWorkService { String editionName = line[0]; String courseCode = line[1]; - EditionDetailsDTO edition = editions.stream().filter( - e -> e.getCourse().getCode().equals(courseCode) && e.getName().equals(editionName)) - .findAny().get(); + EditionDetailsDTO edition = editions + .stream() + .filter(e -> e.getCourse().getCode().equals(courseCode) + && e.getName().equals(editionName)) + .findAny() + .get(); work.setEditionId(edition.getId()); work.setName(line[2]); diff --git a/src/main/java/nl/tudelft/tam/service/JobOfferService.java b/src/main/java/nl/tudelft/tam/service/JobOfferService.java index 3b725c206f21bad5cc696670ace51a283c639535..378309883590f45663f6d4428a27efacc104706a 100644 --- a/src/main/java/nl/tudelft/tam/service/JobOfferService.java +++ b/src/main/java/nl/tudelft/tam/service/JobOfferService.java @@ -22,7 +22,9 @@ import java.nio.file.Path; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.*; +import java.util.function.Function; import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.transaction.Transactional; import javax.validation.constraints.NotNull; @@ -36,16 +38,12 @@ import nl.tudelft.tam.enums.Status; import nl.tudelft.tam.exception.WrongDataCallException; import nl.tudelft.tam.model.Application; import nl.tudelft.tam.model.JobOffer; -import nl.tudelft.tam.model.Profile; import nl.tudelft.tam.repository.JobOfferRepository; -import org.apache.commons.lang3.tuple.MutablePair; -import org.apache.commons.lang3.tuple.Pair; import org.modelmapper.ModelMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Lazy; -import org.springframework.data.rest.webmvc.ResourceNotFoundException; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; @@ -96,6 +94,9 @@ public class JobOfferService { @Autowired CohortService cohortService; + @Autowired + CoordinatorService coordinatorService; + @Autowired CoordinatorDefaultService coordinatorDefaultService; @@ -266,24 +267,34 @@ public class JobOfferService { .map(o -> mapper.map(o, JobOfferSummaryDTO.class)).collect(Collectors.toList()); } + /** + * Gets the most common approve/disapprove action-maker on TAM since that person will also approve in + * FlexDelft + * + * @param editionId the id of the edition for which to find the approver + * @return The person who is the most common approver/disapprover on TAM for that edition + */ public PersonSummaryDTO getApprover(Long editionId) { - List<PersonSummaryDTO> teachers = roleService.getTeachersById(editionId).stream() - .map(t -> new MutablePair<PersonSummaryDTO, Integer>(t.getPerson(), 0)) - .peek(tPair -> { - try { - Profile profile = profileService.findByIdOrThrow(tPair.getKey().getId()); - tPair.setValue(profile.getHandledDeclarations().size() - + profile.getHandledApplications().size()); - } catch (ResourceNotFoundException e) { - tPair.setValue(0); - } - }) - .sorted(Comparator.comparingInt(Pair::getValue)) - .map(Pair::getKey) - .collect(Collectors.toList()); + Stream<PersonSummaryDTO> coordinators = coordinatorService.getCoordinatorsByEditionId(editionId) + .stream() + .map(coord -> personService.getOrThrow(coord.getPersonId())); + Stream<PersonSummaryDTO> teachers = roleService.getTeachersById(editionId) + .stream() + .map(RolePersonDetailsDTO::getPerson); + Stream<PersonSummaryDTO> both = Stream.concat(teachers, coordinators); // Return the teacher with the most TAM actions - return teachers.get(0); + return both.collect(Collectors.toMap( + Function.identity(), + p -> profileService.findById(p.getId()) + .map(profile -> profile.getHandledDeclarations().size() + + profile.getHandledApplications().size()) + .orElse(0))) + .entrySet() + .stream() + .max(Map.Entry.comparingByValue()) + .map(Map.Entry::getKey) + .orElse(null); } /** @@ -353,17 +364,24 @@ public class JobOfferService { String editionName = line[0]; String courseCode = line[1]; - Optional<EditionDetailsDTO> matchingEdition = editions.stream().filter( - e -> e.getCourse().getCode().equals(courseCode) && e.getName().equals(editionName)) + Optional<EditionDetailsDTO> matchingEdition = editions + .stream() + .filter(e -> e.getCourse().getCode().equals(courseCode) + && e.getName().equals(editionName)) .findAny(); EditionDetailsDTO edition = matchingEdition.orElseGet(() -> { - CourseSummaryDTO course = courses.stream().filter(c -> c.getCode().equals(courseCode)) + CourseSummaryDTO course = courses + .stream() + .filter(c -> c.getCode().equals(courseCode)) + .findAny() + .get(); + CohortDetailsDTO cohort = cohorts + .stream() + .filter(c -> c.getProgram().getName().equals(line[12]) + && c.getName().equals(line[11])) .findAny() .get(); - CohortDetailsDTO cohort = cohorts.stream().filter( - c -> c.getProgram().getName().equals(line[12]) && c.getName().equals(line[11])) - .findAny().get(); return editionService.getEditionById(editionService.addEdition(new EditionCreateDTO() .name(editionName) .course(new CourseIdDTO().id(course.getId())) diff --git a/src/main/java/nl/tudelft/tam/service/ProfileService.java b/src/main/java/nl/tudelft/tam/service/ProfileService.java index b35a722cfd226a8f028b5cff95565bd818b65e7c..2d337cee272b45757dd892950161ac35076afc2e 100644 --- a/src/main/java/nl/tudelft/tam/service/ProfileService.java +++ b/src/main/java/nl/tudelft/tam/service/ProfileService.java @@ -17,6 +17,8 @@ */ package nl.tudelft.tam.service; +import java.util.Optional; + import javax.validation.constraints.NotNull; import nl.tudelft.tam.dto.patch.NotificationPatchDTO; @@ -44,6 +46,16 @@ public class ProfileService { return repository.findByIdOrThrow(id); } + /** + * Finds the profile object by id + * + * @param id The id to find + * @return The object found wrapped in an optional + */ + public Optional<Profile> findById(Long id) { + return repository.findById(id); + } + /** * Updates the polo information for a person * diff --git a/src/main/java/nl/tudelft/tam/service/ProgramService.java b/src/main/java/nl/tudelft/tam/service/ProgramService.java index a0d1d7c932c65c3cfe89b0aaffeefe92795243ff..eefcfac33b429147e344c4b73b0f5a300159979f 100644 --- a/src/main/java/nl/tudelft/tam/service/ProgramService.java +++ b/src/main/java/nl/tudelft/tam/service/ProgramService.java @@ -21,6 +21,7 @@ import java.util.List; import nl.tudelft.labracore.api.ProgramControllerApi; import nl.tudelft.labracore.api.dto.ProgramDetailsDTO; +import nl.tudelft.labracore.api.dto.ProgramSummaryDTO; import nl.tudelft.tam.repository.CoordinatorRepository; import org.springframework.beans.factory.annotation.Autowired; @@ -30,10 +31,10 @@ import org.springframework.stereotype.Service; public class ProgramService { @Autowired - ProgramControllerApi programApi; + private ProgramControllerApi programApi; @Autowired - CoordinatorRepository coordinatorRepository; + private CoordinatorRepository coordinatorRepository; /** * Get a program by id @@ -55,16 +56,30 @@ public class ProgramService { return programApi.getAllProgramsById(ids).collectList().block(); } - /* + public List<ProgramSummaryDTO> getAllPrograms() { + return programApi.getAllPrograms().collectList().block(); + } + + /** * Gets all programs coordinated by a coordinator. * - * @param coordinatorId The id of the coordinator + * @param coordinatorId The id of the coordinator * - * @return All program details coordinated + * @return All program details coordinated */ public List<ProgramDetailsDTO> getCoordinatingPrograms(Long coordinatorId) { return coordinatorRepository.findAllByPersonId(coordinatorId).stream() .map(c -> programApi.getProgramById(c.getProgramId()).block()).toList(); } + /** + * Returns a list of all the programs which are relevant to the person. + * + * @param personId The person to get the programs for + * @return The list of programs + */ + public List<ProgramSummaryDTO> getAllRelevantPrograms(Long personId) { + return programApi.getAllRelevantPrograms(personId).collectList().block(); + } + } diff --git a/src/main/java/nl/tudelft/tam/service/RaiseRequestService.java b/src/main/java/nl/tudelft/tam/service/RaiseRequestService.java index b86b0db93c9d387fadd70cf9c8437bd63e8b566c..fd9a01344da9075bb1d6327aed8d782681f6b399 100644 --- a/src/main/java/nl/tudelft/tam/service/RaiseRequestService.java +++ b/src/main/java/nl/tudelft/tam/service/RaiseRequestService.java @@ -24,6 +24,7 @@ import java.util.stream.Collectors; import javax.validation.constraints.NotNull; import nl.tudelft.labracore.api.dto.PersonSummaryDTO; +import nl.tudelft.labracore.api.dto.ProgramSummaryDTO; import nl.tudelft.labracore.lib.security.user.Person; import nl.tudelft.librador.dto.view.View; import nl.tudelft.tam.dto.create.RaiseRequestCreateDTO; @@ -58,6 +59,9 @@ public class RaiseRequestService { @Autowired private PersonService personService; + @Autowired + private ProgramService programService; + @Value("${tam.filesys.payscale-proofs-dir}") private String PROOF_DIRECTORY; @@ -169,9 +173,11 @@ public class RaiseRequestService { /** * Return all raise requests with status submitted. * - * @return list of found raise requests + * @param programIds list of program ids for which to get all submitted raise requests + * @return list of found raise requests */ - public List<RaiseRequestDetailsDTO> getAllSubmitted() throws InvalidDatabaseEntryException { + public List<RaiseRequestDetailsDTO> getAllSubmitted(List<Long> programIds) + throws InvalidDatabaseEntryException { List<RaiseRequest> requests = raiseRequestRepository.findAllByStatus(Status.SUBMITTED); @@ -186,19 +192,37 @@ public class RaiseRequestService { List<PersonSummaryDTO> people = personService.getPeopleById( profiles.stream().map(Profile::getId).collect(Collectors.toList())); - if (list.size() != people.size()) { + if (list.size() != people.size() || people.size() != profiles.size()) { throw new InvalidDatabaseEntryException( "The list of raise requests contains an invalid request, " + "a matching person or profile was not found for at least one request."); } + List<RaiseRequestDetailsDTO> filteredList = new ArrayList<>(); + for (int i = 0; i < list.size(); i++) { RaiseRequestDetailsDTO request = list.get(i); - request.setPerson(people.get(i)); - request.setCurrentPayScale(profiles.get(i).getPayScale()); + + List<Long> relevantProgramIds = programService + .getAllRelevantPrograms(people.get(i).getId()) + .stream() + .map(ProgramSummaryDTO::getId) + .collect(Collectors.toList()); + + for (Long programId : relevantProgramIds) { + if (programIds.contains(programId)) { + request.setPerson(people.get(i)); + request.setCurrentPayScale(profiles.get(i).getPayScale()); + request.setRelevantProgramIds(relevantProgramIds); + + filteredList.add(request); + break; + } + } + } - return list; + return filteredList; } /** diff --git a/src/main/java/nl/tudelft/tam/service/TrainingApprovalRequestService.java b/src/main/java/nl/tudelft/tam/service/TrainingApprovalRequestService.java index 1615003f06d22273583ebdb7c5ecb6a673fe20f1..e3a21d5682bb4b114faa0957c80c94e10d80f1d6 100644 --- a/src/main/java/nl/tudelft/tam/service/TrainingApprovalRequestService.java +++ b/src/main/java/nl/tudelft/tam/service/TrainingApprovalRequestService.java @@ -22,6 +22,7 @@ import java.util.*; import java.util.stream.Collectors; import nl.tudelft.labracore.api.dto.PersonSummaryDTO; +import nl.tudelft.labracore.api.dto.ProgramSummaryDTO; import nl.tudelft.labracore.lib.security.user.Person; import nl.tudelft.librador.dto.view.View; import nl.tudelft.tam.dto.create.TrainingApprovalRequestCreateDTO; @@ -49,6 +50,9 @@ public class TrainingApprovalRequestService { @Autowired private PersonService personService; + @Autowired + private ProgramService programService; + @Autowired private ModelMapper mapper; @@ -122,9 +126,11 @@ public class TrainingApprovalRequestService { /** * Return all training approval requests with status submitted. * - * @return list of found training approval requests + * @param programIds list of program ids for which to get all submitted training approval requests + * @return list of found training approval requests */ - public List<TrainingApprovalRequestDetailsDTO> getAllSubmitted() throws InvalidDatabaseEntryException { + public List<TrainingApprovalRequestDetailsDTO> getAllSubmitted(List<Long> programIds) + throws InvalidDatabaseEntryException { List<TrainingApprovalRequest> requests = trainingApprovalRequestRepository .findAllByStatus(Status.SUBMITTED); @@ -148,12 +154,30 @@ public class TrainingApprovalRequestService { "a matching person or profile was not found for at least one request."); } + List<TrainingApprovalRequestDetailsDTO> filteredList = new ArrayList<>(); + for (int i = 0; i < list.size(); i++) { TrainingApprovalRequestDetailsDTO request = list.get(i); - request.setPerson(people.get(i)); + + List<Long> relevantProgramIds = programService + .getAllRelevantPrograms(people.get(i).getId()) + .stream() + .map(ProgramSummaryDTO::getId) + .collect(Collectors.toList()); + + for (Long programId : relevantProgramIds) { + if (programIds.contains(programId)) { + request.setPerson(people.get(i)); + request.setRelevantProgramIds(relevantProgramIds); + + filteredList.add(request); + break; + } + } + } - return list; + return filteredList; } /** diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties index c901cf18b8e76c84b0a04da194bebd88ae373edc..00bec3fde6a7ea59ebbcdb01d96485255665ac66 100644 --- a/src/main/resources/messages.properties +++ b/src/main/resources/messages.properties @@ -113,7 +113,7 @@ jobOffer.add = New Job Offer jobOffer.import = Import Job Offers jobOffer.import.info = Import bulk job offers via a CSV file. Each job offer should be a separate row, with the following columns in this order: jobOffer.import.info.columnOrder = Edition Name (e.g 2022/2023), Course Code, Job Offer Name, Contract Name, Contract Start Date, Contract End Date, Contract Baan Code, Max Hours, Hiring Message, Rejection Message, Application Deadline -jobOffer.import.info.editionCreate = If you want to create non-existing editions, add the following columns: Cohort Name, Program Name, Edition Start Date, Edition End Date +jobOffer.import.info.editionCreate = If you want to create non-existing/new editions, add the following columns: Cohort Name, Program Name, Edition Start Date, Edition End Date jobOffer.deadline = Deadline For Applying jobOffer.deadline.none = No Deadline jobOffer.deadline.closed = Closed @@ -156,6 +156,7 @@ jobOffer.export.beforeFirstExport = Before first export jobOffer.before = TAed Before jobOffer.queueFeedback = Queue Feedback jobOffer.queueFeedback.view = View feedback +jobOffer.selectProgram = Select a programme to view job offers. application.all = All Applications application.my = My Applications @@ -204,6 +205,7 @@ extraWork.add = New Extra Work extraWork.import = Import Extra Work extraWork.import.info = Import bulk extra work via a CSV file. Each extra work should be a separate row, with the following columns in this order: extraWork.import.info.columnOrder = Edition Name (e.g. 2022/2023), Course Code, Extra Work Name, Contract Name, Contract Baan Code, Max Hours, Contract Request Deadline +extraWork.import.info.editionCreate = If you want to create non-existing/new editions, add the following columns: Cohort Name, Program Name, Edition Start Date, Edition End Date extraWork.deadline = Deadline For Contract Requests extraWork.deadline.none = No Deadline extraWork.deadline.closed = Closed @@ -342,9 +344,9 @@ training.import.conflict.ignore = Ignore Conflicts & Close coordinator = Coordinator coordinator.portal = Coordinator Portal coordinator.manager.managed = Managed Courses -coordinator.manager.all = All Courses -coordinator.defaults = Manage Defaults -coordinator.defaults.title = Manage Program Defaults +coordinator.manager.all = Coordinating Courses +coordinator.defaults = Manage Programmes +coordinator.defaults.title = Manage Programmes coordinator.defaults.contractName = Contract Name coordinator.defaults.contractBaan = Baan Code coordinator.defaults.contractStartDate = Contract Start Date diff --git a/src/main/resources/templates/coordinator/defaults.html b/src/main/resources/templates/coordinator/defaults.html index c7b6d3bb23bb716c19e7c4d1191bde05c509d1b4..76d06a8b5c36dd81f494383f3aece47e60272a76 100644 --- a/src/main/resources/templates/coordinator/defaults.html +++ b/src/main/resources/templates/coordinator/defaults.html @@ -39,9 +39,11 @@ <h1 class="title" th:text="#{coordinator.defaults.title}"></h1> </div> + <hr /> + <div class="info_row_4x4"> <div class="card_stats card" th:each="currDefault : ${programNamesAndDefaults}"> - <h2 class="card_title" th:text="${currDefault.key}"></h2> + <h2 class="card_title" th:text="|${currDefault.key} - Default Values|"></h2> <h3 class="card_subtitle" th:text="#{extraWork}"></h3> diff --git a/src/main/resources/templates/extra_work/import.html b/src/main/resources/templates/extra_work/import.html index 7a3a805110a0d7054954fc786a764885423b3992..6aa879fd1bad9a5672d681ab912c258601d8fe7d 100644 --- a/src/main/resources/templates/extra_work/import.html +++ b/src/main/resources/templates/extra_work/import.html @@ -36,10 +36,11 @@ <h1 class="underlined title" th:text="#{extraWork.import}"></h1> <div class="form-group maxw-60"> <span> - <span th:text="#{extraWork.import.info}"></span> + <p th:text="#{extraWork.import.info}"></p> <br /> + <p th:text="#{extraWork.import.info.columnOrder}"></p> <br /> - <span th:text="#{extraWork.import.info.columnOrder}"></span> + <p th:text="#{extraWork.import.info.editionCreate}"></p> </span> </div> diff --git a/src/main/resources/templates/job_offer/add_offer.html b/src/main/resources/templates/job_offer/add_offer.html index 3df7e93ab52964b7b22649391feca9a6a1a6e713..23261be44319206000113f5c098386c18dcde885 100644 --- a/src/main/resources/templates/job_offer/add_offer.html +++ b/src/main/resources/templates/job_offer/add_offer.html @@ -30,7 +30,7 @@ <form th:id="add-offer-form" th:action="@{/person/find-candidate}" - th:method="get"></form> + th:method="post"></form> <div class="boxed-content"> <h1 class="underlined title" th:text="#{jobOffer.addOffer}"></h1> diff --git a/src/main/resources/templates/job_offer/confirm_retract_app.html b/src/main/resources/templates/job_offer/confirm_retract_app.html index f2f8a514031fa38d74fb3cf64505950e3f39beff..32c8b2dd0a32b4bb9367e86e2be2330a181b8d99 100644 --- a/src/main/resources/templates/job_offer/confirm_retract_app.html +++ b/src/main/resources/templates/job_offer/confirm_retract_app.html @@ -34,6 +34,12 @@ class="boxed-content"> <h1 class="underlined title" th:text="#{jobOffer.retract.confirm}"></h1> + <input + th:if="${param.program}" + type="hidden" + name="program" + th:value="${param.program}" /> + <div class="form-group"> <span class="form-group-desc" th:text="#{jobOffer.retract.confirm.desc}"></span> <br /> diff --git a/src/main/resources/templates/job_offer/import.html b/src/main/resources/templates/job_offer/import.html index 17981aae791558038b808939be753fed248054e7..5fd6e4621f190a2cad84a4b76434060e0225e492 100644 --- a/src/main/resources/templates/job_offer/import.html +++ b/src/main/resources/templates/job_offer/import.html @@ -37,7 +37,9 @@ <div class="form-group maxw-60"> <div> <p th:text="#{jobOffer.import.info}"></p> + <br /> <p th:text="#{jobOffer.import.info.columnOrder}"></p> + <br /> <p th:text="#{jobOffer.import.info.editionCreate}"></p> </div> </div> diff --git a/src/main/resources/templates/job_offer/view_many.html b/src/main/resources/templates/job_offer/view_many.html index 928f34c27257345a00b977ff7e3981a5bc35bba4..824766698f0e41d75cee43e25a48d8a64fc9f5b4 100644 --- a/src/main/resources/templates/job_offer/view_many.html +++ b/src/main/resources/templates/job_offer/view_many.html @@ -29,20 +29,6 @@ <link rel="stylesheet" type="text/css" th:href="@{/css/applications.css}" /> <title th:text="#{jobOffer.many}"></title> - - <script th:inline="text"> - let currentTab = "[[${tab}]]"; - function switchTab(to) { - if (to !== currentTab) { - document.getElementById("offers").classList.toggle("hidden"); - document.getElementById("applications").classList.toggle("hidden"); - document.getElementById("offers-tab").classList.toggle("active"); - document.getElementById("applications-tab").classList.toggle("active"); - window.history.replaceState(null, document.title, "/job-offer/open?tab=" + to); - } - currentTab = to; - } - </script> </head> <body> @@ -70,11 +56,12 @@ <div class="title-and-search"> <h1 class="title" th:text="#{jobOffer.many}"></h1> - <form class="search"> + <form class="search" id="search-form"> <input name="q" + id="job-offer-search" class="search_field" - aria-label="Global search" + aria-label="Search job offers" type="search" th:value="${param.q}" th:placeholder="#{jobOffer.search}" /> @@ -84,6 +71,20 @@ </form> </div> + <div> + <label for="program-select" style="margin-right: 0.25rem"> + View job offers for: + </label> + <select id="program-select" class="selectbox"> + <option disabled selected>Select study program</option> + <option + th:each="program : ${programs}" + th:value="${program.id}" + th:selected="${param.program?.toString() == program.id.toString()}" + th:text="${program.name}"></option> + </select> + </div> + <div class="non_empty" th:unless="${editions.isEmpty()}"> <div class="accordion"> <table @@ -138,6 +139,11 @@ th:action="@{|/application/submit/${offer.id}|}" method="post" data-scroll-to="true"> + <input + th:if="${param.program}" + type="hidden" + name="program" + th:value="${param.program}" /> <input type="submit" class="accordion__action" @@ -163,6 +169,11 @@ th:action="@{|/application/accept/${offer.id}|}" method="post" data-scroll-to="true"> + <input + th:if="${param.program}" + type="hidden" + name="program" + th:value="${param.program}" /> <input type="submit" class="accordion__action" @@ -194,7 +205,14 @@ </div> <div data-empty="true" th:if="${editions.isEmpty()}"> - <span class="empty_description" th:text="#{jobOffer.empty.description}"></span> + <span + th:if="${param.program}" + class="empty_description" + th:text="#{jobOffer.empty.description}"></span> + <span + th:unless="${param.program}" + class="empty_description" + th:text="#{jobOffer.selectProgram}"></span> </div> </div> @@ -293,6 +311,39 @@ </tr> </table> </div> + + <script th:inline="text"> + let currentTab = "[[${tab}]]"; + function switchTab(to) { + if (to !== currentTab) { + document.getElementById("offers").classList.toggle("hidden"); + document.getElementById("applications").classList.toggle("hidden"); + document.getElementById("offers-tab").classList.toggle("active"); + document.getElementById("applications-tab").classList.toggle("active"); + window.history.replaceState( + null, + document.title, + "/job-offer/open?tab=" + to + ); + } + currentTab = to; + } + + $(() => { + $("#program-select").change(function () { + let url = new URL(window.location); + url.searchParams.set("program", $(this).val()); + window.location = url.toString(); + }); + + $("#search-form").submit(function (event) { + event.preventDefault(); + let url = new URL(window.location); + url.searchParams.set("q", $(this).find("input[name='q']").val()); + window.location = url.toString(); + }); + }); + </script> </div> </body> </html> diff --git a/src/test/java/nl/tudelft/tam/controller/JobOfferControllerTest.java b/src/test/java/nl/tudelft/tam/controller/JobOfferControllerTest.java index 9c85ae1b7b6b5047e5eadd527887f07eef6bb5bd..86f38442bd67eddc40fa838bd5dbcfca942d2153 100644 --- a/src/test/java/nl/tudelft/tam/controller/JobOfferControllerTest.java +++ b/src/test/java/nl/tudelft/tam/controller/JobOfferControllerTest.java @@ -38,7 +38,6 @@ import nl.tudelft.tam.dto.view.summary.ApplicationSummaryDTO; import nl.tudelft.tam.dto.view.summary.ExtraWorkSummaryDTO; import nl.tudelft.tam.dto.view.summary.JobOfferSummaryDTO; import nl.tudelft.tam.enums.Status; -import nl.tudelft.tam.model.Coordinator; import nl.tudelft.tam.model.CoordinatorDefault; import nl.tudelft.tam.model.JobOffer; import nl.tudelft.tam.model.embeddables.ExtraWorkDefault; @@ -101,6 +100,9 @@ class JobOfferControllerTest { @MockBean CoordinatorService coordinatorService; + @MockBean + ProgramService programService; + @MockBean CoordinatorDefaultService coordinatorDefaultService; @@ -126,7 +128,7 @@ class JobOfferControllerTest { @BeforeEach void setUp() { edition = new EditionDetailsDTO().id(EDITION_ID) - .course(new CourseSummaryDTO().name("course-name").code("course-code")) + .course(new CourseSummaryDTO().id(1L).name("course-name").code("course-code")) .cohort(new CohortSummaryDTO().name("cohort-name")).name("edition-name") .endDate(LocalDateTime.now().plusMonths(2)); @@ -186,6 +188,10 @@ class JobOfferControllerTest { @Test @WithUserDetails("username") void getOpenTest() throws Exception { + when(programService.getAllPrograms()).thenReturn(List.of(new ProgramSummaryDTO().id(1L))); + when(courseService.getAllCoursesInProgram(anyLong())) + .thenReturn(List.of(new CourseSummaryDTO().id(1L))); + ApplicationDetailsJobOfferDTO ap1 = mapper.map(app1a, ApplicationDetailsJobOfferDTO.class); ap1.setJobOffer(offer1); @@ -216,7 +222,7 @@ class JobOfferControllerTest { when(applicationService.getMapWithStatusAppOpenByIdForPerson(anyLong(), anyList())).thenReturn(apps); - mvc.perform(get("/job-offer/open").with(csrf())) + mvc.perform(get("/job-offer/open").param("program", "1").with(csrf())) .andExpect(status().isOk()) .andExpect(view().name("job_offer/view_many")) .andExpect(model().attribute("editions", editions)) @@ -233,6 +239,10 @@ class JobOfferControllerTest { @Test @WithUserDetails("username") void getOpenWithQueryTest() throws Exception { + when(programService.getAllPrograms()).thenReturn(List.of(new ProgramSummaryDTO().id(1L))); + when(courseService.getAllCoursesInProgram(anyLong())) + .thenReturn(List.of(new CourseSummaryDTO().id(1L))); + String query = "test-query"; ApplicationDetailsJobOfferDTO ap1 = mapper.map(app1a, ApplicationDetailsJobOfferDTO.class); @@ -264,7 +274,7 @@ class JobOfferControllerTest { when(applicationService.getMapWithStatusAppOpenByIdForPerson(anyLong(), anyList())).thenReturn(apps); - mvc.perform(get("/job-offer/open").param("q", query).with(csrf())) + mvc.perform(get("/job-offer/open").param("q", query).param("program", "1").with(csrf())) .andExpect(status().isOk()) .andExpect(view().name("job_offer/view_many")) .andExpect(model().attribute("editions", editions)) @@ -284,7 +294,7 @@ class JobOfferControllerTest { String q = "edi"; RoleEditionDetailsDTO role = new RoleEditionDetailsDTO().type(RoleEditionDetailsDTO.TypeEnum.TEACHER) .edition(new EditionSummaryDTO().id(EDITION_ID)); - List<Long> editionIds = List.of(EDITION_ID); + List<Long> editionIds = List.of(); List<EditionDetailsOffersWorkDTO> editions = List .of(mapper.map(edition, EditionDetailsOffersWorkDTO.class)); editions.get(0).setOffers(List.of(offer1, offer2)); @@ -312,33 +322,36 @@ class JobOfferControllerTest { .andExpect(model().attribute("managed", List.of())) .andExpect(model().attribute("cohorts", List.of())); - verify(personService).getRolesForPerson(anyLong()); + verify(editionService).getAllEditionIdsThatPersonIsAMangerOf(anyLong()); + verify(courseService).getManagingCourses(any()); + verify(courseService).getCoursesById(anyList()); verify(editionService).getEditionsWithJobsAndWork(editionIds); verify(editionService).filter(editions, q); + verify(coordinatorDefaultService).addDefaultsToEditionsWithJobsAndWork(anyList()); } @Test @WithUserDetails("admin") void getManagerPortalTestWithHidePrev() throws Exception { + when(authService.isManagerOfAny()).thenReturn(true); + String q = "edi"; - RoleEditionDetailsDTO role = new RoleEditionDetailsDTO().type(RoleEditionDetailsDTO.TypeEnum.TEACHER) - .edition(new EditionSummaryDTO().id(EDITION_ID)); - List<Long> editionIds = List.of(EDITION_ID); + List<Long> editionIds = List.of(); + List<EditionDetailsOffersWorkDTO> editions = List .of(mapper.map(edition, EditionDetailsOffersWorkDTO.class)); editions.get(0).setOffers(List.of(offer1, offer2)); editions.get(0).setWork(List.of(work1, work2)); editions.get(0).setDefaults(cseDefault); + List<EditionDetailsOffersWorkDTO> filtered = List .of(mapper.map(edition, EditionDetailsOffersWorkDTO.class)); filtered.get(0).setOffers(List.of(offer1)); filtered.get(0).setWork(List.of(work1)); filtered.get(0).setDefaults(cseDefault); - when(personService.getRolesForPerson(anyLong())).thenReturn(List.of(role)); when(editionService.getEditionsWithJobsAndWork(anyList())).thenReturn(editions); when(editionService.filter(anyList(), anyString())).thenReturn(filtered); - when(authService.isManagerOfAny()).thenReturn(true); doNothing().when(coordinatorDefaultService).addDefaultsToEditionsWithJobsAndWork(anyList()); @@ -352,9 +365,12 @@ class JobOfferControllerTest { .andExpect(model().attribute("managed", List.of())) .andExpect(model().attribute("cohorts", List.of())); - verify(personService).getRolesForPerson(anyLong()); + verify(editionService).getAllEditionIdsThatPersonIsAMangerOf(anyLong()); + verify(courseService).getManagingCourses(any()); + verify(courseService).getCoursesById(anyList()); verify(editionService).getEditionsWithJobsAndWork(editionIds); verify(editionService).filter(editions, q); + verify(coordinatorDefaultService).addDefaultsToEditionsWithJobsAndWork(anyList()); } @Test @@ -362,11 +378,10 @@ class JobOfferControllerTest { void getManagerPortalAllTest() throws Exception { when(authService.isCoordinatorOfAny()).thenReturn(true); - Coordinator coordinator = Coordinator.builder().programId(EDITION_ID).build(); - List<Coordinator> coordinatingList = List.of(coordinator); - when(coordinatorService.findAllByPersonId(anyLong())).thenReturn(coordinatingList); - - when(editionService.getAllEditionIds()).thenReturn(List.of(EDITION_ID)); + when(coordinatorService.getProgramIdsByPersonId(anyLong())).thenReturn(Set.of(1L)); + when(courseService.getAllCoursesInProgram(anyLong())) + .thenReturn(List.of(new CourseSummaryDTO().id(1L))); + when(editionService.getEditionsForCourse(anyLong())).thenReturn(List.of(EDITION_ID)); List<EditionDetailsOffersWorkDTO> editions = List .of(mapper.map(edition, EditionDetailsOffersWorkDTO.class)); @@ -391,10 +406,13 @@ class JobOfferControllerTest { .andExpect(model().attribute("showAll", true)) .andExpect(model().attribute("hidePrev", true)); - verify(coordinatorService).findAllByPersonId(anyLong()); - verify(editionService).getAllEditionIds(); + verify(coordinatorService).getProgramIdsByPersonId(anyLong()); + verify(courseService).getAllCoursesInProgram(anyLong()); + verify(editionService).getEditionsForCourse(anyLong()); + verify(cohortService).getCohortsByProgrammeIds(anyList()); verify(editionService).getEditionsWithJobsAndWork(List.of(EDITION_ID)); verify(editionService).filter(editions, null); + verify(coordinatorDefaultService).addDefaultsToEditionsWithJobsAndWork(anyList()); } @Test @@ -404,11 +422,10 @@ class JobOfferControllerTest { when(authService.isCoordinatorOfAny()).thenReturn(true); - Coordinator coordinator = Coordinator.builder().programId(EDITION_ID).build(); - List<Coordinator> coordinatingList = List.of(coordinator); - when(coordinatorService.findAllByPersonId(anyLong())).thenReturn(coordinatingList); - - when(editionService.getAllEditionIds()).thenReturn(List.of(EDITION_ID)); + when(coordinatorService.getProgramIdsByPersonId(anyLong())).thenReturn(Set.of(1L)); + when(courseService.getAllCoursesInProgram(anyLong())) + .thenReturn(List.of(new CourseSummaryDTO().id(1L))); + when(editionService.getEditionsForCourse(anyLong())).thenReturn(List.of(EDITION_ID)); List<EditionDetailsOffersWorkDTO> editions = List .of(mapper.map(edition, EditionDetailsOffersWorkDTO.class)); @@ -417,9 +434,6 @@ class JobOfferControllerTest { editions.get(0).setDefaults(cseDefault); when(editionService.getEditionsWithJobsAndWork(any())).thenReturn(editions); - when(courseService.getOrThrow(any())) - .thenReturn(new CourseDetailsDTO().program(new ProgramSummaryDTO().id(EDITION_ID))); - when(editionService.filter(anyList(), eq(q))).thenReturn(editions); doNothing().when(coordinatorDefaultService).addDefaultsToEditionsWithJobsAndWork(anyList()); @@ -433,10 +447,13 @@ class JobOfferControllerTest { .andExpect(model().attribute("showAll", true)) .andExpect(model().attribute("hidePrev", true)); - verify(coordinatorService).findAllByPersonId(anyLong()); - verify(editionService).getAllEditionIds(); + verify(coordinatorService).getProgramIdsByPersonId(anyLong()); + verify(courseService).getAllCoursesInProgram(anyLong()); + verify(editionService).getEditionsForCourse(anyLong()); + verify(cohortService).getCohortsByProgrammeIds(anyList()); verify(editionService).getEditionsWithJobsAndWork(List.of(EDITION_ID)); verify(editionService).filter(editions, q); + verify(coordinatorDefaultService).addDefaultsToEditionsWithJobsAndWork(anyList()); } @Test diff --git a/src/test/java/nl/tudelft/tam/service/ApplicationServiceMockTest.java b/src/test/java/nl/tudelft/tam/service/ApplicationServiceMockTest.java index 73d1fc6da786af35460f2cabed122e6fe0e931a3..d4453f61cfa93ea9ffa1376ed480e18c94ab91fd 100644 --- a/src/test/java/nl/tudelft/tam/service/ApplicationServiceMockTest.java +++ b/src/test/java/nl/tudelft/tam/service/ApplicationServiceMockTest.java @@ -18,6 +18,7 @@ package nl.tudelft.tam.service; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.*; import java.io.IOException; @@ -140,8 +141,12 @@ class ApplicationServiceMockTest { @BeforeEach void setUp() { offer1 = JobOffer.builder().name("offer-1").id(JOFFER_ID_1).editionId(EDITION_ID) + .contractStartDate(LocalDate.of(2023, 2, 1)) + .contractEndDate(LocalDate.of(2023, 2, 2)) .deadline(LocalDate.now().plusDays(1)).build(); offer2 = JobOffer.builder().name("offer-2").id(JOFFER_ID_2).editionId(EDITION_ID) + .contractStartDate(LocalDate.of(2023, 2, 1)) + .contractEndDate(LocalDate.of(2023, 2, 2)) .deadline(LocalDate.now().plusDays(2)).build(); app1a = Application.builder().id(new Application.AppId(PERSON_ID_1, JOFFER_ID_1)).jobOffer(offer1) @@ -675,8 +680,8 @@ class ApplicationServiceMockTest { "Contract Name Default", "null", "SA2", - "null", - "null", + "01-02-2023", + "02-02-2023", "1", "" }, @@ -688,9 +693,17 @@ class ApplicationServiceMockTest { "Contract Name Default", "null", "SA2", - "null", - "null", + "01-02-2023", + "02-02-2023", "1", "" }); } + + @Test + void getNumberOfUnhandledApplicationsTest() { + when(service.getApplicationsForJobOffer(anyLong())).thenReturn(List.of()); + when(service.getStatsForApps(anyList())).thenReturn(new int[] { 10, 1, 2, 3 }); + + assertEquals(4, service.getNumberOfUnhandledApplications(1L)); + } } diff --git a/src/test/java/nl/tudelft/tam/service/RaiseRequestServiceMockTest.java b/src/test/java/nl/tudelft/tam/service/RaiseRequestServiceMockTest.java index 2ca32b910dd9f7e21e1f4ad71f16860b2cbb8c3f..c59a50286de961bfdeaeeb154e66973c2dadb1dc 100644 --- a/src/test/java/nl/tudelft/tam/service/RaiseRequestServiceMockTest.java +++ b/src/test/java/nl/tudelft/tam/service/RaiseRequestServiceMockTest.java @@ -26,6 +26,7 @@ import java.util.List; import java.util.Optional; import nl.tudelft.labracore.api.dto.PersonSummaryDTO; +import nl.tudelft.labracore.api.dto.ProgramSummaryDTO; import nl.tudelft.labracore.lib.security.user.Person; import nl.tudelft.tam.dto.create.RaiseRequestCreateDTO; import nl.tudelft.tam.dto.view.details.RaiseRequestDetailsDTO; @@ -65,6 +66,9 @@ class RaiseRequestServiceMockTest { @MockBean PersonService personService; + @MockBean + ProgramService programService; + @MockBean ProfileService profileService; @@ -142,7 +146,8 @@ class RaiseRequestServiceMockTest { when(personService.getPeopleById(anyList())).thenReturn(List.of()); - assertThrows(InvalidDatabaseEntryException.class, () -> raiseRequestService.getAllSubmitted()); + assertThrows(InvalidDatabaseEntryException.class, + () -> raiseRequestService.getAllSubmitted(List.of(1L))); verify(personService).getPeopleById(List.of(STUDENT_ID)); } @@ -153,8 +158,10 @@ class RaiseRequestServiceMockTest { String studentName = "abc-student-4984654"; when(personService.getPeopleById(anyList())) .thenReturn(List.of(new PersonSummaryDTO().id(STUDENT_ID).externalId(studentName))); + when(programService.getAllRelevantPrograms(anyLong())) + .thenReturn(List.of(new ProgramSummaryDTO().id(1L).name("Test"))); - List<RaiseRequestDetailsDTO> result = raiseRequestService.getAllSubmitted(); + List<RaiseRequestDetailsDTO> result = raiseRequestService.getAllSubmitted(List.of(1L)); assertThat(result.size()).isEqualTo(1); assertThat(result.get(0).getId()).isEqualTo(REQUEST_ID); diff --git a/src/test/java/nl/tudelft/tam/service/RaiseRequestServiceTest.java b/src/test/java/nl/tudelft/tam/service/RaiseRequestServiceTest.java index ad72801964438ba7e63c0f5ac2342182da3de406..497ecf43b36ab4939d2a31fdceccf73814544e86 100644 --- a/src/test/java/nl/tudelft/tam/service/RaiseRequestServiceTest.java +++ b/src/test/java/nl/tudelft/tam/service/RaiseRequestServiceTest.java @@ -22,6 +22,7 @@ import static org.junit.jupiter.api.Assertions.*; import java.io.IOException; import java.util.HashMap; +import java.util.List; import java.util.Map; import javax.transaction.Transactional; @@ -142,8 +143,6 @@ class RaiseRequestServiceTest { boolean res = raiseRequestService.newRaiseRequest(createDTO, fileb); assertThat(res).isFalse(); - - //TODO proof didn't get replaced } @Test @@ -156,8 +155,6 @@ class RaiseRequestServiceTest { assertThat(res).isTrue(); assertThat(raiseRequestRepository.findByRequesterAndSubmitted(profile.getId())).isPresent(); - - // TODO check proof is stored } @Test @@ -170,8 +167,6 @@ class RaiseRequestServiceTest { assertThat(res).isTrue(); assertThat(raiseRequestRepository.findByRequesterAndSubmitted(profile.getId())).isPresent(); - - // TODO check request is retracted } @Test @@ -196,7 +191,7 @@ class RaiseRequestServiceTest { void getAllSubmittedEmpty() throws InvalidDatabaseEntryException { raiseRequestRepository.deleteAll(); - assertThat(raiseRequestService.getAllSubmitted().size()).isEqualTo(0); + assertThat(raiseRequestService.getAllSubmitted(List.of()).size()).isEqualTo(0); } @Test diff --git a/src/test/java/nl/tudelft/tam/service/TrainingApprovalRequestServiceMockTest.java b/src/test/java/nl/tudelft/tam/service/TrainingApprovalRequestServiceMockTest.java index 44e97358ea63e650869c6a82c48adc4c1664cc70..7b0a94fadae8848de36728d30203eb56a520cac7 100644 --- a/src/test/java/nl/tudelft/tam/service/TrainingApprovalRequestServiceMockTest.java +++ b/src/test/java/nl/tudelft/tam/service/TrainingApprovalRequestServiceMockTest.java @@ -25,6 +25,7 @@ import java.time.LocalDate; import java.util.List; import nl.tudelft.labracore.api.dto.PersonSummaryDTO; +import nl.tudelft.labracore.api.dto.ProgramSummaryDTO; import nl.tudelft.labracore.lib.security.user.Person; import nl.tudelft.tam.dto.view.details.TrainingApprovalRequestDetailsDTO; import nl.tudelft.tam.enums.Status; @@ -57,6 +58,9 @@ class TrainingApprovalRequestServiceMockTest { @MockBean PersonService personService; + @MockBean + ProgramService programService; + @MockBean ProfileService profileService; @@ -134,7 +138,7 @@ class TrainingApprovalRequestServiceMockTest { when(personService.getPeopleById(anyList())).thenReturn(List.of()); assertThrows(InvalidDatabaseEntryException.class, - () -> trainingApprovalRequestService.getAllSubmitted()); + () -> trainingApprovalRequestService.getAllSubmitted(List.of(1L))); verify(personService).getPeopleById(List.of(STUDENT_ID)); } @@ -146,8 +150,11 @@ class TrainingApprovalRequestServiceMockTest { String studentName = "abc-student-4984654"; when(personService.getPeopleById(anyList())) .thenReturn(List.of(new PersonSummaryDTO().id(STUDENT_ID).externalId(studentName))); + when(programService.getAllRelevantPrograms(anyLong())) + .thenReturn(List.of(new ProgramSummaryDTO().id(1L).name("Test"))); - List<TrainingApprovalRequestDetailsDTO> result = trainingApprovalRequestService.getAllSubmitted(); + List<TrainingApprovalRequestDetailsDTO> result = trainingApprovalRequestService + .getAllSubmitted(List.of(1L)); assertThat(result.size()).isEqualTo(1); assertThat(result.get(0).getId()).isEqualTo(REQUEST_ID); diff --git a/src/test/java/nl/tudelft/tam/service/TrainingApprovalRequestServiceTest.java b/src/test/java/nl/tudelft/tam/service/TrainingApprovalRequestServiceTest.java index f7982aafdfebc363ef34fa42451a26197757b5c5..24798812b9472fe24fbf74a95c81b64620406db9 100644 --- a/src/test/java/nl/tudelft/tam/service/TrainingApprovalRequestServiceTest.java +++ b/src/test/java/nl/tudelft/tam/service/TrainingApprovalRequestServiceTest.java @@ -20,6 +20,7 @@ package nl.tudelft.tam.service; import static org.assertj.core.api.Assertions.assertThat; import java.time.LocalDate; +import java.util.List; import javax.transaction.Transactional; @@ -128,7 +129,7 @@ class TrainingApprovalRequestServiceTest { void getAllSubmittedEmpty() throws InvalidDatabaseEntryException { trainingApprovalRequestRepository.deleteAll(); - assertThat(trainingApprovalRequestService.getAllSubmitted().size()).isEqualTo(0); + assertThat(trainingApprovalRequestService.getAllSubmitted(List.of(1L)).size()).isEqualTo(0); } @Test