Skip to content
Snippets Groups Projects
Commit c9d9bd9b authored by Ruben Backx's avatar Ruben Backx :coffee:
Browse files

Merge branch 85-96-97-98-add-defaults with refs/heads/dev into refs/merge-requests/210/train

parents 8e9b18e6 0693f668
No related branches found
No related tags found
No related merge requests found
Pipeline #748002 passed
Showing
with 545 additions and 6 deletions
......@@ -19,6 +19,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* Exporting all extra work declarations @toberhuber
* Search by name when offering a position to a specific person @toberhuber
* Spotless for the frontend (HTML, SCSS, JS) @toberhuber
* Contract name, start date, end date, and baan code for extra work @toberhuber
* Coordinator defaults for job offer deadlines, extra work baan codes and start/end dates @toberhuber
* A cohort cache @toberhuber
* TA training approval page @toberhuber
* Open job offers are now sorted by cohort, then start date, then name @toberhuber
* Scroll to same location after accepting, rejecting or sending job offers.
......
......
......@@ -150,21 +150,37 @@ public class DevDatabaseLoader {
oop_now_ew_grading = extraWorkRepository.save(ExtraWork.builder()
.name("grading (no deadline)")
.editionId(ID_OOP_NOW)
.contractName("OOP-NOW-Grading")
.contractStartDate(LocalDate.now())
.contractEndDate(LocalDate.now().plusWeeks(2))
.baanCode("OOP BAAN CODE")
.deadline(null)
.build());
oop_now_ew_oral_check = extraWorkRepository.save(ExtraWork.builder()
.name("oral check (closes today)")
.editionId(ID_OOP_NOW)
.contractName("OOP-NOW-OralCheck")
.contractStartDate(LocalDate.now())
.contractEndDate(LocalDate.now().plusWeeks(2))
.baanCode("OOP BAAN CODE")
.deadline(LocalDate.now())
.build());
ads_now_ew_grading = extraWorkRepository.save(ExtraWork.builder()
.name("grading (closes in a week)")
.editionId(ID_ADS_NOW)
.contractName("ADS-NOW-Grading")
.contractStartDate(LocalDate.now())
.contractEndDate(LocalDate.now().plusWeeks(2))
.baanCode("ADS BAAN CODE")
.deadline(LocalDate.now().plusDays(7))
.build());
sqt_now_ew_grading = extraWorkRepository.save(ExtraWork.builder()
.name("grading (closed)")
.editionId(ID_SQT_NOW)
.contractName("SQT-NOW-Grading")
.contractStartDate(LocalDate.now())
.contractEndDate(LocalDate.now().plusWeeks(1))
.baanCode("SQT BAAN CODE")
.deadline(LocalDate.now().minusDays(1))
.build());
}
......
......
/*
* TAM
* Copyright (C) 2021 - Delft University of Technology
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package nl.tudelft.tam.cache;
import java.util.List;
import java.util.stream.Collectors;
import nl.tudelft.labracore.api.CohortControllerApi;
import nl.tudelft.labracore.api.dto.CohortDetailsDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.annotation.RequestScope;
@Component
@RequestScope
public class CohortCacheManager extends CoreCacheManager<Long, CohortDetailsDTO> {
@Autowired
private CohortControllerApi api;
@Override
protected List<CohortDetailsDTO> fetch(List<Long> ids) {
return ids.stream().map(id -> api.getCohortById(id).block()).collect(Collectors.toList());
}
@Override
protected Long getId(CohortDetailsDTO dto) {
return dto.getId();
}
}
/*
* TAM
* Copyright (C) 2021 - Delft University of Technology
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package nl.tudelft.tam.controller;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import javax.validation.Valid;
import nl.tudelft.labracore.lib.security.user.AuthenticatedPerson;
import nl.tudelft.labracore.lib.security.user.Person;
import nl.tudelft.tam.dto.patch.CoordinatorDefaultPatchDTO;
import nl.tudelft.tam.model.Coordinator;
import nl.tudelft.tam.model.CoordinatorDefault;
import nl.tudelft.tam.service.CohortService;
import nl.tudelft.tam.service.CoordinatorDefaultService;
import nl.tudelft.tam.service.CoordinatorService;
import nl.tudelft.tam.service.ProgramService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
@Controller
@RequestMapping("coordinator")
public class CoordinatorController {
@Autowired
CoordinatorDefaultService coordinatorDefaultService;
@Autowired
CoordinatorService coordinatorService;
@Autowired
ProgramService programService;
@Autowired
CohortService cohortService;
/**
* Allows the coordinator to set defaults for their program
*
* @return The defaults page
*/
@GetMapping("defaults")
@PreAuthorize("@authorisationService.isCoordinatorOfAny()")
public String getDefaults(@AuthenticatedPerson Person person, Model model) {
List<Long> coordinatingPrograms = coordinatorService.findAllByPersonId(person.getId()).stream()
.map(Coordinator::getProgramId).collect(Collectors.toList());
Map<String, CoordinatorDefault> programNamesAndDefaults = new HashMap<>();
for (Long program : coordinatingPrograms) {
programNamesAndDefaults.put(programService.get(program).getName(),
coordinatorDefaultService.getOrCreate(program));
}
model.addAttribute("programNamesAndDefaults", programNamesAndDefaults);
model.addAttribute("patchCoordinatorDefault", new CoordinatorDefaultPatchDTO());
return "coordinator/defaults";
}
@PatchMapping("defaults/{programId}")
@PreAuthorize("@authorisationService.isCoordinatorForProgram(#programId)")
public String modifyDefaults(@PathVariable Long programId,
@Valid @ModelAttribute("patchCoordinatorDefault") CoordinatorDefaultPatchDTO dto) {
coordinatorDefaultService.patchCoordinatorDefault(programId, dto);
return "redirect:/coordinator/defaults";
}
}
......@@ -172,7 +172,7 @@ public class ExtraWorkController {
* @param dto The data
* @return The specific extra work page
*/
@PostMapping("{id}")
@PatchMapping("{id}")
@PreAuthorize("@authorisationService.isManagerOfWork(#id)")
public String patchExtraWork(@PathVariable Long id,
@Valid @ModelAttribute("patchExtraWork") ExtraWorkPatchDTO dto) {
......
......
......@@ -84,6 +84,9 @@ public class JobOfferController {
@Autowired
CoordinatorService coordinatorService;
@Autowired
CoordinatorDefaultService coordinatorDefaultService;
// endregion
// region view pages
......@@ -173,6 +176,8 @@ public class JobOfferController {
List<EditionDetailsOffersWorkDTO> editions = editionService
.filter(editionService.getEditionsWithJobsAndWork(editionIds), q);
coordinatorDefaultService.addDefaultsToEditionsWithJobsAndWork(editions);
if (hidePrev == null || hidePrev) {
editions = editions.stream().filter(x -> !x.getEndDate()
.isBefore(LocalDateTime.now())).collect(Collectors.toList());
......@@ -218,6 +223,8 @@ public class JobOfferController {
.collect(Collectors.toList()),
q);
coordinatorDefaultService.addDefaultsToEditionsWithJobsAndWork(editions);
if (hidePrev == null || hidePrev) {
editions = editions.stream().filter(x -> !x.getEndDate()
.isBefore(LocalDateTime.now())).collect(Collectors.toList());
......@@ -236,7 +243,7 @@ public class JobOfferController {
* Gets the details of a specific job offer
*
* @param id The id of the job offer
* @param q The search quey
* @param q The search query
* @param model The model to add the data to
* @return The specific job offer page
*/
......@@ -286,7 +293,7 @@ public class JobOfferController {
* @param dto The data of the job offer
* @return The specific job offer page
*/
@PostMapping("{id}")
@PatchMapping("{id}")
@PreAuthorize("@authorisationService.isManagerOfJob(#id)")
public String patchJobOffer(@PathVariable Long id,
@Valid @ModelAttribute("patchJobOffer") JobOfferPatchDTO dto) {
......
......
......@@ -85,7 +85,7 @@ public class TAMProfileController {
* @return a redirect to the view page
*/
@Transactional
@PostMapping("polo")
@PatchMapping("polo")
public String updatePoloData(@AuthenticatedPerson Person person,
@ModelAttribute("poloPatch") ShirtPatchDTO patch) {
profileService.updatePoloData(person.getId(), patch);
......
......
......@@ -39,6 +39,16 @@ public class ExtraWorkCreateDTO extends Create<ExtraWork> {
private String name;
private String contractName;
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate contractStartDate;
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate contractEndDate;
private String baanCode;
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate deadline;
......@@ -54,8 +64,16 @@ public class ExtraWorkCreateDTO extends Create<ExtraWork> {
data.setEditionId(editionId);
if (name != null && !name.isBlank())
data.setName(name);
if (contractName != null && !contractName.isBlank())
data.setContractName(contractName);
if (contractStartDate != null)
data.setContractStartDate(contractStartDate);
if (contractEndDate != null)
data.setContractEndDate(contractEndDate);
if (deadline != null)
data.setDeadline(deadline);
if (baanCode != null && !baanCode.isBlank())
data.setBaanCode(baanCode);
postApply(data);
return data;
}
......
......
/*
* TAM
* Copyright (C) 2021 - Delft University of Technology
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package nl.tudelft.tam.dto.patch;
import java.time.LocalDate;
import lombok.*;
import nl.tudelft.librador.dto.patch.Patch;
import nl.tudelft.tam.model.CoordinatorDefault;
import org.springframework.format.annotation.DateTimeFormat;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = false)
public class CoordinatorDefaultPatchDTO extends Patch<CoordinatorDefault> {
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate applicationDeadline;
private String contractBaanCode;
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate contractStartDate;
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate contractEndDate;
@Override
protected void applyOneToOne() {
updateNonNull(contractBaanCode, data::setContractBaanCode);
data.setApplicationDeadline(applicationDeadline);
data.setContractStartDate(contractStartDate);
data.setContractEndDate(contractEndDate);
}
@Override
protected void validate() {
}
}
......@@ -34,13 +34,27 @@ public class ExtraWorkPatchDTO extends Patch<ExtraWork> {
private String name;
private String contractName;
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate contractStartDate;
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate contractEndDate;
private String baanCode;
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate deadline;
@Override
protected void applyOneToOne() {
updateNonNull(name, data::setName);
updateNonNull(contractName, data::setContractName);
data.setContractStartDate(contractStartDate);
data.setContractEndDate(contractEndDate);
data.setDeadline(deadline);
updateNonNull(baanCode, data::setBaanCode);
}
@Override
......
......
......@@ -26,6 +26,7 @@ import lombok.NoArgsConstructor;
import nl.tudelft.labracore.api.dto.EditionDetailsDTO;
import nl.tudelft.tam.dto.view.summary.ExtraWorkSummaryDTO;
import nl.tudelft.tam.dto.view.summary.JobOfferSummaryDTO;
import nl.tudelft.tam.model.CoordinatorDefault;
@Data
@NoArgsConstructor
......@@ -37,4 +38,6 @@ public class EditionDetailsOffersWorkDTO extends EditionDetailsDTO {
List<ExtraWorkSummaryDTO> work;
CoordinatorDefault defaults;
}
......@@ -40,6 +40,14 @@ public class ExtraWorkSummaryDTO extends View<ExtraWork> {
private String name;
private String contractName;
private LocalDate contractStartDate;
private LocalDate contractEndDate;
private String baanCode;
private LocalDate deadline;
}
/*
* TAM
* Copyright (C) 2021 - Delft University of Technology
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package nl.tudelft.tam.model;
import java.time.LocalDate;
import javax.annotation.Nullable;
import javax.persistence.Entity;
import javax.persistence.Id;
import lombok.*;
@Data
@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CoordinatorDefault {
@Id
@NonNull
private Long programId;
@Nullable
private LocalDate applicationDeadline;
@Nullable
private String contractName;
@Nullable
private String contractBaanCode;
@Nullable
private LocalDate contractStartDate;
@Nullable
private LocalDate contractEndDate;
}
......@@ -44,6 +44,18 @@ public class ExtraWork {
@NotNull
private String name;
@NotNull
private String contractName;
@NotNull
private LocalDate contractStartDate;
@NotNull
private LocalDate contractEndDate;
@NotNull
private String baanCode;
@Nullable
private LocalDate deadline;
......
......
/*
* TAM
* Copyright (C) 2021 - Delft University of Technology
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package nl.tudelft.tam.repository;
import nl.tudelft.tam.model.CoordinatorDefault;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.rest.webmvc.ResourceNotFoundException;
public interface CoordinatorDefaultRepository extends JpaRepository<CoordinatorDefault, Long> {
/**
* Finds the coordinator default by id or throws a ResourceNotFoundException.
*
* @param id the id of the coordinator default
* @return the CoordinatorDefault object
*/
default CoordinatorDefault findByIdOrThrow(Long id) {
// Spring will actually wrap this in a JpaObjectRetrievalFailureException
return findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Coordinator Default was not found: " + id));
}
}
......@@ -20,7 +20,9 @@ package nl.tudelft.tam.service;
import java.util.List;
import nl.tudelft.labracore.api.CohortControllerApi;
import nl.tudelft.labracore.api.dto.CohortDetailsDTO;
import nl.tudelft.labracore.api.dto.CohortSummaryDTO;
import nl.tudelft.tam.cache.CohortCacheManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
......@@ -31,6 +33,9 @@ public class CohortService {
@Autowired
private CohortControllerApi cohortControllerApi;
@Autowired
private CohortCacheManager cohortCache;
/**
* Gets all cohorts in the database.
*
......@@ -39,4 +44,13 @@ public class CohortService {
public List<CohortSummaryDTO> getAllCohorts() {
return cohortControllerApi.getAllCohorts().collectList().block();
}
/**
* Gets cohort details by id.
*
* @return The CohortDetailsDTO object
*/
public CohortDetailsDTO getOrThrowById(Long id) {
return cohortCache.getOrThrow(id);
}
}
/*
* TAM
* Copyright (C) 2021 - Delft University of Technology
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package nl.tudelft.tam.service;
import java.util.List;
import java.util.Objects;
import javax.transaction.Transactional;
import nl.tudelft.tam.dto.patch.CoordinatorDefaultPatchDTO;
import nl.tudelft.tam.dto.view.details.EditionDetailsOffersWorkDTO;
import nl.tudelft.tam.model.CoordinatorDefault;
import nl.tudelft.tam.repository.CoordinatorDefaultRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.rest.webmvc.ResourceNotFoundException;
import org.springframework.stereotype.Service;
@Service
public class CoordinatorDefaultService {
@Autowired
CoordinatorDefaultRepository repository;
@Autowired
CohortService cohortService;
/**
* Finds the coordinator default by id
*
* @param id The id of the coordinator default to find
* @return The coordinator default object found
*/
public CoordinatorDefault findByIdOrThrow(Long id) {
return repository.findByIdOrThrow(id);
}
/**
* Either gets the existing defaults from the repo, or creates a new set of blank defaults for the
* programme
*
* @param id The program id for which to get the defaults
* @return The CoordinatorDefault object
*/
public CoordinatorDefault getOrCreate(Long id) {
try {
return findByIdOrThrow(id);
} catch (ResourceNotFoundException e) {
return repository.save(CoordinatorDefault.builder().programId(id).build());
}
}
/**
* Adds the coordinator defaults to each edition depending on the program id
*
* @param editions The list of editions for which to add the defaults
*/
public void addDefaultsToEditionsWithJobsAndWork(List<EditionDetailsOffersWorkDTO> editions) {
for (EditionDetailsOffersWorkDTO edition : editions) {
Long programId = Objects
.requireNonNull(cohortService.getOrThrowById(edition.getCohort().getId()).getProgram())
.getId();
CoordinatorDefault defaults = getOrCreate(programId);
edition.setDefaults(defaults);
}
}
/**
* Patches the coordinator defaults for a program
*
* @param id of the program to change the defaults for
* @param dto the changed defaults
*/
@Transactional
public void patchCoordinatorDefault(Long id, CoordinatorDefaultPatchDTO dto) {
dto.apply(repository.findByIdOrThrow(id));
}
}
/*
* TAM
* Copyright (C) 2021 - Delft University of Technology
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package nl.tudelft.tam.service;
import nl.tudelft.labracore.api.ProgramControllerApi;
import nl.tudelft.labracore.api.dto.ProgramDetailsDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class ProgramService {
@Autowired
ProgramControllerApi programApi;
/**
* Get a program by id
*
* @param id The id of the program to get
* @return The ProgramDetailsDTO object
*/
public ProgramDetailsDTO get(Long id) {
return programApi.getProgramById(id).block();
}
}
......@@ -35,6 +35,9 @@ spring:
main:
allow-circular-references: true
mvc:
hiddenmethod.filter.enabled: true
jpa:
hibernate:
ddl-auto: create
......
......
......@@ -22,6 +22,7 @@ general.error = Oops! Something went wrong :(
general.cancel = Cancel
general.create = Create
general.save = Save
general.saveChanges = Save Changes
general.submit = Submit
general.yes = Yes
general.no = No
......@@ -82,7 +83,7 @@ jobOffer.retract = Retract
jobOffer.description = Job Description
jobOffer.status = Status
jobOffer.add = New Job Offer
jobOffer.deadline = Deadline for applying
jobOffer.deadline = Deadline For Applying
jobOffer.deadline.none = No Deadline
jobOffer.deadline.closed = Closed
jobOffer.date.enter = Enter a deadline...
......@@ -126,6 +127,14 @@ application.status.rejected = Rejected
extraWork = Extra Work
extraWork.many = Extra Work
extraWork.description = Work Description
extraWork.contractName = Contract Name
extraWork.contractName.enter = Enter a name for the FlexDelft contract...
extraWork.contractStartDate = Contract Start Date
extraWork.contractStartDate.enter = Enter a start date for the FlexDelft contract...
extraWork.contractEndDate = Contract End Date
extraWork.contractEndDate.enter = Enter an end date for the FlexDelft contract...
extraWork.baanCode = Baan Code
extraWork.baanCode.enter = Enter the baan code for the FlexDelft contract...
extraWork.time = Time Declared
extraWork.status = Status
extraWork.retract = Retract
......@@ -133,7 +142,7 @@ extraWork.search = Course / Position …
extraWork.empty = No Available Extra Work
extraWork.empty.description = There is currently no open extra work. Come back later to see if there is something new.
extraWork.add = New Extra Work
extraWork.deadline = Deadline for declaring
extraWork.deadline = Deadline For Declaring
extraWork.deadline.none = No Deadline
extraWork.deadline.closed = Closed
extraWork.date.enter = Enter a deadline...
......@@ -231,6 +240,14 @@ training.student_search = Username / Student # / Student Name ...
training.empty = Currently no training approval requests to review.
training.datePassed = Date of Completion
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.defaults.contractName = Contract Name
coordinator.defaults.contractBaan = Baan Code
coordinator.defaults.contractStartDate = Contract Start Date
coordinator.defaults.contractEndDate = Contract End Date
coordinator.defaults.applicationDeadline = Application Deadline
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment