Improve supervisor change integration #114

Merged
ansv7779 merged 4 commits from supervisor-change-daisy-integration into develop 2025-02-21 00:27:23 +01:00
7 changed files with 79 additions and 63 deletions

View File

@ -1,7 +1,10 @@
package se.su.dsv.scipro.reusable; package se.su.dsv.scipro.reusable;
import java.time.LocalDate; import java.util.ArrayList;
import java.util.*; import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
public final class SciProUtilities { public final class SciProUtilities {
@ -75,15 +78,4 @@ public final class SciProUtilities {
} }
return copy; return copy;
} }
public static Calendar toCalendar(final LocalDate localDate) {
if (localDate == null) {
return null;
} else {
final Calendar calendar = Calendar.getInstance();
calendar.clear();
calendar.set(localDate.getYear(), localDate.getMonthValue() - 1, localDate.getDayOfMonth());
return calendar;
}
}
} }

View File

@ -9,6 +9,10 @@
parseMethod="jakarta.xml.bind.DatatypeConverter.parseDateTime" parseMethod="jakarta.xml.bind.DatatypeConverter.parseDateTime"
printMethod="jakarta.xml.bind.DatatypeConverter.printDateTime" /> printMethod="jakarta.xml.bind.DatatypeConverter.printDateTime" />
<jaxb:javaType name="java.time.LocalDate" xmlType="xs:date"
parseMethod="java.time.LocalDate.parse"
printMethod="java.time.format.DateTimeFormatter.ISO_LOCAL_DATE.format" />
<!-- Force all classes implements Serializable --> <!-- Force all classes implements Serializable -->
<xjc:simple /> <xjc:simple />
<xjc:serializable uid="1" /> <xjc:serializable uid="1" />

View File

@ -375,14 +375,16 @@
</xs:element> </xs:element>
<xs:element name="title_en" type="xs:string" minOccurs="0"> <xs:element name="title_en" type="xs:string" minOccurs="0">
</xs:element> </xs:element>
<xs:element name="startDate" type="xs:dateTime" minOccurs="0"> <xs:element name="startDate" type="xs:date" minOccurs="0">
</xs:element> </xs:element>
<xs:element name="endDate" type="xs:dateTime" minOccurs="0"> <xs:element name="endDate" type="xs:date" minOccurs="0">
</xs:element> </xs:element>
<xs:element name="unit" type="serializableUnit" minOccurs="0"> <xs:element name="unit" type="serializableUnit" minOccurs="0">
</xs:element> </xs:element>
<xs:element name="researchAreas" type="researchAreas" minOccurs="0"> <xs:element name="researchAreas" type="researchAreas" minOccurs="0">
</xs:element> </xs:element>
<xs:element name="externalID" type="xs:string" minOccurs="0">
</xs:element>
</xs:sequence> </xs:sequence>
</xs:complexType> </xs:complexType>
@ -390,13 +392,15 @@
<xs:sequence> <xs:sequence>
<xs:element name="title_en" type="xs:string" minOccurs="0"> <xs:element name="title_en" type="xs:string" minOccurs="0">
</xs:element> </xs:element>
<xs:element name="externalID" type="xs:string" minOccurs="0">
</xs:element>
<xs:element name="aborted" type="xs:boolean" minOccurs="1"> <xs:element name="aborted" type="xs:boolean" minOccurs="1">
</xs:element> </xs:element>
<xs:element name="level" type="educationalLevel" minOccurs="0"> <xs:element name="level" type="educationalLevel" minOccurs="0">
</xs:element> </xs:element>
<xs:element name="title" type="xs:string" minOccurs="0"> <xs:element name="title" type="xs:string" minOccurs="0">
</xs:element> </xs:element>
<xs:element name="endDate" type="xs:dateTime" minOccurs="0"> <xs:element name="endDate" type="xs:date" minOccurs="0">
</xs:element> </xs:element>
<xs:element name="status" type="STATUS" minOccurs="0"> <xs:element name="status" type="STATUS" minOccurs="0">
</xs:element> </xs:element>
@ -404,7 +408,7 @@
</xs:element> </xs:element>
<xs:element name="finished" type="xs:boolean" minOccurs="1"> <xs:element name="finished" type="xs:boolean" minOccurs="1">
</xs:element> </xs:element>
<xs:element name="startDate" type="xs:dateTime" minOccurs="0"> <xs:element name="startDate" type="xs:date" minOccurs="0">
</xs:element> </xs:element>
<xs:element name="id" type="xs:int" minOccurs="1"> <xs:element name="id" type="xs:int" minOccurs="1">
</xs:element> </xs:element>
@ -475,9 +479,11 @@
</xs:element> </xs:element>
<xs:element name="title_en" type="xs:string" minOccurs="0"> <xs:element name="title_en" type="xs:string" minOccurs="0">
</xs:element> </xs:element>
<xs:element name="startDate" type="xs:dateTime" minOccurs="0"> <xs:element name="externalID" type="xs:string" minOccurs="0">
</xs:element> </xs:element>
<xs:element name="endDate" type="xs:dateTime" minOccurs="0"> <xs:element name="startDate" type="xs:date" minOccurs="0">
</xs:element>
<xs:element name="endDate" type="xs:date" minOccurs="0">
</xs:element> </xs:element>
<xs:element name="aborted" type="xs:boolean" minOccurs="1"> <xs:element name="aborted" type="xs:boolean" minOccurs="1">
</xs:element> </xs:element>

View File

@ -4,10 +4,9 @@ import static com.querydsl.core.types.dsl.Expressions.anyOf;
import jakarta.inject.Inject; import jakarta.inject.Inject;
import jakarta.inject.Named; import jakarta.inject.Named;
import jakarta.ws.rs.core.Response;
import java.time.Instant; import java.time.Instant;
import java.time.LocalDate;
import java.time.Period; import java.time.Period;
import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
@ -38,7 +37,6 @@ import se.su.dsv.scipro.project.Project;
import se.su.dsv.scipro.project.ProjectRepo; import se.su.dsv.scipro.project.ProjectRepo;
import se.su.dsv.scipro.project.ProjectStatus; import se.su.dsv.scipro.project.ProjectStatus;
import se.su.dsv.scipro.project.QProject; import se.su.dsv.scipro.project.QProject;
import se.su.dsv.scipro.reusable.SciProUtilities;
import se.su.dsv.scipro.system.DegreeType; import se.su.dsv.scipro.system.DegreeType;
import se.su.dsv.scipro.system.User; import se.su.dsv.scipro.system.User;
import se.su.dsv.scipro.workerthreads.AbstractWorker; import se.su.dsv.scipro.workerthreads.AbstractWorker;
@ -198,7 +196,7 @@ public class ProjectExporter extends AbstractWorker {
UnitWithID unit = new UnitWithID(); UnitWithID unit = new UnitWithID();
unit.setId(project.getHeadSupervisor().getUnit().getIdentifier()); unit.setId(project.getHeadSupervisor().getUnit().getIdentifier());
Calendar startDate = SciProUtilities.toCalendar(project.getStartDate()); LocalDate startDate = project.getStartDate();
String title = finalThesis String title = finalThesis
.map(FinalThesis::getSwedishTitle) .map(FinalThesis::getSwedishTitle)
@ -211,7 +209,7 @@ public class ProjectExporter extends AbstractWorker {
thesis.setUnit(unit); thesis.setUnit(unit);
thesis.setAborted(project.getProjectStatus() == ProjectStatus.INACTIVE); thesis.setAborted(project.getProjectStatus() == ProjectStatus.INACTIVE);
thesis.setStartDate(startDate); thesis.setStartDate(startDate);
thesis.setEndDate(SciProUtilities.toCalendar(project.getExpectedEndDate())); thesis.setEndDate(project.getExpectedEndDate());
final ResearchAreas researchAreas = new ResearchAreas(); final ResearchAreas researchAreas = new ResearchAreas();
if (project.getResearchArea() != null && project.getResearchArea().getIdentifier() != null) { if (project.getResearchArea() != null && project.getResearchArea().getIdentifier() != null) {
final ResearchAreaWithID researchArea2 = new ResearchAreaWithID(); final ResearchAreaWithID researchArea2 = new ResearchAreaWithID();
@ -260,36 +258,14 @@ public class ProjectExporter extends AbstractWorker {
} }
private void updateHeadSupervisor(Project project) throws ExternalExportException { private void updateHeadSupervisor(Project project) throws ExternalExportException {
boolean toAdd = true; boolean shouldChange = true;
Set<ProjectParticipant> contributors = daisyAPI.getContributors(project.getIdentifier()); Set<ProjectParticipant> contributors = daisyAPI.getContributors(project.getIdentifier());
for (ProjectParticipant contributor : contributors) { for (ProjectParticipant contributor : contributors) {
if (contributor.getRole() == Role.SUPERVISOR) { if (contributor.getRole() == Role.SUPERVISOR) {
if (contributor.getPerson().getId().equals(project.getHeadSupervisor().getIdentifier())) { shouldChange = !contributor.getPerson().getId().equals(project.getHeadSupervisor().getIdentifier());
toAdd = false;
} else {
Response response = daisyAPI.deleteThesisPerson(
project.getIdentifier(),
contributor.getPerson().getId()
);
if (response.getStatusInfo().getFamily() != Response.Status.Family.SUCCESSFUL) {
LOG.warn(
"Failed to delete supervisor from project: {} (id: {} / {})",
project.getTitle(),
project.getId(),
project.getIdentifier()
);
// Card 3413
// Do not attempt to add the new supervisor if the old one is still there to prevent
// the project from having multiple supervisors.
// Hopefully Daisy API will soon have support for the function "change supervisor"
// rather than just INSERT and DELETE rows in a table.
toAdd = false;
} }
} }
} if (shouldChange) {
}
if (toAdd) {
externalExporter.addContributorToProject(project, project.getHeadSupervisor(), Role.SUPERVISOR); externalExporter.addContributorToProject(project, project.getHeadSupervisor(), Role.SUPERVISOR);
} }
} }

View File

@ -3,7 +3,7 @@ package se.su.dsv.scipro.io.impl;
import jakarta.inject.Inject; import jakarta.inject.Inject;
import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Calendar; import java.time.LocalDate;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import se.su.dsv.scipro.daisyexternal.http.DaisyAPI; import se.su.dsv.scipro.daisyexternal.http.DaisyAPI;
@ -21,7 +21,6 @@ import se.su.dsv.scipro.io.dto.ThesisToBeCreated;
import se.su.dsv.scipro.io.dto.UnitWithID; import se.su.dsv.scipro.io.dto.UnitWithID;
import se.su.dsv.scipro.io.exceptions.ExternalExportException; import se.su.dsv.scipro.io.exceptions.ExternalExportException;
import se.su.dsv.scipro.project.Project; import se.su.dsv.scipro.project.Project;
import se.su.dsv.scipro.reusable.SciProUtilities;
import se.su.dsv.scipro.system.Unit; import se.su.dsv.scipro.system.Unit;
import se.su.dsv.scipro.system.User; import se.su.dsv.scipro.system.User;
@ -46,9 +45,9 @@ public class ExternalExporterDaisyImpl implements ExternalExporter {
ThesisToBeCreated exportProjectDTO = new ThesisToBeCreated(); ThesisToBeCreated exportProjectDTO = new ThesisToBeCreated();
exportProjectDTO.setTitle(truncate(project.getTitle())); exportProjectDTO.setTitle(truncate(project.getTitle()));
Calendar startDate = SciProUtilities.toCalendar(project.getStartDate()); LocalDate startDate = project.getStartDate();
exportProjectDTO.setStartDate(startDate); exportProjectDTO.setStartDate(startDate);
exportProjectDTO.setEndDate(SciProUtilities.toCalendar(project.getExpectedEndDate())); exportProjectDTO.setEndDate(project.getExpectedEndDate());
exportProjectDTO.setUnit(unitDTO); exportProjectDTO.setUnit(unitDTO);
final ResearchAreas researchAreas = new ResearchAreas(); final ResearchAreas researchAreas = new ResearchAreas();
if (project.getResearchArea() != null && project.getResearchArea().getIdentifier() != null) { if (project.getResearchArea() != null && project.getResearchArea().getIdentifier() != null) {

View File

@ -2,6 +2,7 @@ package se.su.dsv.scipro.integration.daisy.workers;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.*; import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@ -144,22 +145,61 @@ public class ProjectExporterTest {
} }
@Test @Test
public void deletes_wrong_head_supervisor() { public void change_head_supervisor() throws Exception {
Person person = new Person(); User newSupervisor = User.builder()
person.setId(234234); .firstName("New")
.lastName("Supervisor")
.emailAddress("new@supervisor.com")
.build();
Unit unit = new Unit();
unit.setIdentifier(239478);
ProjectParticipant headSupervisor = new ProjectParticipant(); newSupervisor.setIdentifier(12345);
headSupervisor.setRole(Role.SUPERVISOR); newSupervisor.setUnit(unit);
headSupervisor.setPerson(person); exportedProject.setHeadSupervisor(newSupervisor);
Set<ProjectParticipant> contributors = new HashSet<>(); Set<ProjectParticipant> contributors = new HashSet<>();
contributors.add(headSupervisor); ProjectParticipant oldSupervisor = new ProjectParticipant();
oldSupervisor.setRole(Role.SUPERVISOR);
Person oldPerson = new Person();
oldPerson.setId(SUPERVISOR_IDENTIFIER);
oldSupervisor.setPerson(oldPerson);
contributors.add(oldSupervisor);
when(daisyAPI.getContributors(exportedProject.getIdentifier())).thenReturn(contributors); when(daisyAPI.getContributors(exportedProject.getIdentifier())).thenReturn(contributors);
projectExporter.run(); projectExporter.run();
verify(daisyAPI).deleteThesisPerson(exportedProject.getIdentifier(), person.getId()); verify(externalExporter).addContributorToProject(exportedProject, newSupervisor, Role.SUPERVISOR);
}
@Test
public void should_not_change_head_supervisor_if_already_exists() throws Exception {
User newSupervisor = User.builder()
.firstName("New")
.lastName("Supervisor")
.emailAddress("new@supervisor.com")
.build();
Unit unit = new Unit();
unit.setIdentifier(239478);
newSupervisor.setIdentifier(SUPERVISOR_IDENTIFIER);
newSupervisor.setUnit(unit);
exportedProject.setHeadSupervisor(newSupervisor);
Set<ProjectParticipant> contributors = new HashSet<>();
ProjectParticipant oldSupervisor = new ProjectParticipant();
oldSupervisor.setRole(Role.SUPERVISOR);
Person oldPerson = new Person();
oldPerson.setId(SUPERVISOR_IDENTIFIER);
oldSupervisor.setPerson(oldPerson);
contributors.add(oldSupervisor);
when(daisyAPI.getContributors(exportedProject.getIdentifier())).thenReturn(contributors);
projectExporter.run();
verify(externalExporter, never()).addContributorToProject(exportedProject, newSupervisor, Role.SUPERVISOR);
} }
@Test @Test

View File

@ -19,7 +19,6 @@ import org.mockito.junit.jupiter.MockitoExtension;
import se.su.dsv.scipro.daisyexternal.http.DaisyAPI; import se.su.dsv.scipro.daisyexternal.http.DaisyAPI;
import se.su.dsv.scipro.io.dto.ThesisToBeCreated; import se.su.dsv.scipro.io.dto.ThesisToBeCreated;
import se.su.dsv.scipro.project.Project; import se.su.dsv.scipro.project.Project;
import se.su.dsv.scipro.reusable.SciProUtilities;
import se.su.dsv.scipro.system.DegreeType; import se.su.dsv.scipro.system.DegreeType;
import se.su.dsv.scipro.system.ProjectType; import se.su.dsv.scipro.system.ProjectType;
import se.su.dsv.scipro.system.Unit; import se.su.dsv.scipro.system.Unit;
@ -71,6 +70,6 @@ public class ExternalExporterDaisyImplTest {
ArgumentCaptor<ThesisToBeCreated> captor = ArgumentCaptor.forClass(ThesisToBeCreated.class); ArgumentCaptor<ThesisToBeCreated> captor = ArgumentCaptor.forClass(ThesisToBeCreated.class);
verify(daisyAPI).createProject(captor.capture()); verify(daisyAPI).createProject(captor.capture());
assertEquals(captor.getValue().getStartDate(), SciProUtilities.toCalendar(daisyStartDate)); assertEquals(captor.getValue().getStartDate(), daisyStartDate);
} }
} }