From f33956068def43c3b98a806407b622ca2103ffe5 Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Thu, 20 Mar 2025 09:18:39 +0100 Subject: [PATCH 01/49] 87: Add initial view for splitting project --- .../admin/pages/AdminEditProjectPage.html | 3 + .../admin/pages/AdminEditProjectPage.java | 36 +++++++-- .../admin/pages/AdminSplitProjectPanel.html | 15 ++++ .../admin/pages/AdminSplitProjectPanel.java | 80 +++++++++++++++++++ .../AdminSplitProjectPanel.utf8.properties | 1 + 5 files changed, 129 insertions(+), 6 deletions(-) create mode 100644 view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.html create mode 100644 view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.java create mode 100644 view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.utf8.properties diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminEditProjectPage.html b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminEditProjectPage.html index 4f18134263..ef96d30cfa 100644 --- a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminEditProjectPage.html +++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminEditProjectPage.html @@ -49,6 +49,9 @@ </form> </div> </div> + + <wicket:container wicket:id="splitPanel"/> + </wicket:extend> </body> </html> diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminEditProjectPage.java b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminEditProjectPage.java index d34c954c7f..6e93e74617 100644 --- a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminEditProjectPage.java +++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminEditProjectPage.java @@ -2,15 +2,24 @@ package se.su.dsv.scipro.admin.pages; import com.google.common.eventbus.EventBus; import jakarta.inject.Inject; -import java.time.LocalDate; -import java.util.*; import org.apache.wicket.RestartResponseException; -import org.apache.wicket.markup.html.form.*; +import org.apache.wicket.markup.html.form.DropDownChoice; +import org.apache.wicket.markup.html.form.Form; +import org.apache.wicket.markup.html.form.FormComponent; +import org.apache.wicket.markup.html.form.LambdaChoiceRenderer; +import org.apache.wicket.markup.html.form.RequiredTextField; +import org.apache.wicket.markup.html.form.TextField; import org.apache.wicket.markup.html.panel.FeedbackPanel; import org.apache.wicket.model.IModel; import org.apache.wicket.model.LambdaModel; import org.apache.wicket.request.mapper.parameter.PageParameters; -import se.su.dsv.scipro.components.*; +import se.su.dsv.scipro.components.AutoCompleteRoleProvider; +import se.su.dsv.scipro.components.BootstrapDatePicker; +import se.su.dsv.scipro.components.DatesValidator; +import se.su.dsv.scipro.components.DefaultSelect2MultiChoice; +import se.su.dsv.scipro.components.ResearchAreaChoiceRenderer; +import se.su.dsv.scipro.components.ResearchAreasModel; +import se.su.dsv.scipro.components.SupervisorAutoComplete; import se.su.dsv.scipro.components.menuhighlighting.MenuHighlightAdminProjectManagement; import se.su.dsv.scipro.data.DetachableServiceModel; import se.su.dsv.scipro.data.DetachableServiceModelCollection; @@ -26,9 +35,21 @@ import se.su.dsv.scipro.project.ProjectService; import se.su.dsv.scipro.project.ReviewerAssignedEvent; import se.su.dsv.scipro.security.auth.Authorization; import se.su.dsv.scipro.security.auth.roles.Roles; -import se.su.dsv.scipro.system.*; +import se.su.dsv.scipro.system.ProjectType; +import se.su.dsv.scipro.system.ProjectTypeService; +import se.su.dsv.scipro.system.ResearchArea; +import se.su.dsv.scipro.system.ResearchAreaService; +import se.su.dsv.scipro.system.User; +import se.su.dsv.scipro.system.UserSearchService; +import se.su.dsv.scipro.system.UserService; import se.su.dsv.scipro.util.PageParameterKeys; +import java.time.LocalDate; +import java.util.Collection; +import java.util.EnumSet; +import java.util.List; +import java.util.TreeSet; + @Authorization(authorizedRoles = { Roles.SYSADMIN }) public class AdminEditProjectPage extends AbstractAdminProjectPage implements MenuHighlightAdminProjectManagement { @@ -74,7 +95,10 @@ public class AdminEditProjectPage extends AbstractAdminProjectPage implements Me if (project == null) { throw new RestartResponseException(AdminCreateProjectPage.class); } - add(new ProjectForm(FORM, new DetachableServiceModel<>(projectService, project))); + DetachableServiceModel<Project> dsModel = new DetachableServiceModel<>(projectService, project); + + add(new ProjectForm(FORM, dsModel)); + add(new AdminSplitProjectPanel("splitPanel", dsModel)); } private class ProjectForm extends Form<Project> { diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.html b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.html new file mode 100644 index 0000000000..0aedf51684 --- /dev/null +++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.html @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org"> +<body> +<wicket:panel> + <div class="row mt-5"> + <div class="col-md-12 col-lg-8 col-xl-5"> + <p> + <a class="btn" wicket:id="splitProjectLink"><wicket:message key="splitButton" /></a> + </p> + <p wicket:id="splitInfo"></p> + </div> + </div> +</wicket:panel> +</body> +</html> \ No newline at end of file diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.java b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.java new file mode 100644 index 0000000000..b8bcaf413b --- /dev/null +++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.java @@ -0,0 +1,80 @@ +package se.su.dsv.scipro.admin.pages; + +import org.apache.wicket.behavior.AttributeAppender; +import org.apache.wicket.markup.html.basic.Label; +import org.apache.wicket.markup.html.link.AbstractLink; +import org.apache.wicket.markup.html.link.BookmarkablePageLink; +import org.apache.wicket.markup.html.panel.Panel; +import org.apache.wicket.model.IModel; +import org.apache.wicket.model.Model; +import se.su.dsv.scipro.project.Project; +import se.su.dsv.scipro.project.ProjectStatus; +import se.su.dsv.scipro.security.auth.roles.Roles; +import se.su.dsv.scipro.session.SciProSession; + +public class AdminSplitProjectPanel extends Panel { + private enum SplittableStatus { + NOT_ACTIVE, NOT_TWO_PARTICIPANTS, FINAL_SEMINAR_PHASE_STARTED, OK + } + + public AdminSplitProjectPanel(String id, final IModel<Project> projectModel) { + super(id, projectModel); + + Project project = projectModel.getObject(); + SplittableStatus splittableStatus = getSplitStatus(project); + + AbstractLink splitProjectLink = new BookmarkablePageLink<Void>("splitProjectLink", AdminCreateProjectPage.class) { + @Override + protected void onConfigure() { + super.onConfigure(); + + if (splittableStatus == SplittableStatus.OK) { + setEnabled(true); + this.add(new AttributeAppender("class", Model.of(" btn-success"))); + } else { + setEnabled(false); + this.add(new AttributeAppender("class", Model.of(" btn-secondary disabled"))); + } + } + }; + add(splitProjectLink); + + if (splittableStatus == SplittableStatus.NOT_TWO_PARTICIPANTS) { + add(new Label("splitInfo", Model.of("To be able to split a project, it needs to have 2 participants."))); + } else if (splittableStatus == SplittableStatus.NOT_ACTIVE) { + add(new Label("splitInfo", Model.of("Only active project can be split."))); + } else { + add(new Label("splitInfo", Model.of(""))); + } + + /* + Label splitInfoLabel = new Label("splitInfoLabel", dsModel) { + @Override + protected void onConfigure() { + super.onConfigure(); + + Project attachedProject = dsModel.getObject(); + if (attachedProject.getProjectParticipants().size() < 2) { + this + } + } + }; + */ + } + + @Override + protected void onConfigure() { + super.onConfigure(); + setVisibilityAllowed(SciProSession.get().authorizedForRole(Roles.ADMIN)); + } + + private SplittableStatus getSplitStatus(final Project project) { + if (project.getProjectParticipants().size() != 2) { + return SplittableStatus.NOT_TWO_PARTICIPANTS; + } else if (project.getProjectStatus() != ProjectStatus.ACTIVE) { + return SplittableStatus.NOT_ACTIVE; + } else { + return SplittableStatus.OK; + } + } +} diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.utf8.properties b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.utf8.properties new file mode 100644 index 0000000000..dbea6689ec --- /dev/null +++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.utf8.properties @@ -0,0 +1 @@ +splitButton = Split ProjectX \ No newline at end of file -- 2.39.5 From 739bf1c0bd90bf595f957901fabe72384e818d4a Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Mon, 24 Mar 2025 14:31:58 +0100 Subject: [PATCH 02/49] 87: Add support for split project confirmation page --- .../se/su/dsv/scipro/SciProApplication.java | 1 + .../admin/pages/AdminSplitProjectPage.html | 30 ++++++++ .../admin/pages/AdminSplitProjectPage.java | 75 +++++++++++++++++++ .../admin/pages/AdminSplitProjectPanel.java | 8 +- .../AdminSplitProjectPanel.utf8.properties | 2 +- 5 files changed, 114 insertions(+), 2 deletions(-) create mode 100644 view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPage.html create mode 100644 view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPage.java diff --git a/view/src/main/java/se/su/dsv/scipro/SciProApplication.java b/view/src/main/java/se/su/dsv/scipro/SciProApplication.java index 37f93c29e7..35b9c3b3e0 100755 --- a/view/src/main/java/se/su/dsv/scipro/SciProApplication.java +++ b/view/src/main/java/se/su/dsv/scipro/SciProApplication.java @@ -294,6 +294,7 @@ public class SciProApplication extends LifecycleManagedWebApplication { mountPage("admin/maintenance", SystemMaintenancePage.class); mountPage("admin/project", ProjectManagementPage.class); mountPage("admin/project/create", AdminCreateProjectPage.class); + mountPage("admin/project/split", AdminSplitProjectPage.class); mountPage("admin/project/survey", AdminSurveyPage.class); mountPage("admin/project/reviewer", AdminAssignReviewerPage.class); mountPage("admin/project/reviewer/capacity", AdminReviewerCapacityManagementPage.class); diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPage.html b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPage.html new file mode 100644 index 0000000000..f49fc10ae1 --- /dev/null +++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPage.html @@ -0,0 +1,30 @@ +<!DOCTYPE html> +<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org" lang="en"> +<body> +<wicket:extend> + <div class="row"> + <div class="col-lg-5"> + <h4 wicket:id="projectTitle"></h4> + + <p wicket:id="errorInfo"></p> + + <form class="form-horizontal" wicket:id="splitProjectForm"> + <p>This project will be split between following authors:</p> + + <div class="mb-3"> + <ul wicket:id="authorList"> + <li wicket:id="author"></li> + </ul> + </div> + <button class="btn btn-success" type="submit">Split Project</button> + </form> + + <div class="mt-5"> + <a class="btn btn-success" wicket:id="cancelLink">Cancel</a> + </div> + </div> + </div> + +</wicket:extend> +</body> +</html> \ No newline at end of file diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPage.java b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPage.java new file mode 100644 index 0000000000..bf7aac2f5a --- /dev/null +++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPage.java @@ -0,0 +1,75 @@ +package se.su.dsv.scipro.admin.pages; + +import jakarta.inject.Inject; +import org.apache.wicket.RestartResponseException; +import org.apache.wicket.markup.html.form.Form; +import org.apache.wicket.markup.html.link.BookmarkablePageLink; +import org.apache.wicket.markup.html.list.ListItem; +import org.apache.wicket.markup.html.list.ListView; +import org.apache.wicket.model.IModel; +import org.apache.wicket.model.Model; +import org.apache.wicket.request.mapper.parameter.PageParameters; +import se.su.dsv.scipro.components.menuhighlighting.MenuHighlightAdminProjectManagement; +import se.su.dsv.scipro.data.DetachableServiceModel; +import se.su.dsv.scipro.project.Project; +import se.su.dsv.scipro.project.ProjectService; +import se.su.dsv.scipro.security.auth.Authorization; +import se.su.dsv.scipro.security.auth.roles.Roles; +import se.su.dsv.scipro.system.User; +import se.su.dsv.scipro.util.PageParameterKeys; + +import org.apache.wicket.markup.html.basic.Label; +import org.apache.wicket.markup.html.link.AbstractLink; + +import java.util.ArrayList; + +@Authorization(authorizedRoles = { Roles.SYSADMIN }) +public class AdminSplitProjectPage extends AbstractAdminProjectPage implements MenuHighlightAdminProjectManagement { + + @Inject + private ProjectService projectService; + + public AdminSplitProjectPage(PageParameters pp) { + final long id = pp.get(PageParameterKeys.MAP.get(Project.class)).toLong(0); + final Project project = projectService.findOne(id); + if (project == null) { + throw new RestartResponseException(AdminCreateProjectPage.class); + } + DetachableServiceModel<Project> dsModel = new DetachableServiceModel<>(projectService, project); + + add(new Label("projectTitle", dsModel.map(Project::getTitle))); + + add(new Label("errorInfo", Model.of("error Info!!")) { + @Override + protected void onConfigure() { + super.onConfigure(); + setVisibilityAllowed(false); + } + } + ); + + add(new SplitProjectForm("splitProjectForm", dsModel)); + + add(new BookmarkablePageLink<Void>("cancelLink", ProjectManagementPage.class)); + } + + private class SplitProjectForm extends Form<Project> { + public SplitProjectForm(String id, final IModel<Project> model) { + super(id, model); + + add(new ListView<>("authorList", model.map(Project::getProjectParticipants).map(ArrayList::new)) { + @Override + protected void populateItem(ListItem<User> item) { + item.add(new Label("author", item.getModel().map(User::getFullName))); + } + }); + } + + @Override + protected void onSubmit() { + Long projectId = getModel().getObject().getId(); + + + } + } +} diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.java b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.java index b8bcaf413b..77811428df 100644 --- a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.java +++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.java @@ -7,10 +7,12 @@ import org.apache.wicket.markup.html.link.BookmarkablePageLink; import org.apache.wicket.markup.html.panel.Panel; import org.apache.wicket.model.IModel; import org.apache.wicket.model.Model; +import org.apache.wicket.request.mapper.parameter.PageParameters; import se.su.dsv.scipro.project.Project; import se.su.dsv.scipro.project.ProjectStatus; import se.su.dsv.scipro.security.auth.roles.Roles; import se.su.dsv.scipro.session.SciProSession; +import se.su.dsv.scipro.util.PageParameterKeys; public class AdminSplitProjectPanel extends Panel { private enum SplittableStatus { @@ -23,7 +25,10 @@ public class AdminSplitProjectPanel extends Panel { Project project = projectModel.getObject(); SplittableStatus splittableStatus = getSplitStatus(project); - AbstractLink splitProjectLink = new BookmarkablePageLink<Void>("splitProjectLink", AdminCreateProjectPage.class) { + final PageParameters pp = new PageParameters(); + pp.set(PageParameterKeys.MAP.get(Project.class), project.getId()); + + AbstractLink splitProjectLink = new BookmarkablePageLink<Void>("splitProjectLink", AdminSplitProjectPage.class, pp) { @Override protected void onConfigure() { super.onConfigure(); @@ -37,6 +42,7 @@ public class AdminSplitProjectPanel extends Panel { } } }; + add(splitProjectLink); if (splittableStatus == SplittableStatus.NOT_TWO_PARTICIPANTS) { diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.utf8.properties b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.utf8.properties index dbea6689ec..2683bf299a 100644 --- a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.utf8.properties +++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.utf8.properties @@ -1 +1 @@ -splitButton = Split ProjectX \ No newline at end of file +splitButton = Split Project \ No newline at end of file -- 2.39.5 From e59178a54049b6cfc4927bdea00140f634c45ae0 Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Wed, 26 Mar 2025 09:10:31 +0100 Subject: [PATCH 03/49] 87: Add initial version of SplitOrRestartProjectService --- .../java/se/su/dsv/scipro/CoreConfig.java | 9 ++ .../split/SplitOrRestartProjectService.java | 29 +++++++ .../SplitOrRestartProjectServiceImpl.java | 86 +++++++++++++++++++ 3 files changed, 124 insertions(+) create mode 100644 core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectService.java create mode 100644 core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java diff --git a/core/src/main/java/se/su/dsv/scipro/CoreConfig.java b/core/src/main/java/se/su/dsv/scipro/CoreConfig.java index 352c43456a..30bd0bd214 100644 --- a/core/src/main/java/se/su/dsv/scipro/CoreConfig.java +++ b/core/src/main/java/se/su/dsv/scipro/CoreConfig.java @@ -145,6 +145,7 @@ import se.su.dsv.scipro.project.ProjectPeopleStatisticsServiceImpl; import se.su.dsv.scipro.project.ProjectRepo; import se.su.dsv.scipro.project.ProjectService; import se.su.dsv.scipro.project.ProjectServiceImpl; +import se.su.dsv.scipro.project.split.SplitOrRestartProjectServiceImpl; import se.su.dsv.scipro.projectpartner.ProjectPartnerServiceImpl; import se.su.dsv.scipro.reflection.ReflectionService; import se.su.dsv.scipro.reflection.ReflectionServiceImpl; @@ -812,6 +813,14 @@ public class CoreConfig { return new ProjectServiceImpl(projectRepo, clock, eventBus, em); } + @Bean + public SplitOrRestartProjectServiceImpl SplitOrRestartProjectService( + ProjectService projectService, + FinalSeminarService finalSeminarService + ) { + return new SplitOrRestartProjectServiceImpl(projectService, finalSeminarService); + } + @Bean public ProjectTypeServiceImpl projectTypeService(Provider<EntityManager> em) { return new ProjectTypeServiceImpl(em); diff --git a/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectService.java b/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectService.java new file mode 100644 index 0000000000..42c0a74b65 --- /dev/null +++ b/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectService.java @@ -0,0 +1,29 @@ +package se.su.dsv.scipro.project.split; + +import se.su.dsv.scipro.project.Project; +import se.su.dsv.scipro.util.Pair; + +public interface SplitOrRestartProjectService { + + enum SplittableStatus { + NOT_EXIST("Project does not exist."), + NOT_ACTIVE("Only active project can be split."), + NOT_TWO_PARTICIPANTS("To be able to split a project, it needs to have 2 participants."), + FINAL_SEMINAR_PHASE_STARTED("Final seminar phase has been started, too late to split."), + OK("Ok to split."); + + private final String message; + + SplittableStatus(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + } + + Pair<SplittableStatus, Project> getSplittableStatus(int projectId); + + void splitProject(int projectId); +} diff --git a/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java new file mode 100644 index 0000000000..f750faa778 --- /dev/null +++ b/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java @@ -0,0 +1,86 @@ +package se.su.dsv.scipro.project.split; + +import jakarta.inject.Inject; +import se.su.dsv.scipro.finalseminar.FinalSeminarService; +import se.su.dsv.scipro.project.Project; +import se.su.dsv.scipro.project.ProjectService; +import se.su.dsv.scipro.project.ProjectStatus; +import se.su.dsv.scipro.system.User; +import se.su.dsv.scipro.util.Pair; + +import java.util.List; + +public class SplitOrRestartProjectServiceImpl implements SplitOrRestartProjectService { + + private final ProjectService projectService; + private final FinalSeminarService finalSeminarService; + + @Inject + public SplitOrRestartProjectServiceImpl(ProjectService projectService, FinalSeminarService finalSeminarService) { + this.projectService = projectService; + this.finalSeminarService = finalSeminarService; + } + + @Override + public Pair<SplittableStatus, Project> getSplittableStatus(int projectId) { + Project project = projectService.findOne(Long.valueOf(projectId)); + if (project == null) + return new Pair<>(SplittableStatus.NOT_EXIST, null); + + if (project.getProjectStatus() != ProjectStatus.ACTIVE) { + return new Pair<>(SplittableStatus.NOT_ACTIVE, project); + } + + if (project.getProjectParticipants().size() != 2) { + return new Pair<>(SplittableStatus.NOT_TWO_PARTICIPANTS, project); + } + + if (finalSeminarService.findByProject(project) != null) { + return new Pair<>(SplittableStatus.FINAL_SEMINAR_PHASE_STARTED, project); + } else { + return new Pair<>(SplittableStatus.OK, project); + } + } + + @Override + public void splitProject(int projectId) { + Pair<SplittableStatus, Project> result = getSplittableStatus(projectId); + if (result.getHead() != SplittableStatus.OK) { + throw new IllegalStateException("Project must to be verified to be able to split " + + "before this method can be called."); + } + + Project project = result.getTail(); + + for (User author : project.getProjectParticipants()) { + Project childProject = new Project(); + childProject.setTitle(project.getTitle()); + childProject.setProjectType(project.getProjectType()); + + childProject.setStartDate(project.getStartDate()); + childProject.setExpectedEndDate(project.getExpectedEndDate()); + + // Copy supervisor + childProject.setProjectParticipants(List.of(author)); + // Copy reviewer + // Copy cosupervisor + // Copy research area + + childProject.setProjectStatus(ProjectStatus.ACTIVE); + + // childProject.setParentProjectId + // childProject.setRootProjectId + + childProject = projectService.save(childProject); + + // Send event to eventBus to synchronize eventual Phase Two Approval + } + + // Parent project will set as inactive + project.setProjectStatus(ProjectStatus.INACTIVE); + projectService.save(project); + + + System.out.println("Hello, split project -> " + projectId); + } +} -- 2.39.5 From f2e19b686d012a1ce9525979c587bd3a64ebb811 Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Wed, 26 Mar 2025 10:04:40 +0100 Subject: [PATCH 04/49] 87: Use service method in AdminSplitProjectPanel --- .../split/SplitOrRestartProjectService.java | 4 +- .../SplitOrRestartProjectServiceImpl.java | 6 +- .../admin/pages/AdminSplitProjectPanel.java | 57 +++++++------------ 3 files changed, 24 insertions(+), 43 deletions(-) diff --git a/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectService.java b/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectService.java index 42c0a74b65..8231532181 100644 --- a/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectService.java +++ b/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectService.java @@ -23,7 +23,7 @@ public interface SplitOrRestartProjectService { } } - Pair<SplittableStatus, Project> getSplittableStatus(int projectId); + Pair<SplittableStatus, Project> getSplittableStatus(long projectId); - void splitProject(int projectId); + void splitProject(long projectId); } diff --git a/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java index f750faa778..cd8fa7a87a 100644 --- a/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java +++ b/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java @@ -22,8 +22,8 @@ public class SplitOrRestartProjectServiceImpl implements SplitOrRestartProjectSe } @Override - public Pair<SplittableStatus, Project> getSplittableStatus(int projectId) { - Project project = projectService.findOne(Long.valueOf(projectId)); + public Pair<SplittableStatus, Project> getSplittableStatus(long projectId) { + Project project = projectService.findOne(projectId); if (project == null) return new Pair<>(SplittableStatus.NOT_EXIST, null); @@ -43,7 +43,7 @@ public class SplitOrRestartProjectServiceImpl implements SplitOrRestartProjectSe } @Override - public void splitProject(int projectId) { + public void splitProject(long projectId) { Pair<SplittableStatus, Project> result = getSplittableStatus(projectId); if (result.getHead() != SplittableStatus.OK) { throw new IllegalStateException("Project must to be verified to be able to split " + diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.java b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.java index 77811428df..15d15e457d 100644 --- a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.java +++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.java @@ -1,29 +1,40 @@ package se.su.dsv.scipro.admin.pages; +import jakarta.inject.Inject; import org.apache.wicket.behavior.AttributeAppender; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.link.AbstractLink; import org.apache.wicket.markup.html.link.BookmarkablePageLink; import org.apache.wicket.markup.html.panel.Panel; import org.apache.wicket.model.IModel; +import org.apache.wicket.model.LoadableDetachableModel; import org.apache.wicket.model.Model; import org.apache.wicket.request.mapper.parameter.PageParameters; import se.su.dsv.scipro.project.Project; -import se.su.dsv.scipro.project.ProjectStatus; +import se.su.dsv.scipro.project.split.SplitOrRestartProjectService; import se.su.dsv.scipro.security.auth.roles.Roles; import se.su.dsv.scipro.session.SciProSession; import se.su.dsv.scipro.util.PageParameterKeys; +import se.su.dsv.scipro.util.Pair; + +import static se.su.dsv.scipro.project.split.SplitOrRestartProjectService.SplittableStatus; public class AdminSplitProjectPanel extends Panel { - private enum SplittableStatus { - NOT_ACTIVE, NOT_TWO_PARTICIPANTS, FINAL_SEMINAR_PHASE_STARTED, OK - } + + @Inject + private SplitOrRestartProjectService splitOrRestartProjectService; public AdminSplitProjectPanel(String id, final IModel<Project> projectModel) { super(id, projectModel); Project project = projectModel.getObject(); - SplittableStatus splittableStatus = getSplitStatus(project); + + LoadableDetachableModel<SplittableStatus> ldModel = LoadableDetachableModel.of( + () -> { + Pair<SplittableStatus, Project > pair = splitOrRestartProjectService.getSplittableStatus(project.getId()); + return pair.getHead(); + } + ); final PageParameters pp = new PageParameters(); pp.set(PageParameterKeys.MAP.get(Project.class), project.getId()); @@ -33,7 +44,7 @@ public class AdminSplitProjectPanel extends Panel { protected void onConfigure() { super.onConfigure(); - if (splittableStatus == SplittableStatus.OK) { + if (ldModel.getObject() == SplittableStatus.OK) { setEnabled(true); this.add(new AttributeAppender("class", Model.of(" btn-success"))); } else { @@ -42,30 +53,10 @@ public class AdminSplitProjectPanel extends Panel { } } }; - add(splitProjectLink); - if (splittableStatus == SplittableStatus.NOT_TWO_PARTICIPANTS) { - add(new Label("splitInfo", Model.of("To be able to split a project, it needs to have 2 participants."))); - } else if (splittableStatus == SplittableStatus.NOT_ACTIVE) { - add(new Label("splitInfo", Model.of("Only active project can be split."))); - } else { - add(new Label("splitInfo", Model.of(""))); - } - - /* - Label splitInfoLabel = new Label("splitInfoLabel", dsModel) { - @Override - protected void onConfigure() { - super.onConfigure(); - - Project attachedProject = dsModel.getObject(); - if (attachedProject.getProjectParticipants().size() < 2) { - this - } - } - }; - */ + add(new Label("splitInfo", Model.of(ldModel.getObject() == SplittableStatus.OK ? "" : + ldModel.getObject().getMessage()))); } @Override @@ -73,14 +64,4 @@ public class AdminSplitProjectPanel extends Panel { super.onConfigure(); setVisibilityAllowed(SciProSession.get().authorizedForRole(Roles.ADMIN)); } - - private SplittableStatus getSplitStatus(final Project project) { - if (project.getProjectParticipants().size() != 2) { - return SplittableStatus.NOT_TWO_PARTICIPANTS; - } else if (project.getProjectStatus() != ProjectStatus.ACTIVE) { - return SplittableStatus.NOT_ACTIVE; - } else { - return SplittableStatus.OK; - } - } } -- 2.39.5 From 02dc0629625cec6c6196709d278e9a283848d025 Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Wed, 26 Mar 2025 10:26:28 +0100 Subject: [PATCH 05/49] 87: Add initial support of AdminViewParentProjectPage --- .../se/su/dsv/scipro/SciProApplication.java | 1 + .../admin/pages/AdminSplitProjectPage.java | 10 ++++--- .../pages/AdminViewParentProjectPage.html | 26 +++++++++++++++++++ .../pages/AdminViewParentProjectPage.java | 18 +++++++++++++ 4 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 view/src/main/java/se/su/dsv/scipro/admin/pages/AdminViewParentProjectPage.html create mode 100644 view/src/main/java/se/su/dsv/scipro/admin/pages/AdminViewParentProjectPage.java diff --git a/view/src/main/java/se/su/dsv/scipro/SciProApplication.java b/view/src/main/java/se/su/dsv/scipro/SciProApplication.java index 35b9c3b3e0..5e3c9cb577 100755 --- a/view/src/main/java/se/su/dsv/scipro/SciProApplication.java +++ b/view/src/main/java/se/su/dsv/scipro/SciProApplication.java @@ -295,6 +295,7 @@ public class SciProApplication extends LifecycleManagedWebApplication { mountPage("admin/project", ProjectManagementPage.class); mountPage("admin/project/create", AdminCreateProjectPage.class); mountPage("admin/project/split", AdminSplitProjectPage.class); + mountPage("admin/project/viewparentproject", AdminViewParentProjectPage.class); mountPage("admin/project/survey", AdminSurveyPage.class); mountPage("admin/project/reviewer", AdminAssignReviewerPage.class); mountPage("admin/project/reviewer/capacity", AdminReviewerCapacityManagementPage.class); diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPage.java b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPage.java index bf7aac2f5a..a91635ff5c 100644 --- a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPage.java +++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPage.java @@ -2,6 +2,7 @@ package se.su.dsv.scipro.admin.pages; import jakarta.inject.Inject; import org.apache.wicket.RestartResponseException; +import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.markup.html.link.BookmarkablePageLink; import org.apache.wicket.markup.html.list.ListItem; @@ -18,9 +19,6 @@ import se.su.dsv.scipro.security.auth.roles.Roles; import se.su.dsv.scipro.system.User; import se.su.dsv.scipro.util.PageParameterKeys; -import org.apache.wicket.markup.html.basic.Label; -import org.apache.wicket.markup.html.link.AbstractLink; - import java.util.ArrayList; @Authorization(authorizedRoles = { Roles.SYSADMIN }) @@ -69,7 +67,13 @@ public class AdminSplitProjectPage extends AbstractAdminProjectPage implements M protected void onSubmit() { Long projectId = getModel().getObject().getId(); + System.out.println("Project ID: " + projectId); + + final PageParameters pp = new PageParameters(); + pp.set(PageParameterKeys.MAP.get(Project.class), projectId); + + setResponsePage(AdminViewParentProjectPage.class, pp); } } } diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminViewParentProjectPage.html b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminViewParentProjectPage.html new file mode 100644 index 0000000000..7cdd694718 --- /dev/null +++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminViewParentProjectPage.html @@ -0,0 +1,26 @@ +<!DOCTYPE html> +<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org" lang="en"> +<body> +<wicket:extend> + <div class="row"> + <div class="col-lg-5"> + <h4>Hahahaha</h4> + + <p>The project has following children projects:</p> + + <div class="mb-3"> + <ul> + <li>Abcd</li> + <li>efbud</li> + </ul> + </div> + + <div class="mt-5"> + <a class="btn btn-success" wicket:id="link">Projects</a> + </div> + </div> + </div> + +</wicket:extend> +</body> +</html> \ No newline at end of file diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminViewParentProjectPage.java b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminViewParentProjectPage.java new file mode 100644 index 0000000000..4b5d46e624 --- /dev/null +++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminViewParentProjectPage.java @@ -0,0 +1,18 @@ +package se.su.dsv.scipro.admin.pages; + +import org.apache.wicket.markup.html.link.BookmarkablePageLink; +import org.apache.wicket.request.mapper.parameter.PageParameters; +import se.su.dsv.scipro.components.menuhighlighting.MenuHighlightAdminProjectManagement; +import se.su.dsv.scipro.security.auth.Authorization; +import se.su.dsv.scipro.security.auth.roles.Roles; + +@Authorization(authorizedRoles = { Roles.SYSADMIN }) +public class AdminViewParentProjectPage extends AbstractAdminProjectPage implements MenuHighlightAdminProjectManagement { + + public AdminViewParentProjectPage(PageParameters pp) { + + add(new BookmarkablePageLink<Void>("link", ProjectManagementPage.class)); + } + + +} -- 2.39.5 From 6526dc1fd915a9915692ae3b7aeec42d70887b88 Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Wed, 26 Mar 2025 12:57:18 +0100 Subject: [PATCH 06/49] 87: Add initial support of parent-child project --- .../se/su/dsv/scipro/project/Project.java | 24 +++++++++++++++++++ .../SplitOrRestartProjectServiceImpl.java | 24 ++++++++++++------- .../v7__project_parent_phase2_review.sql | 16 +++++++++++++ .../admin/pages/AdminSplitProjectPage.java | 5 ++++ 4 files changed, 60 insertions(+), 9 deletions(-) create mode 100644 core/src/main/resources/db/migration/v7__project_parent_phase2_review.sql diff --git a/core/src/main/java/se/su/dsv/scipro/project/Project.java b/core/src/main/java/se/su/dsv/scipro/project/Project.java index 82f8400f9c..9a9e469738 100755 --- a/core/src/main/java/se/su/dsv/scipro/project/Project.java +++ b/core/src/main/java/se/su/dsv/scipro/project/Project.java @@ -113,6 +113,14 @@ public class Project extends DomainObject { @Column(name = "daisy_identifier", unique = true) private Integer identifier; + @Basic + @Column(name = "parent_project_id") + private Long parentProjectId; + + @Basic + @Column(name = "root_project_id") + private Long rootProjectId; + // ---------------------------------------------------------------------------------- // Embedded JPA-mapping // ---------------------------------------------------------------------------------- @@ -365,6 +373,22 @@ public class Project extends DomainObject { this.userNotes = userNotes; } + public Long getParentProjectId() { + return parentProjectId; + } + + public void setParentProjectId(Long parentProjectId) { + this.parentProjectId = parentProjectId; + } + + public Long getRootProjectId() { + return rootProjectId; + } + + public void setRootProjectId(Long rootProjectId) { + this.rootProjectId = rootProjectId; + } + // ---------------------------------------------------------------------------------- // Methods Common To All Objects // ---------------------------------------------------------------------------------- diff --git a/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java index cd8fa7a87a..f0d5532afe 100644 --- a/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java +++ b/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java @@ -1,6 +1,7 @@ package se.su.dsv.scipro.project.split; import jakarta.inject.Inject; +import jakarta.transaction.Transactional; import se.su.dsv.scipro.finalseminar.FinalSeminarService; import se.su.dsv.scipro.project.Project; import se.su.dsv.scipro.project.ProjectService; @@ -22,6 +23,7 @@ public class SplitOrRestartProjectServiceImpl implements SplitOrRestartProjectSe } @Override + @Transactional public Pair<SplittableStatus, Project> getSplittableStatus(long projectId) { Project project = projectService.findOne(projectId); if (project == null) @@ -43,6 +45,7 @@ public class SplitOrRestartProjectServiceImpl implements SplitOrRestartProjectSe } @Override + @Transactional public void splitProject(long projectId) { Pair<SplittableStatus, Project> result = getSplittableStatus(projectId); if (result.getHead() != SplittableStatus.OK) { @@ -52,35 +55,38 @@ public class SplitOrRestartProjectServiceImpl implements SplitOrRestartProjectSe Project project = result.getTail(); + // Todo: Get ev. Phase Two Approval + for (User author : project.getProjectParticipants()) { Project childProject = new Project(); + childProject.setTitle(project.getTitle()); childProject.setProjectType(project.getProjectType()); + childProject.setProjectStatus(ProjectStatus.ACTIVE); + childProject.setResearchArea(project.getResearchArea()); childProject.setStartDate(project.getStartDate()); childProject.setExpectedEndDate(project.getExpectedEndDate()); - // Copy supervisor + childProject.setHeadSupervisor(project.getHeadSupervisor()); childProject.setProjectParticipants(List.of(author)); - // Copy reviewer - // Copy cosupervisor - // Copy research area + childProject.setCoSupervisors(project.getCoSupervisors()); - childProject.setProjectStatus(ProjectStatus.ACTIVE); + childProject.setReviewers(project.getReviewers()); - // childProject.setParentProjectId - // childProject.setRootProjectId + childProject.setParentProjectId(project.getId()); + childProject.setRootProjectId(project.getRootProjectId() != null ? project.getRootProjectId() : + project.getId()); childProject = projectService.save(childProject); - // Send event to eventBus to synchronize eventual Phase Two Approval + // Todo: Send event to eventBus to synchronize eventual Phase Two Approval } // Parent project will set as inactive project.setProjectStatus(ProjectStatus.INACTIVE); projectService.save(project); - System.out.println("Hello, split project -> " + projectId); } } diff --git a/core/src/main/resources/db/migration/v7__project_parent_phase2_review.sql b/core/src/main/resources/db/migration/v7__project_parent_phase2_review.sql new file mode 100644 index 0000000000..2fa8341982 --- /dev/null +++ b/core/src/main/resources/db/migration/v7__project_parent_phase2_review.sql @@ -0,0 +1,16 @@ + +alter table `project` + add column `parent_project_id` bigint(20) null default null; + +alter table `project` + add column `root_project_id` bigint(20) null default null; + +alter table `project` + add constraint fk_project_parent_project_id_project_id + foreign key (parent_project_id) references project(id) + on delete cascade on update cascade; + +alter table `project` + add constraint fk_project_root_project_id_project_id + foreign key (root_project_id) references project(id) + on delete cascade on update cascade; diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPage.java b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPage.java index a91635ff5c..b3f5221637 100644 --- a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPage.java +++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPage.java @@ -14,6 +14,7 @@ import se.su.dsv.scipro.components.menuhighlighting.MenuHighlightAdminProjectMan import se.su.dsv.scipro.data.DetachableServiceModel; import se.su.dsv.scipro.project.Project; import se.su.dsv.scipro.project.ProjectService; +import se.su.dsv.scipro.project.split.SplitOrRestartProjectService; import se.su.dsv.scipro.security.auth.Authorization; import se.su.dsv.scipro.security.auth.roles.Roles; import se.su.dsv.scipro.system.User; @@ -27,6 +28,9 @@ public class AdminSplitProjectPage extends AbstractAdminProjectPage implements M @Inject private ProjectService projectService; + @Inject + private SplitOrRestartProjectService splitOrRestartProjectService; + public AdminSplitProjectPage(PageParameters pp) { final long id = pp.get(PageParameterKeys.MAP.get(Project.class)).toLong(0); final Project project = projectService.findOne(id); @@ -69,6 +73,7 @@ public class AdminSplitProjectPage extends AbstractAdminProjectPage implements M System.out.println("Project ID: " + projectId); + splitOrRestartProjectService.splitProject(projectId); final PageParameters pp = new PageParameters(); pp.set(PageParameterKeys.MAP.get(Project.class), projectId); -- 2.39.5 From 0e261fc85eda5c05a20584db001a768559a5300f Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Wed, 26 Mar 2025 13:00:11 +0100 Subject: [PATCH 07/49] 87: Reformat code --- .../java/se/su/dsv/scipro/CoreConfig.java | 4 +-- .../split/SplitOrRestartProjectService.java | 1 - .../SplitOrRestartProjectServiceImpl.java | 17 +++++------ .../admin/pages/AdminEditProjectPage.java | 11 ++++--- .../admin/pages/AdminSplitProjectPage.java | 20 +++++++------ .../admin/pages/AdminSplitProjectPanel.java | 29 +++++++++++-------- .../pages/AdminViewParentProjectPage.java | 7 ++--- 7 files changed, 46 insertions(+), 43 deletions(-) diff --git a/core/src/main/java/se/su/dsv/scipro/CoreConfig.java b/core/src/main/java/se/su/dsv/scipro/CoreConfig.java index 30bd0bd214..6de4da3244 100644 --- a/core/src/main/java/se/su/dsv/scipro/CoreConfig.java +++ b/core/src/main/java/se/su/dsv/scipro/CoreConfig.java @@ -815,8 +815,8 @@ public class CoreConfig { @Bean public SplitOrRestartProjectServiceImpl SplitOrRestartProjectService( - ProjectService projectService, - FinalSeminarService finalSeminarService + ProjectService projectService, + FinalSeminarService finalSeminarService ) { return new SplitOrRestartProjectServiceImpl(projectService, finalSeminarService); } diff --git a/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectService.java b/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectService.java index 8231532181..e2fc88005c 100644 --- a/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectService.java +++ b/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectService.java @@ -4,7 +4,6 @@ import se.su.dsv.scipro.project.Project; import se.su.dsv.scipro.util.Pair; public interface SplitOrRestartProjectService { - enum SplittableStatus { NOT_EXIST("Project does not exist."), NOT_ACTIVE("Only active project can be split."), diff --git a/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java index f0d5532afe..7937045a58 100644 --- a/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java +++ b/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java @@ -2,6 +2,7 @@ package se.su.dsv.scipro.project.split; import jakarta.inject.Inject; import jakarta.transaction.Transactional; +import java.util.List; import se.su.dsv.scipro.finalseminar.FinalSeminarService; import se.su.dsv.scipro.project.Project; import se.su.dsv.scipro.project.ProjectService; @@ -9,8 +10,6 @@ import se.su.dsv.scipro.project.ProjectStatus; import se.su.dsv.scipro.system.User; import se.su.dsv.scipro.util.Pair; -import java.util.List; - public class SplitOrRestartProjectServiceImpl implements SplitOrRestartProjectService { private final ProjectService projectService; @@ -26,8 +25,7 @@ public class SplitOrRestartProjectServiceImpl implements SplitOrRestartProjectSe @Transactional public Pair<SplittableStatus, Project> getSplittableStatus(long projectId) { Project project = projectService.findOne(projectId); - if (project == null) - return new Pair<>(SplittableStatus.NOT_EXIST, null); + if (project == null) return new Pair<>(SplittableStatus.NOT_EXIST, null); if (project.getProjectStatus() != ProjectStatus.ACTIVE) { return new Pair<>(SplittableStatus.NOT_ACTIVE, project); @@ -49,8 +47,9 @@ public class SplitOrRestartProjectServiceImpl implements SplitOrRestartProjectSe public void splitProject(long projectId) { Pair<SplittableStatus, Project> result = getSplittableStatus(projectId); if (result.getHead() != SplittableStatus.OK) { - throw new IllegalStateException("Project must to be verified to be able to split " + - "before this method can be called."); + throw new IllegalStateException( + "Project must to be verified to be able to split " + "before this method can be called." + ); } Project project = result.getTail(); @@ -75,11 +74,11 @@ public class SplitOrRestartProjectServiceImpl implements SplitOrRestartProjectSe childProject.setReviewers(project.getReviewers()); childProject.setParentProjectId(project.getId()); - childProject.setRootProjectId(project.getRootProjectId() != null ? project.getRootProjectId() : - project.getId()); + childProject.setRootProjectId( + project.getRootProjectId() != null ? project.getRootProjectId() : project.getId() + ); childProject = projectService.save(childProject); - // Todo: Send event to eventBus to synchronize eventual Phase Two Approval } diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminEditProjectPage.java b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminEditProjectPage.java index 6e93e74617..ae6ac05b4a 100644 --- a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminEditProjectPage.java +++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminEditProjectPage.java @@ -2,6 +2,11 @@ package se.su.dsv.scipro.admin.pages; import com.google.common.eventbus.EventBus; import jakarta.inject.Inject; +import java.time.LocalDate; +import java.util.Collection; +import java.util.EnumSet; +import java.util.List; +import java.util.TreeSet; import org.apache.wicket.RestartResponseException; import org.apache.wicket.markup.html.form.DropDownChoice; import org.apache.wicket.markup.html.form.Form; @@ -44,12 +49,6 @@ import se.su.dsv.scipro.system.UserSearchService; import se.su.dsv.scipro.system.UserService; import se.su.dsv.scipro.util.PageParameterKeys; -import java.time.LocalDate; -import java.util.Collection; -import java.util.EnumSet; -import java.util.List; -import java.util.TreeSet; - @Authorization(authorizedRoles = { Roles.SYSADMIN }) public class AdminEditProjectPage extends AbstractAdminProjectPage implements MenuHighlightAdminProjectManagement { diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPage.java b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPage.java index b3f5221637..6a0d779fbb 100644 --- a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPage.java +++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPage.java @@ -1,6 +1,7 @@ package se.su.dsv.scipro.admin.pages; import jakarta.inject.Inject; +import java.util.ArrayList; import org.apache.wicket.RestartResponseException; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.form.Form; @@ -20,8 +21,6 @@ import se.su.dsv.scipro.security.auth.roles.Roles; import se.su.dsv.scipro.system.User; import se.su.dsv.scipro.util.PageParameterKeys; -import java.util.ArrayList; - @Authorization(authorizedRoles = { Roles.SYSADMIN }) public class AdminSplitProjectPage extends AbstractAdminProjectPage implements MenuHighlightAdminProjectManagement { @@ -41,7 +40,8 @@ public class AdminSplitProjectPage extends AbstractAdminProjectPage implements M add(new Label("projectTitle", dsModel.map(Project::getTitle))); - add(new Label("errorInfo", Model.of("error Info!!")) { + add( + new Label("errorInfo", Model.of("error Info!!")) { @Override protected void onConfigure() { super.onConfigure(); @@ -56,15 +56,17 @@ public class AdminSplitProjectPage extends AbstractAdminProjectPage implements M } private class SplitProjectForm extends Form<Project> { + public SplitProjectForm(String id, final IModel<Project> model) { super(id, model); - - add(new ListView<>("authorList", model.map(Project::getProjectParticipants).map(ArrayList::new)) { - @Override - protected void populateItem(ListItem<User> item) { - item.add(new Label("author", item.getModel().map(User::getFullName))); + add( + new ListView<>("authorList", model.map(Project::getProjectParticipants).map(ArrayList::new)) { + @Override + protected void populateItem(ListItem<User> item) { + item.add(new Label("author", item.getModel().map(User::getFullName))); + } } - }); + ); } @Override diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.java b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.java index 15d15e457d..2585786797 100644 --- a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.java +++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.java @@ -1,5 +1,7 @@ package se.su.dsv.scipro.admin.pages; +import static se.su.dsv.scipro.project.split.SplitOrRestartProjectService.SplittableStatus; + import jakarta.inject.Inject; import org.apache.wicket.behavior.AttributeAppender; import org.apache.wicket.markup.html.basic.Label; @@ -17,8 +19,6 @@ import se.su.dsv.scipro.session.SciProSession; import se.su.dsv.scipro.util.PageParameterKeys; import se.su.dsv.scipro.util.Pair; -import static se.su.dsv.scipro.project.split.SplitOrRestartProjectService.SplittableStatus; - public class AdminSplitProjectPanel extends Panel { @Inject @@ -26,20 +26,21 @@ public class AdminSplitProjectPanel extends Panel { public AdminSplitProjectPanel(String id, final IModel<Project> projectModel) { super(id, projectModel); - Project project = projectModel.getObject(); - LoadableDetachableModel<SplittableStatus> ldModel = LoadableDetachableModel.of( - () -> { - Pair<SplittableStatus, Project > pair = splitOrRestartProjectService.getSplittableStatus(project.getId()); - return pair.getHead(); - } - ); + LoadableDetachableModel<SplittableStatus> ldModel = LoadableDetachableModel.of(() -> { + Pair<SplittableStatus, Project> pair = splitOrRestartProjectService.getSplittableStatus(project.getId()); + return pair.getHead(); + }); final PageParameters pp = new PageParameters(); pp.set(PageParameterKeys.MAP.get(Project.class), project.getId()); - AbstractLink splitProjectLink = new BookmarkablePageLink<Void>("splitProjectLink", AdminSplitProjectPage.class, pp) { + AbstractLink splitProjectLink = new BookmarkablePageLink<Void>( + "splitProjectLink", + AdminSplitProjectPage.class, + pp + ) { @Override protected void onConfigure() { super.onConfigure(); @@ -55,8 +56,12 @@ public class AdminSplitProjectPanel extends Panel { }; add(splitProjectLink); - add(new Label("splitInfo", Model.of(ldModel.getObject() == SplittableStatus.OK ? "" : - ldModel.getObject().getMessage()))); + add( + new Label( + "splitInfo", + Model.of(ldModel.getObject() == SplittableStatus.OK ? "" : ldModel.getObject().getMessage()) + ) + ); } @Override diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminViewParentProjectPage.java b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminViewParentProjectPage.java index 4b5d46e624..56c1ed8e4e 100644 --- a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminViewParentProjectPage.java +++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminViewParentProjectPage.java @@ -7,12 +7,11 @@ import se.su.dsv.scipro.security.auth.Authorization; import se.su.dsv.scipro.security.auth.roles.Roles; @Authorization(authorizedRoles = { Roles.SYSADMIN }) -public class AdminViewParentProjectPage extends AbstractAdminProjectPage implements MenuHighlightAdminProjectManagement { +public class AdminViewParentProjectPage + extends AbstractAdminProjectPage + implements MenuHighlightAdminProjectManagement { public AdminViewParentProjectPage(PageParameters pp) { - add(new BookmarkablePageLink<Void>("link", ProjectManagementPage.class)); } - - } -- 2.39.5 From 38830f3856e02623ebb246bdc7c0cb471a5840eb Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Wed, 26 Mar 2025 13:23:45 +0100 Subject: [PATCH 08/49] 87: Remove SQL-file with wrong naming convention --- .../v7__project_parent_phase2_review.sql | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 core/src/main/resources/db/migration/v7__project_parent_phase2_review.sql diff --git a/core/src/main/resources/db/migration/v7__project_parent_phase2_review.sql b/core/src/main/resources/db/migration/v7__project_parent_phase2_review.sql deleted file mode 100644 index 2fa8341982..0000000000 --- a/core/src/main/resources/db/migration/v7__project_parent_phase2_review.sql +++ /dev/null @@ -1,16 +0,0 @@ - -alter table `project` - add column `parent_project_id` bigint(20) null default null; - -alter table `project` - add column `root_project_id` bigint(20) null default null; - -alter table `project` - add constraint fk_project_parent_project_id_project_id - foreign key (parent_project_id) references project(id) - on delete cascade on update cascade; - -alter table `project` - add constraint fk_project_root_project_id_project_id - foreign key (root_project_id) references project(id) - on delete cascade on update cascade; -- 2.39.5 From 7d92dc4ddbccfe7ff284253acd8c830d19cdfb41 Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Wed, 26 Mar 2025 13:24:32 +0100 Subject: [PATCH 09/49] 87: Rename sql with with capital V --- .../V7__project_parent_phase2_review.sql | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 core/src/main/resources/db/migration/V7__project_parent_phase2_review.sql diff --git a/core/src/main/resources/db/migration/V7__project_parent_phase2_review.sql b/core/src/main/resources/db/migration/V7__project_parent_phase2_review.sql new file mode 100644 index 0000000000..2fa8341982 --- /dev/null +++ b/core/src/main/resources/db/migration/V7__project_parent_phase2_review.sql @@ -0,0 +1,16 @@ + +alter table `project` + add column `parent_project_id` bigint(20) null default null; + +alter table `project` + add column `root_project_id` bigint(20) null default null; + +alter table `project` + add constraint fk_project_parent_project_id_project_id + foreign key (parent_project_id) references project(id) + on delete cascade on update cascade; + +alter table `project` + add constraint fk_project_root_project_id_project_id + foreign key (root_project_id) references project(id) + on delete cascade on update cascade; -- 2.39.5 From d1936635907d39326192b78027d4a86c7134b214 Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Wed, 26 Mar 2025 13:59:13 +0100 Subject: [PATCH 10/49] 87: Fix test & Reformat code --- view/src/test/java/se/su/dsv/scipro/SciProTest.java | 4 ++++ .../su/dsv/scipro/admin/pages/AdminEditProjectPageTest.java | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/view/src/test/java/se/su/dsv/scipro/SciProTest.java b/view/src/test/java/se/su/dsv/scipro/SciProTest.java index d83e739cb0..78c2b93168 100755 --- a/view/src/test/java/se/su/dsv/scipro/SciProTest.java +++ b/view/src/test/java/se/su/dsv/scipro/SciProTest.java @@ -97,6 +97,7 @@ import se.su.dsv.scipro.project.ProjectNoteService; import se.su.dsv.scipro.project.ProjectPeopleStatisticsService; import se.su.dsv.scipro.project.ProjectService; import se.su.dsv.scipro.project.pages.ProjectStartPage; +import se.su.dsv.scipro.project.split.SplitOrRestartProjectService; import se.su.dsv.scipro.projectpartner.ProjectPartnerService; import se.su.dsv.scipro.reflection.ReflectionService; import se.su.dsv.scipro.report.GradeCalculatorService; @@ -252,6 +253,9 @@ public abstract class SciProTest { @Mock protected ProjectService projectService; + @Mock + protected SplitOrRestartProjectService splitOrRestartProjectService; + @Mock protected ResearchAreaService researchAreaService; diff --git a/view/src/test/java/se/su/dsv/scipro/admin/pages/AdminEditProjectPageTest.java b/view/src/test/java/se/su/dsv/scipro/admin/pages/AdminEditProjectPageTest.java index fca1614838..65ceb1bb27 100644 --- a/view/src/test/java/se/su/dsv/scipro/admin/pages/AdminEditProjectPageTest.java +++ b/view/src/test/java/se/su/dsv/scipro/admin/pages/AdminEditProjectPageTest.java @@ -20,11 +20,13 @@ import se.su.dsv.scipro.mail.MailEvent; import se.su.dsv.scipro.notifications.dataobject.NotificationSource; import se.su.dsv.scipro.notifications.dataobject.ProjectEvent; import se.su.dsv.scipro.project.Project; +import se.su.dsv.scipro.project.split.SplitOrRestartProjectService; import se.su.dsv.scipro.security.auth.roles.Roles; import se.su.dsv.scipro.system.DegreeType; import se.su.dsv.scipro.system.ProjectType; import se.su.dsv.scipro.system.User; import se.su.dsv.scipro.util.PageParameterKeys; +import se.su.dsv.scipro.util.Pair; public class AdminEditProjectPageTest extends SciProTest { @@ -269,6 +271,10 @@ public class AdminEditProjectPageTest extends SciProTest { private void startPage(Project project) { if (project.getId() != null) when(projectService.findOne(project.getId())).thenReturn(project); + lenient() + .when(splitOrRestartProjectService.getSplittableStatus(project.getId() != null ? project.getId() : 0L)) + .thenReturn(new Pair<>(SplitOrRestartProjectService.SplittableStatus.OK, project)); + PageParameters pp = new PageParameters(); pp.set(PageParameterKeys.MAP.get(Project.class), project.getId()); tester.startPage(AdminEditProjectPage.class, pp); -- 2.39.5 From 8266b1f1fe0d403282d5c51f3d2d9c8b2f0e02ee Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Thu, 27 Mar 2025 11:48:34 +0100 Subject: [PATCH 11/49] 87: Improve result page (AdminViewParentProjectPage) after split --- .../split/SplitOrRestartProjectService.java | 3 ++ .../SplitOrRestartProjectServiceImpl.java | 12 ++++- .../admin/pages/AdminSplitProjectPage.java | 2 - .../pages/AdminViewParentProjectPage.html | 7 ++- .../pages/AdminViewParentProjectPage.java | 51 +++++++++++++++++++ 5 files changed, 67 insertions(+), 8 deletions(-) diff --git a/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectService.java b/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectService.java index e2fc88005c..d11626a0a0 100644 --- a/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectService.java +++ b/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectService.java @@ -1,5 +1,6 @@ package se.su.dsv.scipro.project.split; +import java.util.List; import se.su.dsv.scipro.project.Project; import se.su.dsv.scipro.util.Pair; @@ -25,4 +26,6 @@ public interface SplitOrRestartProjectService { Pair<SplittableStatus, Project> getSplittableStatus(long projectId); void splitProject(long projectId); + + List<Project> getChildProjects(long parentProjectId); } diff --git a/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java index 7937045a58..0b5bc53f82 100644 --- a/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java +++ b/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java @@ -1,5 +1,6 @@ package se.su.dsv.scipro.project.split; +import com.querydsl.core.types.dsl.BooleanExpression; import jakarta.inject.Inject; import jakarta.transaction.Transactional; import java.util.List; @@ -7,6 +8,7 @@ import se.su.dsv.scipro.finalseminar.FinalSeminarService; import se.su.dsv.scipro.project.Project; import se.su.dsv.scipro.project.ProjectService; import se.su.dsv.scipro.project.ProjectStatus; +import se.su.dsv.scipro.project.QProject; import se.su.dsv.scipro.system.User; import se.su.dsv.scipro.util.Pair; @@ -78,14 +80,20 @@ public class SplitOrRestartProjectServiceImpl implements SplitOrRestartProjectSe project.getRootProjectId() != null ? project.getRootProjectId() : project.getId() ); + // Todo: add RoughDraftApproval if it's 'APPROVED' + childProject = projectService.save(childProject); - // Todo: Send event to eventBus to synchronize eventual Phase Two Approval + // Todo: Send event to eventBus to synchronize eventual Phase Two Approval with MileStone } // Parent project will set as inactive project.setProjectStatus(ProjectStatus.INACTIVE); projectService.save(project); + } - System.out.println("Hello, split project -> " + projectId); + @Override + @Transactional + public List<Project> getChildProjects(long parentProjectId) { + return projectService.findAll(QProject.project.rootProjectId.eq(parentProjectId)); } } diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPage.java b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPage.java index 6a0d779fbb..5ec65d6b52 100644 --- a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPage.java +++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPage.java @@ -73,8 +73,6 @@ public class AdminSplitProjectPage extends AbstractAdminProjectPage implements M protected void onSubmit() { Long projectId = getModel().getObject().getId(); - System.out.println("Project ID: " + projectId); - splitOrRestartProjectService.splitProject(projectId); final PageParameters pp = new PageParameters(); diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminViewParentProjectPage.html b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminViewParentProjectPage.html index 7cdd694718..c7f6ecaa59 100644 --- a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminViewParentProjectPage.html +++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminViewParentProjectPage.html @@ -4,14 +4,13 @@ <wicket:extend> <div class="row"> <div class="col-lg-5"> - <h4>Hahahaha</h4> + <h4 wicket:id="projectTitle"></h4> <p>The project has following children projects:</p> <div class="mb-3"> - <ul> - <li>Abcd</li> - <li>efbud</li> + <ul wicket:id="projectList"> + <li><a wicket:id="editLink"></a></li> </ul> </div> diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminViewParentProjectPage.java b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminViewParentProjectPage.java index 56c1ed8e4e..36ec634056 100644 --- a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminViewParentProjectPage.java +++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminViewParentProjectPage.java @@ -1,17 +1,68 @@ package se.su.dsv.scipro.admin.pages; +import jakarta.inject.Inject; +import java.util.List; +import org.apache.wicket.RestartResponseException; +import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.link.BookmarkablePageLink; +import org.apache.wicket.markup.html.list.ListItem; +import org.apache.wicket.markup.html.list.ListView; +import org.apache.wicket.model.LoadableDetachableModel; +import org.apache.wicket.model.Model; import org.apache.wicket.request.mapper.parameter.PageParameters; import se.su.dsv.scipro.components.menuhighlighting.MenuHighlightAdminProjectManagement; +import se.su.dsv.scipro.project.Project; +import se.su.dsv.scipro.project.ProjectService; +import se.su.dsv.scipro.project.split.SplitOrRestartProjectService; import se.su.dsv.scipro.security.auth.Authorization; import se.su.dsv.scipro.security.auth.roles.Roles; +import se.su.dsv.scipro.util.PageParameterKeys; @Authorization(authorizedRoles = { Roles.SYSADMIN }) public class AdminViewParentProjectPage extends AbstractAdminProjectPage implements MenuHighlightAdminProjectManagement { + @Inject + private ProjectService projectService; + + @Inject + private SplitOrRestartProjectService splitOrRestartProjectService; + public AdminViewParentProjectPage(PageParameters pp) { + final long id = pp.get(PageParameterKeys.MAP.get(Project.class)).toLong(0); + final Project project = projectService.findOne(id); + if (project == null) { + throw new RestartResponseException(AdminCreateProjectPage.class); + } + + add(new Label("projectTitle", Model.of(project.getTitle()))); + + LoadableDetachableModel<List<Project>> ldModel = LoadableDetachableModel.of(() -> + splitOrRestartProjectService.getChildProjects(id) + ); + + add( + new ListView<>("projectList", ldModel) { + @Override + protected void populateItem(ListItem<Project> item) { + Project project = item.getModelObject(); + + final PageParameters pp = new PageParameters(); + pp.set(PageParameterKeys.MAP.get(Project.class), project.getId()); + + BookmarkablePageLink<Void> link = new BookmarkablePageLink<Void>( + "editLink", + AdminEditProjectPage.class, + pp + ); + link.setBody(Model.of(project.getTitle() + " - " + project.getAuthorNames())); + + item.add(link); + } + } + ); + add(new BookmarkablePageLink<Void>("link", ProjectManagementPage.class)); } } -- 2.39.5 From 1ebf21f14bf03ac466b490437d6e2b35611dcc1a Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Wed, 2 Apr 2025 09:41:27 +0200 Subject: [PATCH 12/49] 87: Refactor SplittableStatusRecord using Java record --- .../java/se/su/dsv/scipro/CoreConfig.java | 5 ++- .../split/SplitOrRestartProjectService.java | 11 ++++- .../SplitOrRestartProjectServiceImpl.java | 41 +++++++++++++------ .../admin/pages/AdminSplitProjectPanel.java | 6 +-- .../admin/pages/AdminEditProjectPageTest.java | 8 +++- 5 files changed, 51 insertions(+), 20 deletions(-) diff --git a/core/src/main/java/se/su/dsv/scipro/CoreConfig.java b/core/src/main/java/se/su/dsv/scipro/CoreConfig.java index 6de4da3244..b0f924f458 100644 --- a/core/src/main/java/se/su/dsv/scipro/CoreConfig.java +++ b/core/src/main/java/se/su/dsv/scipro/CoreConfig.java @@ -816,9 +816,10 @@ public class CoreConfig { @Bean public SplitOrRestartProjectServiceImpl SplitOrRestartProjectService( ProjectService projectService, - FinalSeminarService finalSeminarService + FinalSeminarService finalSeminarService, + RoughDraftApprovalService roughDraftApprovalService ) { - return new SplitOrRestartProjectServiceImpl(projectService, finalSeminarService); + return new SplitOrRestartProjectServiceImpl(projectService, finalSeminarService, roughDraftApprovalService); } @Bean diff --git a/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectService.java b/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectService.java index d11626a0a0..0ae111bc44 100644 --- a/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectService.java +++ b/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectService.java @@ -2,13 +2,14 @@ package se.su.dsv.scipro.project.split; import java.util.List; import se.su.dsv.scipro.project.Project; -import se.su.dsv.scipro.util.Pair; +import se.su.dsv.scipro.reviewing.RoughDraftApproval; public interface SplitOrRestartProjectService { enum SplittableStatus { NOT_EXIST("Project does not exist."), NOT_ACTIVE("Only active project can be split."), NOT_TWO_PARTICIPANTS("To be able to split a project, it needs to have 2 participants."), + PHASE_TWO_STARTED("Phase 2 (Review) is already started, can't split right now."), FINAL_SEMINAR_PHASE_STARTED("Final seminar phase has been started, too late to split."), OK("Ok to split."); @@ -23,7 +24,13 @@ public interface SplitOrRestartProjectService { } } - Pair<SplittableStatus, Project> getSplittableStatus(long projectId); + record SplittableStatusRecord( + SplittableStatus splittableStatus, + Project project, + RoughDraftApproval roughDraftApproval + ) {} + + SplittableStatusRecord getSplittableStatus(long projectId); void splitProject(long projectId); diff --git a/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java index 0b5bc53f82..5f52b113c3 100644 --- a/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java +++ b/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java @@ -4,11 +4,14 @@ import com.querydsl.core.types.dsl.BooleanExpression; import jakarta.inject.Inject; import jakarta.transaction.Transactional; import java.util.List; +import java.util.Optional; import se.su.dsv.scipro.finalseminar.FinalSeminarService; import se.su.dsv.scipro.project.Project; import se.su.dsv.scipro.project.ProjectService; import se.su.dsv.scipro.project.ProjectStatus; import se.su.dsv.scipro.project.QProject; +import se.su.dsv.scipro.reviewing.RoughDraftApproval; +import se.su.dsv.scipro.reviewing.RoughDraftApprovalService; import se.su.dsv.scipro.system.User; import se.su.dsv.scipro.util.Pair; @@ -16,47 +19,60 @@ public class SplitOrRestartProjectServiceImpl implements SplitOrRestartProjectSe private final ProjectService projectService; private final FinalSeminarService finalSeminarService; + private final RoughDraftApprovalService roughDraftApprovalService; @Inject - public SplitOrRestartProjectServiceImpl(ProjectService projectService, FinalSeminarService finalSeminarService) { + public SplitOrRestartProjectServiceImpl( + ProjectService projectService, + FinalSeminarService finalSeminarService, + RoughDraftApprovalService roughDraftApprovalService + ) { this.projectService = projectService; this.finalSeminarService = finalSeminarService; + this.roughDraftApprovalService = roughDraftApprovalService; } @Override @Transactional - public Pair<SplittableStatus, Project> getSplittableStatus(long projectId) { + public SplittableStatusRecord getSplittableStatus(long projectId) { Project project = projectService.findOne(projectId); - if (project == null) return new Pair<>(SplittableStatus.NOT_EXIST, null); + if (project == null) return new SplittableStatusRecord(SplittableStatus.NOT_EXIST, null, null); if (project.getProjectStatus() != ProjectStatus.ACTIVE) { - return new Pair<>(SplittableStatus.NOT_ACTIVE, project); + return new SplittableStatusRecord(SplittableStatus.NOT_ACTIVE, project, null); } if (project.getProjectParticipants().size() != 2) { - return new Pair<>(SplittableStatus.NOT_TWO_PARTICIPANTS, project); + return new SplittableStatusRecord(SplittableStatus.NOT_TWO_PARTICIPANTS, project, null); } if (finalSeminarService.findByProject(project) != null) { - return new Pair<>(SplittableStatus.FINAL_SEMINAR_PHASE_STARTED, project); + return new SplittableStatusRecord(SplittableStatus.FINAL_SEMINAR_PHASE_STARTED, project, null); + } + + Optional<RoughDraftApproval> o = roughDraftApprovalService.findBy(project); + if (o.isPresent() && !o.get().isDecided()) { + return new SplittableStatusRecord(SplittableStatus.PHASE_TWO_STARTED, project, null); + } + + if (o.isPresent() && o.get().isApproved()) { + return new SplittableStatusRecord(SplittableStatus.OK, project, o.get()); } else { - return new Pair<>(SplittableStatus.OK, project); + return new SplittableStatusRecord(SplittableStatus.OK, project, null); } } @Override @Transactional public void splitProject(long projectId) { - Pair<SplittableStatus, Project> result = getSplittableStatus(projectId); - if (result.getHead() != SplittableStatus.OK) { + SplittableStatusRecord result = getSplittableStatus(projectId); + if (result.splittableStatus() != SplittableStatus.OK) { throw new IllegalStateException( "Project must to be verified to be able to split " + "before this method can be called." ); } - Project project = result.getTail(); - - // Todo: Get ev. Phase Two Approval + Project project = result.project(); for (User author : project.getProjectParticipants()) { Project childProject = new Project(); @@ -81,6 +97,7 @@ public class SplitOrRestartProjectServiceImpl implements SplitOrRestartProjectSe ); // Todo: add RoughDraftApproval if it's 'APPROVED' + if (result.roughDraftApproval() != null) {} childProject = projectService.save(childProject); // Todo: Send event to eventBus to synchronize eventual Phase Two Approval with MileStone diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.java b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.java index 2585786797..9d4a095e41 100644 --- a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.java +++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.java @@ -17,7 +17,6 @@ import se.su.dsv.scipro.project.split.SplitOrRestartProjectService; import se.su.dsv.scipro.security.auth.roles.Roles; import se.su.dsv.scipro.session.SciProSession; import se.su.dsv.scipro.util.PageParameterKeys; -import se.su.dsv.scipro.util.Pair; public class AdminSplitProjectPanel extends Panel { @@ -29,8 +28,9 @@ public class AdminSplitProjectPanel extends Panel { Project project = projectModel.getObject(); LoadableDetachableModel<SplittableStatus> ldModel = LoadableDetachableModel.of(() -> { - Pair<SplittableStatus, Project> pair = splitOrRestartProjectService.getSplittableStatus(project.getId()); - return pair.getHead(); + SplitOrRestartProjectService.SplittableStatusRecord status = + splitOrRestartProjectService.getSplittableStatus(project.getId()); + return status.splittableStatus(); }); final PageParameters pp = new PageParameters(); diff --git a/view/src/test/java/se/su/dsv/scipro/admin/pages/AdminEditProjectPageTest.java b/view/src/test/java/se/su/dsv/scipro/admin/pages/AdminEditProjectPageTest.java index 65ceb1bb27..f9fb3c2bc8 100644 --- a/view/src/test/java/se/su/dsv/scipro/admin/pages/AdminEditProjectPageTest.java +++ b/view/src/test/java/se/su/dsv/scipro/admin/pages/AdminEditProjectPageTest.java @@ -273,7 +273,13 @@ public class AdminEditProjectPageTest extends SciProTest { if (project.getId() != null) when(projectService.findOne(project.getId())).thenReturn(project); lenient() .when(splitOrRestartProjectService.getSplittableStatus(project.getId() != null ? project.getId() : 0L)) - .thenReturn(new Pair<>(SplitOrRestartProjectService.SplittableStatus.OK, project)); + .thenReturn( + new SplitOrRestartProjectService.SplittableStatusRecord( + SplitOrRestartProjectService.SplittableStatus.OK, + project, + null + ) + ); PageParameters pp = new PageParameters(); pp.set(PageParameterKeys.MAP.get(Project.class), project.getId()); -- 2.39.5 From 8a050491f98e0938ccf060ac31f19114b6c84ba2 Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Wed, 2 Apr 2025 12:44:24 +0200 Subject: [PATCH 13/49] 87: Add clone functionality --- .../se/su/dsv/scipro/project/Project.java | 16 ++++++++++ .../se/su/dsv/scipro/reviewing/Decision.java | 19 ++++++++++++ .../scipro/reviewing/ReviewerApproval.java | 30 +++++++++++++++++++ .../scipro/reviewing/RoughDraftApproval.java | 15 +++++++++- .../V7__project_parent_phase2_review.sql | 12 ++++++++ 5 files changed, 91 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/se/su/dsv/scipro/project/Project.java b/core/src/main/java/se/su/dsv/scipro/project/Project.java index 9a9e469738..7e2a9dc6ad 100755 --- a/core/src/main/java/se/su/dsv/scipro/project/Project.java +++ b/core/src/main/java/se/su/dsv/scipro/project/Project.java @@ -36,6 +36,9 @@ import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import java.util.stream.Collectors; + +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; import se.su.dsv.scipro.data.dataobjects.Member; import se.su.dsv.scipro.reusable.SciProUtilities; import se.su.dsv.scipro.system.DegreeType; @@ -121,6 +124,11 @@ public class Project extends DomainObject { @Column(name = "root_project_id") private Long rootProjectId; + @Basic + @Column(name = "clone_timestamp") + @Temporal(TemporalType.TIMESTAMP) + private Date cloneTimestamp; + // ---------------------------------------------------------------------------------- // Embedded JPA-mapping // ---------------------------------------------------------------------------------- @@ -389,6 +397,14 @@ public class Project extends DomainObject { this.rootProjectId = rootProjectId; } + public Date getCloneTimestamp() { + return cloneTimestamp; + } + + public void setCloneTimestamp(Date cloneTimestamp) { + this.cloneTimestamp = cloneTimestamp; + } + // ---------------------------------------------------------------------------------- // Methods Common To All Objects // ---------------------------------------------------------------------------------- diff --git a/core/src/main/java/se/su/dsv/scipro/reviewing/Decision.java b/core/src/main/java/se/su/dsv/scipro/reviewing/Decision.java index 67d701cdc9..b3b4ebc635 100644 --- a/core/src/main/java/se/su/dsv/scipro/reviewing/Decision.java +++ b/core/src/main/java/se/su/dsv/scipro/reviewing/Decision.java @@ -196,6 +196,25 @@ public class Decision { decideWithDecisionDate(status, reason, attachment, Instant.now()); } + Decision cloneToReviewerApproval(RoughDraftApproval roughDraftApproval) { + Decision clonedDecision = new Decision(); + clonedDecision.reviewerApproval = roughDraftApproval; + + clonedDecision.status = this.status; + clonedDecision.reason = this.reason; + clonedDecision.comment = this.comment; + clonedDecision.requested = this.requested; + clonedDecision.decisionDate = this.decisionDate; + clonedDecision.deadline = this.deadline; + clonedDecision.reviewerAssignedAt = this.reviewerAssignedAt; + + clonedDecision.assignedReviewer = this.assignedReviewer; + clonedDecision.attachment = this.attachment; + clonedDecision.thesis = this.thesis; + + return clonedDecision; + } + private void decideWithDecisionDate( final Status status, final String reason, diff --git a/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewerApproval.java b/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewerApproval.java index 9e91d2fca1..0f25dd4aa4 100644 --- a/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewerApproval.java +++ b/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewerApproval.java @@ -1,6 +1,8 @@ package se.su.dsv.scipro.reviewing; +import jakarta.persistence.Basic; import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; import jakarta.persistence.DiscriminatorColumn; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; @@ -16,6 +18,9 @@ import java.util.Date; import java.util.LinkedList; import java.util.List; import java.util.Optional; + +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; import se.su.dsv.scipro.file.FileReference; import se.su.dsv.scipro.project.Project; import se.su.dsv.scipro.system.DomainObject; @@ -32,6 +37,15 @@ public abstract class ReviewerApproval extends DomainObject { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + @Basic + @Column(name = "is_cloned") + protected Boolean isCloned = false; + + @Basic + @Column(name = "clone_timestamp") + @Temporal(TemporalType.TIMESTAMP) + protected Date cloneTimestamp; + // ---------------------------------------------------------------------------------- // JPA-mappings of foreign keys in this table (reviewer_approval) referencing other // tables. @@ -59,6 +73,22 @@ public abstract class ReviewerApproval extends DomainObject { return this.project; } + public Boolean getCloned() { + return isCloned; + } + + public void setCloned(Boolean cloned) { + isCloned = cloned; + } + + public Date getCloneTimestamp() { + return cloneTimestamp; + } + + public void setCloneTimestamp(Date cloneDate) { + this.cloneTimestamp = cloneDate; + } + // ---------------------------------------------------------------------------------- // Other methods // ---------------------------------------------------------------------------------- diff --git a/core/src/main/java/se/su/dsv/scipro/reviewing/RoughDraftApproval.java b/core/src/main/java/se/su/dsv/scipro/reviewing/RoughDraftApproval.java index bd8854b67b..1e9ca0b8a3 100644 --- a/core/src/main/java/se/su/dsv/scipro/reviewing/RoughDraftApproval.java +++ b/core/src/main/java/se/su/dsv/scipro/reviewing/RoughDraftApproval.java @@ -1,7 +1,9 @@ package se.su.dsv.scipro.reviewing; import jakarta.persistence.Entity; -import java.util.*; + +import java.time.Instant; +import java.util.Date; import se.su.dsv.scipro.file.FileReference; import se.su.dsv.scipro.project.Project; @@ -24,4 +26,15 @@ public class RoughDraftApproval extends ReviewerApproval { public Step getStep() { return Step.ROUGH_DRAFT_APPROVAL; } + + public RoughDraftApproval cloneToProject(final Project newProject) { + RoughDraftApproval rda = new RoughDraftApproval(); + rda.project = newProject; + this.decisions.forEach(decision -> rda.decisions.add(decision.cloneToReviewerApproval(rda))); + + rda.isCloned = true; + rda.cloneTimestamp = Date.from(Instant.now()); + + return rda; + } } diff --git a/core/src/main/resources/db/migration/V7__project_parent_phase2_review.sql b/core/src/main/resources/db/migration/V7__project_parent_phase2_review.sql index 2fa8341982..dd5e3e588c 100644 --- a/core/src/main/resources/db/migration/V7__project_parent_phase2_review.sql +++ b/core/src/main/resources/db/migration/V7__project_parent_phase2_review.sql @@ -5,6 +5,9 @@ alter table `project` alter table `project` add column `root_project_id` bigint(20) null default null; +alter table `project` + add column `clone_timestamp` datetime not null default null; + alter table `project` add constraint fk_project_parent_project_id_project_id foreign key (parent_project_id) references project(id) @@ -14,3 +17,12 @@ alter table `project` add constraint fk_project_root_project_id_project_id foreign key (root_project_id) references project(id) on delete cascade on update cascade; + + +alter table `reviewer_approval` + add column `is_cloned` bit(1) not null default false; + +alter table `reviewer_approval` + add column `clone_timestamp` datetime not null default null; + +update table `reviewer_approval` set is_cloned = false; \ No newline at end of file -- 2.39.5 From 57774b1c3260102176fbf5dc9076e13f0cfb399f Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Wed, 2 Apr 2025 13:18:06 +0200 Subject: [PATCH 14/49] 87: Save cloned RoughDraftApproval --- .../java/se/su/dsv/scipro/CoreConfig.java | 5 ++-- .../SplitOrRestartProjectServiceImpl.java | 30 +++++++++++++------ .../reviewing/RoughDraftApprovalService.java | 4 ++- .../RoughDraftApprovalServiceImpl.java | 6 ++++ 4 files changed, 33 insertions(+), 12 deletions(-) diff --git a/core/src/main/java/se/su/dsv/scipro/CoreConfig.java b/core/src/main/java/se/su/dsv/scipro/CoreConfig.java index b0f924f458..16a400668c 100644 --- a/core/src/main/java/se/su/dsv/scipro/CoreConfig.java +++ b/core/src/main/java/se/su/dsv/scipro/CoreConfig.java @@ -817,9 +817,10 @@ public class CoreConfig { public SplitOrRestartProjectServiceImpl SplitOrRestartProjectService( ProjectService projectService, FinalSeminarService finalSeminarService, - RoughDraftApprovalService roughDraftApprovalService + RoughDraftApprovalService roughDraftApprovalService, + EventBus eventBus ) { - return new SplitOrRestartProjectServiceImpl(projectService, finalSeminarService, roughDraftApprovalService); + return new SplitOrRestartProjectServiceImpl(projectService, finalSeminarService, roughDraftApprovalService, eventBus); } @Bean diff --git a/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java index 5f52b113c3..877274f46e 100644 --- a/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java +++ b/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java @@ -1,35 +1,41 @@ package se.su.dsv.scipro.project.split; -import com.querydsl.core.types.dsl.BooleanExpression; +import com.google.common.eventbus.EventBus; import jakarta.inject.Inject; import jakarta.transaction.Transactional; -import java.util.List; -import java.util.Optional; import se.su.dsv.scipro.finalseminar.FinalSeminarService; import se.su.dsv.scipro.project.Project; import se.su.dsv.scipro.project.ProjectService; import se.su.dsv.scipro.project.ProjectStatus; import se.su.dsv.scipro.project.QProject; import se.su.dsv.scipro.reviewing.RoughDraftApproval; +import se.su.dsv.scipro.reviewing.RoughDraftApprovalApprovedEvent; import se.su.dsv.scipro.reviewing.RoughDraftApprovalService; import se.su.dsv.scipro.system.User; -import se.su.dsv.scipro.util.Pair; + +import java.time.Instant; +import java.util.Date; +import java.util.List; +import java.util.Optional; public class SplitOrRestartProjectServiceImpl implements SplitOrRestartProjectService { private final ProjectService projectService; private final FinalSeminarService finalSeminarService; private final RoughDraftApprovalService roughDraftApprovalService; + private final EventBus eventBus; @Inject public SplitOrRestartProjectServiceImpl( ProjectService projectService, FinalSeminarService finalSeminarService, - RoughDraftApprovalService roughDraftApprovalService + RoughDraftApprovalService roughDraftApprovalService, + EventBus eventBus ) { this.projectService = projectService; this.finalSeminarService = finalSeminarService; this.roughDraftApprovalService = roughDraftApprovalService; + this.eventBus = eventBus; } @Override @@ -95,12 +101,18 @@ public class SplitOrRestartProjectServiceImpl implements SplitOrRestartProjectSe childProject.setRootProjectId( project.getRootProjectId() != null ? project.getRootProjectId() : project.getId() ); - - // Todo: add RoughDraftApproval if it's 'APPROVED' - if (result.roughDraftApproval() != null) {} + childProject.setCloneTimestamp(Date.from(Instant.now())); childProject = projectService.save(childProject); - // Todo: Send event to eventBus to synchronize eventual Phase Two Approval with MileStone + + // Add cloned RoughDraftApproval if it's 'APPROVED' + RoughDraftApproval rda = result.roughDraftApproval(); + if (rda != null && rda.isApproved()) { + RoughDraftApproval clonedRda = roughDraftApprovalService.saveCloned(rda.cloneToProject(childProject)); + + // Send event to eventBus to synchronize eventual Phase Two Approval with MileStone + eventBus.post(new RoughDraftApprovalApprovedEvent(clonedRda)); + } } // Parent project will set as inactive diff --git a/core/src/main/java/se/su/dsv/scipro/reviewing/RoughDraftApprovalService.java b/core/src/main/java/se/su/dsv/scipro/reviewing/RoughDraftApprovalService.java index a4e41dfa40..308ff71d00 100644 --- a/core/src/main/java/se/su/dsv/scipro/reviewing/RoughDraftApprovalService.java +++ b/core/src/main/java/se/su/dsv/scipro/reviewing/RoughDraftApprovalService.java @@ -1,3 +1,5 @@ package se.su.dsv.scipro.reviewing; -public interface RoughDraftApprovalService extends ReviewerApprovalService<RoughDraftApproval> {} +public interface RoughDraftApprovalService extends ReviewerApprovalService<RoughDraftApproval> { + RoughDraftApproval saveCloned(RoughDraftApproval approval); +} diff --git a/core/src/main/java/se/su/dsv/scipro/reviewing/RoughDraftApprovalServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/reviewing/RoughDraftApprovalServiceImpl.java index e0bb356aad..413444cd07 100644 --- a/core/src/main/java/se/su/dsv/scipro/reviewing/RoughDraftApprovalServiceImpl.java +++ b/core/src/main/java/se/su/dsv/scipro/reviewing/RoughDraftApprovalServiceImpl.java @@ -71,4 +71,10 @@ public class RoughDraftApprovalServiceImpl public Optional<RoughDraftApproval> findBy(Project project) { return Optional.ofNullable(findOne(QRoughDraftApproval.roughDraftApproval.project.eq(project))); } + + @Override + @Transactional + public RoughDraftApproval saveCloned(RoughDraftApproval approval) { + return save(approval); + } } -- 2.39.5 From b04c5d439e0eeb782f961f62ed6d4a0e70ad3b17 Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Wed, 2 Apr 2025 13:19:56 +0200 Subject: [PATCH 15/49] 87: reformat code --- core/src/main/java/se/su/dsv/scipro/CoreConfig.java | 7 ++++++- core/src/main/java/se/su/dsv/scipro/project/Project.java | 5 ++--- .../project/split/SplitOrRestartProjectServiceImpl.java | 9 ++++----- .../se/su/dsv/scipro/reviewing/ReviewerApproval.java | 5 ++--- .../se/su/dsv/scipro/reviewing/RoughDraftApproval.java | 1 - 5 files changed, 14 insertions(+), 13 deletions(-) diff --git a/core/src/main/java/se/su/dsv/scipro/CoreConfig.java b/core/src/main/java/se/su/dsv/scipro/CoreConfig.java index 16a400668c..03e3b29123 100644 --- a/core/src/main/java/se/su/dsv/scipro/CoreConfig.java +++ b/core/src/main/java/se/su/dsv/scipro/CoreConfig.java @@ -820,7 +820,12 @@ public class CoreConfig { RoughDraftApprovalService roughDraftApprovalService, EventBus eventBus ) { - return new SplitOrRestartProjectServiceImpl(projectService, finalSeminarService, roughDraftApprovalService, eventBus); + return new SplitOrRestartProjectServiceImpl( + projectService, + finalSeminarService, + roughDraftApprovalService, + eventBus + ); } @Bean diff --git a/core/src/main/java/se/su/dsv/scipro/project/Project.java b/core/src/main/java/se/su/dsv/scipro/project/Project.java index 7e2a9dc6ad..178eed3051 100755 --- a/core/src/main/java/se/su/dsv/scipro/project/Project.java +++ b/core/src/main/java/se/su/dsv/scipro/project/Project.java @@ -23,6 +23,8 @@ import jakarta.persistence.MapKeyJoinColumn; import jakarta.persistence.PrePersist; import jakarta.persistence.PreUpdate; import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; import java.time.LocalDate; import java.util.ArrayList; import java.util.Collection; @@ -36,9 +38,6 @@ import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import java.util.stream.Collectors; - -import jakarta.persistence.Temporal; -import jakarta.persistence.TemporalType; import se.su.dsv.scipro.data.dataobjects.Member; import se.su.dsv.scipro.reusable.SciProUtilities; import se.su.dsv.scipro.system.DegreeType; diff --git a/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java index 877274f46e..2f727f4471 100644 --- a/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java +++ b/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java @@ -3,6 +3,10 @@ package se.su.dsv.scipro.project.split; import com.google.common.eventbus.EventBus; import jakarta.inject.Inject; import jakarta.transaction.Transactional; +import java.time.Instant; +import java.util.Date; +import java.util.List; +import java.util.Optional; import se.su.dsv.scipro.finalseminar.FinalSeminarService; import se.su.dsv.scipro.project.Project; import se.su.dsv.scipro.project.ProjectService; @@ -13,11 +17,6 @@ import se.su.dsv.scipro.reviewing.RoughDraftApprovalApprovedEvent; import se.su.dsv.scipro.reviewing.RoughDraftApprovalService; import se.su.dsv.scipro.system.User; -import java.time.Instant; -import java.util.Date; -import java.util.List; -import java.util.Optional; - public class SplitOrRestartProjectServiceImpl implements SplitOrRestartProjectService { private final ProjectService projectService; diff --git a/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewerApproval.java b/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewerApproval.java index 0f25dd4aa4..db77e685be 100644 --- a/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewerApproval.java +++ b/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewerApproval.java @@ -13,14 +13,13 @@ import jakarta.persistence.OneToMany; import jakarta.persistence.OneToOne; import jakarta.persistence.OrderBy; import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; import java.util.Collections; import java.util.Date; import java.util.LinkedList; import java.util.List; import java.util.Optional; - -import jakarta.persistence.Temporal; -import jakarta.persistence.TemporalType; import se.su.dsv.scipro.file.FileReference; import se.su.dsv.scipro.project.Project; import se.su.dsv.scipro.system.DomainObject; diff --git a/core/src/main/java/se/su/dsv/scipro/reviewing/RoughDraftApproval.java b/core/src/main/java/se/su/dsv/scipro/reviewing/RoughDraftApproval.java index 1e9ca0b8a3..1ddc5906fb 100644 --- a/core/src/main/java/se/su/dsv/scipro/reviewing/RoughDraftApproval.java +++ b/core/src/main/java/se/su/dsv/scipro/reviewing/RoughDraftApproval.java @@ -1,7 +1,6 @@ package se.su.dsv.scipro.reviewing; import jakarta.persistence.Entity; - import java.time.Instant; import java.util.Date; import se.su.dsv.scipro.file.FileReference; -- 2.39.5 From 8465d4c6873301335dca8055a9174bf4f6a532bd Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Wed, 2 Apr 2025 15:18:46 +0200 Subject: [PATCH 16/49] 87: Loose up some SQL-constraints --- .../db/migration/V7__project_parent_phase2_review.sql | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/core/src/main/resources/db/migration/V7__project_parent_phase2_review.sql b/core/src/main/resources/db/migration/V7__project_parent_phase2_review.sql index dd5e3e588c..394207522f 100644 --- a/core/src/main/resources/db/migration/V7__project_parent_phase2_review.sql +++ b/core/src/main/resources/db/migration/V7__project_parent_phase2_review.sql @@ -6,7 +6,7 @@ alter table `project` add column `root_project_id` bigint(20) null default null; alter table `project` - add column `clone_timestamp` datetime not null default null; + add column `clone_timestamp` datetime null default null; alter table `project` add constraint fk_project_parent_project_id_project_id @@ -20,9 +20,7 @@ alter table `project` alter table `reviewer_approval` - add column `is_cloned` bit(1) not null default false; + add column `is_cloned` bit(1) null default null; alter table `reviewer_approval` - add column `clone_timestamp` datetime not null default null; - -update table `reviewer_approval` set is_cloned = false; \ No newline at end of file + add column `clone_timestamp` datetime null default null; -- 2.39.5 From e963bee932917da2d21a0680bb3996b736896e37 Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Thu, 3 Apr 2025 12:56:20 +0200 Subject: [PATCH 17/49] 87: Use other event so no notification will go --- .../milestones/service/MilestoneActivator.java | 6 ++++++ .../split/SplitOrRestartProjectServiceImpl.java | 13 +++++++------ .../RoughDraftApprovalApprovedClonedEvent.java | 14 ++++++++++++++ 3 files changed, 27 insertions(+), 6 deletions(-) create mode 100644 core/src/main/java/se/su/dsv/scipro/reviewing/RoughDraftApprovalApprovedClonedEvent.java diff --git a/core/src/main/java/se/su/dsv/scipro/milestones/service/MilestoneActivator.java b/core/src/main/java/se/su/dsv/scipro/milestones/service/MilestoneActivator.java index 145ee9d096..7a518492fc 100644 --- a/core/src/main/java/se/su/dsv/scipro/milestones/service/MilestoneActivator.java +++ b/core/src/main/java/se/su/dsv/scipro/milestones/service/MilestoneActivator.java @@ -21,6 +21,7 @@ import se.su.dsv.scipro.peer.SecondPeerReviewCompletedEvent; import se.su.dsv.scipro.project.Project; import se.su.dsv.scipro.report.SupervisorGradingReportSubmittedEvent; import se.su.dsv.scipro.reviewing.FinalSeminarApprovalApprovedEvent; +import se.su.dsv.scipro.reviewing.RoughDraftApprovalApprovedClonedEvent; import se.su.dsv.scipro.reviewing.RoughDraftApprovalApprovedEvent; import se.su.dsv.scipro.reviewing.RoughDraftApprovalRequestedEvent; import se.su.dsv.scipro.system.User; @@ -136,6 +137,11 @@ public class MilestoneActivator { activateProjectMilestone(Set.of(event.getName()), event.getProject()); } + @Subscribe + public void reviewerApprovalApprovedClone(RoughDraftApprovalApprovedClonedEvent event) { + activateProjectMilestone(Set.of(event.getName()), event.getProject()); + } + @Subscribe public void finalSeminarThesisDeleted(FinalSeminarThesisDeletedEvent event) { deactivateProjectMilestone(Set.of("FinalSeminarThesisUploaded"), event.getFinalSeminar().getProject()); diff --git a/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java index 2f727f4471..9bddb9433a 100644 --- a/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java +++ b/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java @@ -3,20 +3,21 @@ package se.su.dsv.scipro.project.split; import com.google.common.eventbus.EventBus; import jakarta.inject.Inject; import jakarta.transaction.Transactional; -import java.time.Instant; -import java.util.Date; -import java.util.List; -import java.util.Optional; import se.su.dsv.scipro.finalseminar.FinalSeminarService; import se.su.dsv.scipro.project.Project; import se.su.dsv.scipro.project.ProjectService; import se.su.dsv.scipro.project.ProjectStatus; import se.su.dsv.scipro.project.QProject; import se.su.dsv.scipro.reviewing.RoughDraftApproval; -import se.su.dsv.scipro.reviewing.RoughDraftApprovalApprovedEvent; +import se.su.dsv.scipro.reviewing.RoughDraftApprovalApprovedClonedEvent; import se.su.dsv.scipro.reviewing.RoughDraftApprovalService; import se.su.dsv.scipro.system.User; +import java.time.Instant; +import java.util.Date; +import java.util.List; +import java.util.Optional; + public class SplitOrRestartProjectServiceImpl implements SplitOrRestartProjectService { private final ProjectService projectService; @@ -110,7 +111,7 @@ public class SplitOrRestartProjectServiceImpl implements SplitOrRestartProjectSe RoughDraftApproval clonedRda = roughDraftApprovalService.saveCloned(rda.cloneToProject(childProject)); // Send event to eventBus to synchronize eventual Phase Two Approval with MileStone - eventBus.post(new RoughDraftApprovalApprovedEvent(clonedRda)); + eventBus.post(new RoughDraftApprovalApprovedClonedEvent(clonedRda)); } } diff --git a/core/src/main/java/se/su/dsv/scipro/reviewing/RoughDraftApprovalApprovedClonedEvent.java b/core/src/main/java/se/su/dsv/scipro/reviewing/RoughDraftApprovalApprovedClonedEvent.java new file mode 100644 index 0000000000..d403a44d07 --- /dev/null +++ b/core/src/main/java/se/su/dsv/scipro/reviewing/RoughDraftApprovalApprovedClonedEvent.java @@ -0,0 +1,14 @@ +package se.su.dsv.scipro.reviewing; + +import se.su.dsv.scipro.project.Project; + +public record RoughDraftApprovalApprovedClonedEvent(ReviewerApproval process) { + public Project getProject() { + return process.getProject(); + } + + public String getName() { + ReviewerApproval.Step step = process.getStep(); + return step.getDeclaringClass().getSimpleName() + "." + step.name(); + } +} -- 2.39.5 From 671d05e0e16e37d2ea5bd7e32d0c24521f0cccb3 Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Thu, 3 Apr 2025 15:02:04 +0200 Subject: [PATCH 18/49] 87: Adjust sql script --- .../db/migration/V7__project_parent_phase2_review.sql | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/main/resources/db/migration/V7__project_parent_phase2_review.sql b/core/src/main/resources/db/migration/V7__project_parent_phase2_review.sql index 394207522f..dda4ed8e2c 100644 --- a/core/src/main/resources/db/migration/V7__project_parent_phase2_review.sql +++ b/core/src/main/resources/db/migration/V7__project_parent_phase2_review.sql @@ -20,7 +20,9 @@ alter table `project` alter table `reviewer_approval` - add column `is_cloned` bit(1) null default null; + add column `is_cloned` boolean not null default false; + +update `reviewer_approval` set is_cloned = false; alter table `reviewer_approval` add column `clone_timestamp` datetime null default null; -- 2.39.5 From d90688648974690ab99ac6ce5e3574e882f526e5 Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Thu, 3 Apr 2025 15:04:05 +0200 Subject: [PATCH 19/49] 87: reformat code --- .../project/split/SplitOrRestartProjectServiceImpl.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java index 9bddb9433a..020e649758 100644 --- a/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java +++ b/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java @@ -3,6 +3,10 @@ package se.su.dsv.scipro.project.split; import com.google.common.eventbus.EventBus; import jakarta.inject.Inject; import jakarta.transaction.Transactional; +import java.time.Instant; +import java.util.Date; +import java.util.List; +import java.util.Optional; import se.su.dsv.scipro.finalseminar.FinalSeminarService; import se.su.dsv.scipro.project.Project; import se.su.dsv.scipro.project.ProjectService; @@ -13,11 +17,6 @@ import se.su.dsv.scipro.reviewing.RoughDraftApprovalApprovedClonedEvent; import se.su.dsv.scipro.reviewing.RoughDraftApprovalService; import se.su.dsv.scipro.system.User; -import java.time.Instant; -import java.util.Date; -import java.util.List; -import java.util.Optional; - public class SplitOrRestartProjectServiceImpl implements SplitOrRestartProjectService { private final ProjectService projectService; -- 2.39.5 From 13fa678ac570bdadb7d60ca9c67dbc6869302abf Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Mon, 7 Apr 2025 09:58:39 +0200 Subject: [PATCH 20/49] 87: Don't count cloned reviews. --- .../java/se/su/dsv/scipro/reviewing/DecisionRepositoryImpl.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/se/su/dsv/scipro/reviewing/DecisionRepositoryImpl.java b/core/src/main/java/se/su/dsv/scipro/reviewing/DecisionRepositoryImpl.java index 5843cf3335..97d55c2b84 100644 --- a/core/src/main/java/se/su/dsv/scipro/reviewing/DecisionRepositoryImpl.java +++ b/core/src/main/java/se/su/dsv/scipro/reviewing/DecisionRepositoryImpl.java @@ -25,6 +25,7 @@ public class DecisionRepositoryImpl extends AbstractRepository implements Decisi .eq(reviewer) .and(QDecision.decision.reviewerAssignedAt.goe(fromDate)) .and(QDecision.decision.reviewerAssignedAt.loe(toDate)) + .and(QDecision.decision.reviewerApproval.isCloned.isFalse()) ) .distinct() .fetchCount(); -- 2.39.5 From 3f604ad926524179a3696d410b050fd210a661f9 Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Mon, 7 Apr 2025 12:50:34 +0200 Subject: [PATCH 21/49] 87: Refactor out status message to view layer --- .../split/SplitOrRestartProjectService.java | 22 +++++-------------- .../admin/pages/AdminSplitProjectPanel.java | 18 ++++++++++----- .../AdminSplitProjectPanel.utf8.properties | 7 +++++- 3 files changed, 24 insertions(+), 23 deletions(-) diff --git a/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectService.java b/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectService.java index 0ae111bc44..43b69e7780 100644 --- a/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectService.java +++ b/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectService.java @@ -6,22 +6,12 @@ import se.su.dsv.scipro.reviewing.RoughDraftApproval; public interface SplitOrRestartProjectService { enum SplittableStatus { - NOT_EXIST("Project does not exist."), - NOT_ACTIVE("Only active project can be split."), - NOT_TWO_PARTICIPANTS("To be able to split a project, it needs to have 2 participants."), - PHASE_TWO_STARTED("Phase 2 (Review) is already started, can't split right now."), - FINAL_SEMINAR_PHASE_STARTED("Final seminar phase has been started, too late to split."), - OK("Ok to split."); - - private final String message; - - SplittableStatus(String message) { - this.message = message; - } - - public String getMessage() { - return message; - } + NOT_EXIST, + NOT_ACTIVE, + NOT_TWO_PARTICIPANTS, + PHASE_TWO_STARTED, + FINAL_SEMINAR_PHASE_STARTED, + OK, } record SplittableStatusRecord( diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.java b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.java index 9d4a095e41..ba322bf238 100644 --- a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.java +++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.java @@ -56,12 +56,7 @@ public class AdminSplitProjectPanel extends Panel { }; add(splitProjectLink); - add( - new Label( - "splitInfo", - Model.of(ldModel.getObject() == SplittableStatus.OK ? "" : ldModel.getObject().getMessage()) - ) - ); + add(new Label("splitInfo", Model.of(getStatusMessage(ldModel.getObject())))); } @Override @@ -69,4 +64,15 @@ public class AdminSplitProjectPanel extends Panel { super.onConfigure(); setVisibilityAllowed(SciProSession.get().authorizedForRole(Roles.ADMIN)); } + + private String getStatusMessage(SplittableStatus status) { + return switch (status) { + case OK -> ""; + case NOT_EXIST -> getString("status_not_exist"); + case NOT_ACTIVE -> getString("status_not_active"); + case NOT_TWO_PARTICIPANTS -> getString("status_not_two_participants"); + case PHASE_TWO_STARTED -> getString("status_phase_two_started"); + case FINAL_SEMINAR_PHASE_STARTED -> getString("status_final_seminar_phase_started"); + }; + } } diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.utf8.properties b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.utf8.properties index 2683bf299a..b3ec52b2aa 100644 --- a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.utf8.properties +++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.utf8.properties @@ -1 +1,6 @@ -splitButton = Split Project \ No newline at end of file +splitButton = Split Project +status_not_exit = Project does not exist. +status_not_active = Only active project can be split. +status_not_two_participants = To be able to split a project, it needs to have 2 participants. +status_phase_two_started = Phase 2 (Review) is already started, can't split right now. +status_final_seminar_phase_started = Final seminar phase has been started, too late to split. -- 2.39.5 From 8d5a082a6b017a911482f4faaf8e0a82f68292e5 Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Mon, 7 Apr 2025 13:31:14 +0200 Subject: [PATCH 22/49] 87: Move cancel link to same row as submit button --- .../scipro/admin/pages/AdminSplitProjectPage.html | 11 ++++------- .../scipro/admin/pages/AdminSplitProjectPage.java | 15 ++------------- 2 files changed, 6 insertions(+), 20 deletions(-) diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPage.html b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPage.html index f49fc10ae1..f05d53317e 100644 --- a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPage.html +++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPage.html @@ -6,8 +6,6 @@ <div class="col-lg-5"> <h4 wicket:id="projectTitle"></h4> - <p wicket:id="errorInfo"></p> - <form class="form-horizontal" wicket:id="splitProjectForm"> <p>This project will be split between following authors:</p> @@ -16,12 +14,11 @@ <li wicket:id="author"></li> </ul> </div> - <button class="btn btn-success" type="submit">Split Project</button> - </form> - <div class="mt-5"> - <a class="btn btn-success" wicket:id="cancelLink">Cancel</a> - </div> + <button class="btn btn-success" type="submit">Split Project</button> + + <a wicket:id="cancelLink">Cancel</a> + </form> </div> </div> diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPage.java b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPage.java index 5ec65d6b52..4145fdd056 100644 --- a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPage.java +++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPage.java @@ -9,7 +9,6 @@ import org.apache.wicket.markup.html.link.BookmarkablePageLink; import org.apache.wicket.markup.html.list.ListItem; import org.apache.wicket.markup.html.list.ListView; import org.apache.wicket.model.IModel; -import org.apache.wicket.model.Model; import org.apache.wicket.request.mapper.parameter.PageParameters; import se.su.dsv.scipro.components.menuhighlighting.MenuHighlightAdminProjectManagement; import se.su.dsv.scipro.data.DetachableServiceModel; @@ -40,19 +39,7 @@ public class AdminSplitProjectPage extends AbstractAdminProjectPage implements M add(new Label("projectTitle", dsModel.map(Project::getTitle))); - add( - new Label("errorInfo", Model.of("error Info!!")) { - @Override - protected void onConfigure() { - super.onConfigure(); - setVisibilityAllowed(false); - } - } - ); - add(new SplitProjectForm("splitProjectForm", dsModel)); - - add(new BookmarkablePageLink<Void>("cancelLink", ProjectManagementPage.class)); } private class SplitProjectForm extends Form<Project> { @@ -67,6 +54,8 @@ public class AdminSplitProjectPage extends AbstractAdminProjectPage implements M } } ); + + add(new BookmarkablePageLink<Void>("cancelLink", ProjectManagementPage.class)); } @Override -- 2.39.5 From 185785582a035e0f64b42edd2f6cbd44701fc65d Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Mon, 7 Apr 2025 15:48:37 +0200 Subject: [PATCH 23/49] 87: First version of integration test --- ...rRestartProjectServiceIntegrationTest.java | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 core/src/test/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceIntegrationTest.java diff --git a/core/src/test/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceIntegrationTest.java b/core/src/test/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceIntegrationTest.java new file mode 100644 index 0000000000..a7dec54c80 --- /dev/null +++ b/core/src/test/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceIntegrationTest.java @@ -0,0 +1,88 @@ +package se.su.dsv.scipro.project.split; + +import jakarta.inject.Inject; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.BeforeEach; +import se.su.dsv.scipro.project.Project; +import se.su.dsv.scipro.project.ProjectStatus; +import se.su.dsv.scipro.system.DegreeType; +import se.su.dsv.scipro.system.ProjectType; +import se.su.dsv.scipro.system.User; +import se.su.dsv.scipro.test.IntegrationTest; + +import java.time.LocalDate; + +import static org.junit.Assert.assertTrue; +import static se.su.dsv.scipro.project.split.SplitOrRestartProjectService.SplittableStatus; +import static se.su.dsv.scipro.project.split.SplitOrRestartProjectService.SplittableStatusRecord; + +public class SplitOrRestartProjectServiceIntegrationTest extends IntegrationTest { + @Inject + private SplitOrRestartProjectService sorpService; + + private Project parentProject; + private User supervisor; + private User reviwer; + private User author1; + private User author2; + + @BeforeEach + public void setUp() { + ProjectType bachelor = createProjectType(); + this.parentProject = createProject(bachelor, ProjectStatus.ACTIVE); + /* + user = createUser(); + Unit unit = new Unit(); + unit.setTitle("DSV IT"); + user.setUnit(save(unit)); + + */ + } + + @Test + public void project_must_exist() { + SplittableStatusRecord record = sorpService.getSplittableStatus(0); + SplittableStatus status = record.splittableStatus(); + + assertTrue(status == SplittableStatus.NOT_EXIST); + } + + @Test + public void project_must_be_active() { + parentProject.setProjectStatus(ProjectStatus.INACTIVE); + parentProject = save(parentProject); + + SplittableStatusRecord record = sorpService.getSplittableStatus(parentProject.getId()); + SplittableStatus status = record.splittableStatus(); + + assertTrue(status == SplittableStatus.NOT_ACTIVE); + } + + + + private ProjectType createProjectType() { + ProjectType projectType = new ProjectType(DegreeType.BACHELOR, "Bachelor", "Bachelor"); + save(projectType); + return projectType; + } + + private Project createProject(ProjectType projectType, ProjectStatus active) { + User headSupervisor = save( + User.builder().firstName("John").lastName("Doe").emailAddress("john@example.com").build() + ); + Project project = new Project(); + project.setTitle("Some title"); + project.setProjectType(projectType); + project.setProjectStatus(active); + project.setHeadSupervisor(headSupervisor); + project.setStartDate(LocalDate.now()); + project = save(project); + System.out.println(project); + return project; + } + + private User createUser() { + User user = User.builder().firstName("Bob").lastName("Sponge").emailAddress("bob@example.com").build(); + return save(user); + } +} -- 2.39.5 From 195e7f3bfb436877a22138ad9559c6ce2fab2b2b Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Wed, 9 Apr 2025 13:56:35 +0200 Subject: [PATCH 24/49] 87: Integration test in progress --- ...rRestartProjectServiceIntegrationTest.java | 156 +++++++++++++++--- 1 file changed, 135 insertions(+), 21 deletions(-) diff --git a/core/src/test/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceIntegrationTest.java b/core/src/test/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceIntegrationTest.java index a7dec54c80..f9866d2efb 100644 --- a/core/src/test/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceIntegrationTest.java +++ b/core/src/test/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceIntegrationTest.java @@ -1,16 +1,28 @@ package se.su.dsv.scipro.project.split; import jakarta.inject.Inject; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import se.su.dsv.scipro.file.FileUpload; import se.su.dsv.scipro.project.Project; import se.su.dsv.scipro.project.ProjectStatus; +import se.su.dsv.scipro.reviewing.ReviewerAssignmentService; +import se.su.dsv.scipro.reviewing.RoughDraftApproval; +import se.su.dsv.scipro.reviewing.RoughDraftApprovalService; +import se.su.dsv.scipro.security.auth.roles.Roles; import se.su.dsv.scipro.system.DegreeType; import se.su.dsv.scipro.system.ProjectType; +import se.su.dsv.scipro.system.ResearchArea; import se.su.dsv.scipro.system.User; import se.su.dsv.scipro.test.IntegrationTest; +import java.io.InputStream; import java.time.LocalDate; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.function.Function; import static org.junit.Assert.assertTrue; import static se.su.dsv.scipro.project.split.SplitOrRestartProjectService.SplittableStatus; @@ -20,6 +32,13 @@ public class SplitOrRestartProjectServiceIntegrationTest extends IntegrationTest @Inject private SplitOrRestartProjectService sorpService; + @Inject + private RoughDraftApprovalService rdaService; + + @Inject + private ReviewerAssignmentService raService; + + private ResearchArea researchArea; private Project parentProject; private User supervisor; private User reviwer; @@ -28,15 +47,13 @@ public class SplitOrRestartProjectServiceIntegrationTest extends IntegrationTest @BeforeEach public void setUp() { - ProjectType bachelor = createProjectType(); - this.parentProject = createProject(bachelor, ProjectStatus.ACTIVE); - /* - user = createUser(); - Unit unit = new Unit(); - unit.setTitle("DSV IT"); - user.setUnit(save(unit)); + ProjectType bachelor = save (new ProjectType(DegreeType.BACHELOR, "Bachelor", "Bachelor")); - */ + researchArea = new ResearchArea(); + researchArea.setTitle("Computer Science"); + researchArea = save(researchArea); + + parentProject = createProject(bachelor, ProjectStatus.ACTIVE); } @Test @@ -58,31 +75,128 @@ public class SplitOrRestartProjectServiceIntegrationTest extends IntegrationTest assertTrue(status == SplittableStatus.NOT_ACTIVE); } + @Test + public void project_must_have_two_participants() { + SplittableStatusRecord record = sorpService.getSplittableStatus(parentProject.getId()); + SplittableStatus status = record.splittableStatus(); - - private ProjectType createProjectType() { - ProjectType projectType = new ProjectType(DegreeType.BACHELOR, "Bachelor", "Bachelor"); - save(projectType); - return projectType; + assertTrue(status == SplittableStatus.NOT_TWO_PARTICIPANTS); } + @Test + public void project_phase_two_started() { + setUpBeforePhaseTwo(); + + SplittableStatusRecord record = sorpService.getSplittableStatus(parentProject.getId()); + SplittableStatus status = record.splittableStatus(); + + assertTrue(status == SplittableStatus.PHASE_TWO_STARTED); + } + + @Test + public void split_on_failed_phase_two() { + setUpBeforePhaseTwo(); + + Optional<RoughDraftApproval> optional = this.rdaService.findBy(parentProject); + optional.ifPresent(rda -> rda.reject("Fail", Optional.empty())); + + SplittableStatusRecord record = sorpService.getSplittableStatus(parentProject.getId()); + SplittableStatus status = record.splittableStatus(); + + assertTrue(status == SplittableStatus.OK); + + sorpService.splitProject(parentProject.getId()); + List<Project> childProjects = sorpService.getChildProjects(parentProject.getId()); + + assertTrue(parentProject.getProjectStatus() == ProjectStatus.INACTIVE); + assertTrue(childProjects.size() == 2); + childProjects.forEach(project -> { + assertTrue(project.getProjectParticipants().size() == 1); + assertTrue(project.getHeadSupervisor().equals(supervisor)); + assertTrue(project.getProjectStatus() == ProjectStatus.ACTIVE); + }); + } + + /* + PHASE_TWO_STARTED, + FINAL_SEMINAR_PHASE_STARTED, + OK, + */ + private Project createProject(ProjectType projectType, ProjectStatus active) { - User headSupervisor = save( - User.builder().firstName("John").lastName("Doe").emailAddress("john@example.com").build() - ); + supervisor = createEmployee("Eric", "Employee", "eric@example.com", false); + Project project = new Project(); project.setTitle("Some title"); project.setProjectType(projectType); project.setProjectStatus(active); - project.setHeadSupervisor(headSupervisor); + project.setHeadSupervisor(supervisor); project.setStartDate(LocalDate.now()); + + author1 = createUser("Adam", "Student", "adam.student@example.com"); + project.addProjectParticipant(author1); + project = save(project); - System.out.println(project); return project; } - private User createUser() { - User user = User.builder().firstName("Bob").lastName("Sponge").emailAddress("bob@example.com").build(); + private User createEmployee(String firstName, String lastName, String email, boolean isReviewer) { + User employee = createUser(firstName, lastName, email); + + Set<ResearchArea> researchAreas = new HashSet<>(); + researchAreas.add(researchArea); + employee.setResearchAreas(researchAreas); + + if (isReviewer) { + employee.addRole(Roles.REVIEWER); + } + + save(employee); + return employee; + } + + private User createUser(String firstName, String lastName, String email) { + User user = User.builder().firstName(firstName).lastName(lastName).emailAddress(email).build(); return save(user); } + + private void setUpBeforePhaseTwo() { + author2 = createUser("Bertil", "Student", "bertil.student@example.com"); + parentProject.addProjectParticipant(author2); + save(parentProject); + + rdaService.requestApproval(parentProject, dummyFile(), "comment"); + + reviwer = createEmployee("Lisa", "Employee", "lisa.employee@example.com", true); + ReviewerAssignmentService.ReviewerAssignment reviewerAssignment = raService.assignReviewer(parentProject, reviwer); + } + + private FileUpload dummyFile() { + return new FileUpload() { + @Override + public String getFileName() { + return "dummy.tmp"; + } + + @Override + public String getContentType() { + return "text/plain"; + } + + @Override + public User getUploader() { + return null; + } + + @Override + public long getSize() { + return 0; + } + + @Override + public <T> T handleData(Function<InputStream, T> handler) { + return handler.apply(InputStream.nullInputStream()); + } + }; + } } -- 2.39.5 From d875d9ce7c6704ab4b577faedd1cb2a0a6da6155 Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Wed, 9 Apr 2025 15:17:21 +0200 Subject: [PATCH 25/49] 87: Update test --- ...rRestartProjectServiceIntegrationTest.java | 42 +++++++++++++------ 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/core/src/test/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceIntegrationTest.java b/core/src/test/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceIntegrationTest.java index f9866d2efb..1dc9429e5b 100644 --- a/core/src/test/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceIntegrationTest.java +++ b/core/src/test/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceIntegrationTest.java @@ -7,6 +7,7 @@ import se.su.dsv.scipro.file.FileUpload; import se.su.dsv.scipro.project.Project; import se.su.dsv.scipro.project.ProjectStatus; import se.su.dsv.scipro.reviewing.ReviewerAssignmentService; +import se.su.dsv.scipro.reviewing.ReviewerCapacityService; import se.su.dsv.scipro.reviewing.RoughDraftApproval; import se.su.dsv.scipro.reviewing.RoughDraftApprovalService; import se.su.dsv.scipro.security.auth.roles.Roles; @@ -30,13 +31,16 @@ import static se.su.dsv.scipro.project.split.SplitOrRestartProjectService.Splitt public class SplitOrRestartProjectServiceIntegrationTest extends IntegrationTest { @Inject - private SplitOrRestartProjectService sorpService; + private SplitOrRestartProjectService splitOrRestartProjectService; @Inject - private RoughDraftApprovalService rdaService; + private RoughDraftApprovalService roughDraftApprovalService; @Inject - private ReviewerAssignmentService raService; + private ReviewerAssignmentService reviewerAssignmentService; + + @Inject + private ReviewerCapacityService reviewerCapacityService; private ResearchArea researchArea; private Project parentProject; @@ -58,7 +62,7 @@ public class SplitOrRestartProjectServiceIntegrationTest extends IntegrationTest @Test public void project_must_exist() { - SplittableStatusRecord record = sorpService.getSplittableStatus(0); + SplittableStatusRecord record = splitOrRestartProjectService.getSplittableStatus(0); SplittableStatus status = record.splittableStatus(); assertTrue(status == SplittableStatus.NOT_EXIST); @@ -69,7 +73,7 @@ public class SplitOrRestartProjectServiceIntegrationTest extends IntegrationTest parentProject.setProjectStatus(ProjectStatus.INACTIVE); parentProject = save(parentProject); - SplittableStatusRecord record = sorpService.getSplittableStatus(parentProject.getId()); + SplittableStatusRecord record = splitOrRestartProjectService.getSplittableStatus(parentProject.getId()); SplittableStatus status = record.splittableStatus(); assertTrue(status == SplittableStatus.NOT_ACTIVE); @@ -77,7 +81,7 @@ public class SplitOrRestartProjectServiceIntegrationTest extends IntegrationTest @Test public void project_must_have_two_participants() { - SplittableStatusRecord record = sorpService.getSplittableStatus(parentProject.getId()); + SplittableStatusRecord record = splitOrRestartProjectService.getSplittableStatus(parentProject.getId()); SplittableStatus status = record.splittableStatus(); assertTrue(status == SplittableStatus.NOT_TWO_PARTICIPANTS); @@ -87,7 +91,7 @@ public class SplitOrRestartProjectServiceIntegrationTest extends IntegrationTest public void project_phase_two_started() { setUpBeforePhaseTwo(); - SplittableStatusRecord record = sorpService.getSplittableStatus(parentProject.getId()); + SplittableStatusRecord record = splitOrRestartProjectService.getSplittableStatus(parentProject.getId()); SplittableStatus status = record.splittableStatus(); assertTrue(status == SplittableStatus.PHASE_TWO_STARTED); @@ -97,26 +101,38 @@ public class SplitOrRestartProjectServiceIntegrationTest extends IntegrationTest public void split_on_failed_phase_two() { setUpBeforePhaseTwo(); - Optional<RoughDraftApproval> optional = this.rdaService.findBy(parentProject); + Optional<RoughDraftApproval> optional = this.roughDraftApprovalService.findBy(parentProject); optional.ifPresent(rda -> rda.reject("Fail", Optional.empty())); - SplittableStatusRecord record = sorpService.getSplittableStatus(parentProject.getId()); + SplittableStatusRecord record = splitOrRestartProjectService.getSplittableStatus(parentProject.getId()); SplittableStatus status = record.splittableStatus(); assertTrue(status == SplittableStatus.OK); - sorpService.splitProject(parentProject.getId()); - List<Project> childProjects = sorpService.getChildProjects(parentProject.getId()); + splitOrRestartProjectService.splitProject(parentProject.getId()); + List<Project> childProjects = splitOrRestartProjectService.getChildProjects(parentProject.getId()); assertTrue(parentProject.getProjectStatus() == ProjectStatus.INACTIVE); assertTrue(childProjects.size() == 2); + childProjects.forEach(project -> { assertTrue(project.getProjectParticipants().size() == 1); assertTrue(project.getHeadSupervisor().equals(supervisor)); assertTrue(project.getProjectStatus() == ProjectStatus.ACTIVE); + assertTrue(roughDraftApprovalService.findBy(project).isEmpty()); }); } + @Test + public void split_on_approved_phase_two() { + setUpBeforePhaseTwo(); + + Optional<RoughDraftApproval> optional = this.roughDraftApprovalService.findBy(parentProject); + optional.ifPresent(rda -> rda.approve("Approve", Optional.empty())); + + + } + /* PHASE_TWO_STARTED, FINAL_SEMINAR_PHASE_STARTED, @@ -165,10 +181,10 @@ public class SplitOrRestartProjectServiceIntegrationTest extends IntegrationTest parentProject.addProjectParticipant(author2); save(parentProject); - rdaService.requestApproval(parentProject, dummyFile(), "comment"); + roughDraftApprovalService.requestApproval(parentProject, dummyFile(), "comment"); reviwer = createEmployee("Lisa", "Employee", "lisa.employee@example.com", true); - ReviewerAssignmentService.ReviewerAssignment reviewerAssignment = raService.assignReviewer(parentProject, reviwer); + ReviewerAssignmentService.ReviewerAssignment reviewerAssignment = reviewerAssignmentService.assignReviewer(parentProject, reviwer); } private FileUpload dummyFile() { -- 2.39.5 From 4be16af51c2580fa5cfe31750ff7b09990eef832 Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Thu, 10 Apr 2025 09:59:48 +0200 Subject: [PATCH 26/49] 87: Improve test --- .../split/SplitOrRestartProjectServiceIntegrationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/test/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceIntegrationTest.java b/core/src/test/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceIntegrationTest.java index 1dc9429e5b..3e9ae50f4f 100644 --- a/core/src/test/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceIntegrationTest.java +++ b/core/src/test/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceIntegrationTest.java @@ -112,7 +112,7 @@ public class SplitOrRestartProjectServiceIntegrationTest extends IntegrationTest splitOrRestartProjectService.splitProject(parentProject.getId()); List<Project> childProjects = splitOrRestartProjectService.getChildProjects(parentProject.getId()); - assertTrue(parentProject.getProjectStatus() == ProjectStatus.INACTIVE); + assertTrue(parentProject.getProjectStatus() ==ProjectStatus.INACTIVE); assertTrue(childProjects.size() == 2); childProjects.forEach(project -> { -- 2.39.5 From 80694e2c14b683bdef059393658fa6d15125565d Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Thu, 10 Apr 2025 14:18:59 +0200 Subject: [PATCH 27/49] 87: Improve test --- ...itOrRestartProjectServiceIntegrationTest.java | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/core/src/test/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceIntegrationTest.java b/core/src/test/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceIntegrationTest.java index 3e9ae50f4f..2c3f957976 100644 --- a/core/src/test/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceIntegrationTest.java +++ b/core/src/test/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceIntegrationTest.java @@ -19,6 +19,7 @@ import se.su.dsv.scipro.test.IntegrationTest; import java.io.InputStream; import java.time.LocalDate; +import java.time.Year; import java.util.HashSet; import java.util.List; import java.util.Optional; @@ -30,6 +31,9 @@ import static se.su.dsv.scipro.project.split.SplitOrRestartProjectService.Splitt import static se.su.dsv.scipro.project.split.SplitOrRestartProjectService.SplittableStatusRecord; public class SplitOrRestartProjectServiceIntegrationTest extends IntegrationTest { + private static int TARGET = 3; + private static int REMAINING_TARGET = 2; + @Inject private SplitOrRestartProjectService splitOrRestartProjectService; @@ -45,7 +49,7 @@ public class SplitOrRestartProjectServiceIntegrationTest extends IntegrationTest private ResearchArea researchArea; private Project parentProject; private User supervisor; - private User reviwer; + private User reviewer; private User author1; private User author2; @@ -110,6 +114,11 @@ public class SplitOrRestartProjectServiceIntegrationTest extends IntegrationTest assertTrue(status == SplittableStatus.OK); splitOrRestartProjectService.splitProject(parentProject.getId()); + + ReviewerCapacityService.RemainingTargets remainingTargets = reviewerCapacityService.getRemainingTargets(reviewer, Year.now()); + assertTrue(remainingTargets.spring() == REMAINING_TARGET || + remainingTargets.autumn() == REMAINING_TARGET); + List<Project> childProjects = splitOrRestartProjectService.getChildProjects(parentProject.getId()); assertTrue(parentProject.getProjectStatus() ==ProjectStatus.INACTIVE); @@ -183,8 +192,9 @@ public class SplitOrRestartProjectServiceIntegrationTest extends IntegrationTest roughDraftApprovalService.requestApproval(parentProject, dummyFile(), "comment"); - reviwer = createEmployee("Lisa", "Employee", "lisa.employee@example.com", true); - ReviewerAssignmentService.ReviewerAssignment reviewerAssignment = reviewerAssignmentService.assignReviewer(parentProject, reviwer); + reviewer = createEmployee("Lisa", "Employee", "lisa.employee@example.com", true); + reviewerCapacityService.assignTarget(reviewer, new ReviewerCapacityService.Target(Year.now(), TARGET, TARGET, "")); + reviewerAssignmentService.assignReviewer(parentProject, reviewer); } private FileUpload dummyFile() { -- 2.39.5 From 96eb62178d3eedebb6c15a4eaced168f40ab8aff Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Thu, 10 Apr 2025 14:20:53 +0200 Subject: [PATCH 28/49] 87: Reformat code --- ...rRestartProjectServiceIntegrationTest.java | 45 ++++++++++--------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/core/src/test/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceIntegrationTest.java b/core/src/test/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceIntegrationTest.java index 2c3f957976..5aaaf6f789 100644 --- a/core/src/test/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceIntegrationTest.java +++ b/core/src/test/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceIntegrationTest.java @@ -1,6 +1,18 @@ package se.su.dsv.scipro.project.split; +import static org.junit.Assert.assertTrue; +import static se.su.dsv.scipro.project.split.SplitOrRestartProjectService.SplittableStatus; +import static se.su.dsv.scipro.project.split.SplitOrRestartProjectService.SplittableStatusRecord; + import jakarta.inject.Inject; +import java.io.InputStream; +import java.time.LocalDate; +import java.time.Year; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.function.Function; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import se.su.dsv.scipro.file.FileUpload; @@ -17,20 +29,8 @@ import se.su.dsv.scipro.system.ResearchArea; import se.su.dsv.scipro.system.User; import se.su.dsv.scipro.test.IntegrationTest; -import java.io.InputStream; -import java.time.LocalDate; -import java.time.Year; -import java.util.HashSet; -import java.util.List; -import java.util.Optional; -import java.util.Set; -import java.util.function.Function; - -import static org.junit.Assert.assertTrue; -import static se.su.dsv.scipro.project.split.SplitOrRestartProjectService.SplittableStatus; -import static se.su.dsv.scipro.project.split.SplitOrRestartProjectService.SplittableStatusRecord; - public class SplitOrRestartProjectServiceIntegrationTest extends IntegrationTest { + private static int TARGET = 3; private static int REMAINING_TARGET = 2; @@ -55,7 +55,7 @@ public class SplitOrRestartProjectServiceIntegrationTest extends IntegrationTest @BeforeEach public void setUp() { - ProjectType bachelor = save (new ProjectType(DegreeType.BACHELOR, "Bachelor", "Bachelor")); + ProjectType bachelor = save(new ProjectType(DegreeType.BACHELOR, "Bachelor", "Bachelor")); researchArea = new ResearchArea(); researchArea.setTitle("Computer Science"); @@ -115,13 +115,15 @@ public class SplitOrRestartProjectServiceIntegrationTest extends IntegrationTest splitOrRestartProjectService.splitProject(parentProject.getId()); - ReviewerCapacityService.RemainingTargets remainingTargets = reviewerCapacityService.getRemainingTargets(reviewer, Year.now()); - assertTrue(remainingTargets.spring() == REMAINING_TARGET || - remainingTargets.autumn() == REMAINING_TARGET); + ReviewerCapacityService.RemainingTargets remainingTargets = reviewerCapacityService.getRemainingTargets( + reviewer, + Year.now() + ); + assertTrue(remainingTargets.spring() == REMAINING_TARGET || remainingTargets.autumn() == REMAINING_TARGET); List<Project> childProjects = splitOrRestartProjectService.getChildProjects(parentProject.getId()); - assertTrue(parentProject.getProjectStatus() ==ProjectStatus.INACTIVE); + assertTrue(parentProject.getProjectStatus() == ProjectStatus.INACTIVE); assertTrue(childProjects.size() == 2); childProjects.forEach(project -> { @@ -138,8 +140,6 @@ public class SplitOrRestartProjectServiceIntegrationTest extends IntegrationTest Optional<RoughDraftApproval> optional = this.roughDraftApprovalService.findBy(parentProject); optional.ifPresent(rda -> rda.approve("Approve", Optional.empty())); - - } /* @@ -193,7 +193,10 @@ public class SplitOrRestartProjectServiceIntegrationTest extends IntegrationTest roughDraftApprovalService.requestApproval(parentProject, dummyFile(), "comment"); reviewer = createEmployee("Lisa", "Employee", "lisa.employee@example.com", true); - reviewerCapacityService.assignTarget(reviewer, new ReviewerCapacityService.Target(Year.now(), TARGET, TARGET, "")); + reviewerCapacityService.assignTarget( + reviewer, + new ReviewerCapacityService.Target(Year.now(), TARGET, TARGET, "") + ); reviewerAssignmentService.assignReviewer(parentProject, reviewer); } -- 2.39.5 From f956ba1af8af5f538660017296257232e83c3925 Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Thu, 10 Apr 2025 14:30:05 +0200 Subject: [PATCH 29/49] 87: Improve test --- ...rRestartProjectServiceIntegrationTest.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/core/src/test/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceIntegrationTest.java b/core/src/test/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceIntegrationTest.java index 5aaaf6f789..465ac9db7a 100644 --- a/core/src/test/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceIntegrationTest.java +++ b/core/src/test/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceIntegrationTest.java @@ -140,6 +140,34 @@ public class SplitOrRestartProjectServiceIntegrationTest extends IntegrationTest Optional<RoughDraftApproval> optional = this.roughDraftApprovalService.findBy(parentProject); optional.ifPresent(rda -> rda.approve("Approve", Optional.empty())); + + SplittableStatusRecord record = splitOrRestartProjectService.getSplittableStatus(parentProject.getId()); + SplittableStatus status = record.splittableStatus(); + + assertTrue(status == SplittableStatus.OK); + + splitOrRestartProjectService.splitProject(parentProject.getId()); + + ReviewerCapacityService.RemainingTargets remainingTargets = reviewerCapacityService.getRemainingTargets( + reviewer, + Year.now() + ); + assertTrue(remainingTargets.spring() == REMAINING_TARGET || remainingTargets.autumn() == REMAINING_TARGET); + + List<Project> childProjects = splitOrRestartProjectService.getChildProjects(parentProject.getId()); + + assertTrue(parentProject.getProjectStatus() == ProjectStatus.INACTIVE); + assertTrue(childProjects.size() == 2); + + childProjects.forEach(project -> { + assertTrue(project.getProjectParticipants().size() == 1); + assertTrue(project.getHeadSupervisor().equals(supervisor)); + assertTrue(project.getProjectStatus() == ProjectStatus.ACTIVE); + + Optional<RoughDraftApproval> optionalRda = roughDraftApprovalService.findBy(project); + assertTrue(optionalRda.isPresent()); + assertTrue(optional.get().isApproved()); + }); } /* -- 2.39.5 From 6ccf72d4ab7585e0af5d56f009a7760a721cb497 Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Mon, 14 Apr 2025 12:56:01 +0200 Subject: [PATCH 30/49] 87: Improve integration test --- ...rRestartProjectServiceIntegrationTest.java | 46 ++++++++++++------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/core/src/test/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceIntegrationTest.java b/core/src/test/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceIntegrationTest.java index 465ac9db7a..6b874bd28a 100644 --- a/core/src/test/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceIntegrationTest.java +++ b/core/src/test/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceIntegrationTest.java @@ -8,25 +8,18 @@ import jakarta.inject.Inject; import java.io.InputStream; import java.time.LocalDate; import java.time.Year; -import java.util.HashSet; -import java.util.List; -import java.util.Optional; -import java.util.Set; +import java.time.ZonedDateTime; +import java.util.*; import java.util.function.Function; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import se.su.dsv.scipro.file.FileUpload; +import se.su.dsv.scipro.finalseminar.FinalSeminar; import se.su.dsv.scipro.project.Project; import se.su.dsv.scipro.project.ProjectStatus; -import se.su.dsv.scipro.reviewing.ReviewerAssignmentService; -import se.su.dsv.scipro.reviewing.ReviewerCapacityService; -import se.su.dsv.scipro.reviewing.RoughDraftApproval; -import se.su.dsv.scipro.reviewing.RoughDraftApprovalService; +import se.su.dsv.scipro.reviewing.*; import se.su.dsv.scipro.security.auth.roles.Roles; -import se.su.dsv.scipro.system.DegreeType; -import se.su.dsv.scipro.system.ProjectType; -import se.su.dsv.scipro.system.ResearchArea; -import se.su.dsv.scipro.system.User; +import se.su.dsv.scipro.system.*; import se.su.dsv.scipro.test.IntegrationTest; public class SplitOrRestartProjectServiceIntegrationTest extends IntegrationTest { @@ -40,6 +33,9 @@ public class SplitOrRestartProjectServiceIntegrationTest extends IntegrationTest @Inject private RoughDraftApprovalService roughDraftApprovalService; + @Inject + private FinalSeminarApprovalService finalSeminarApprovalService; + @Inject private ReviewerAssignmentService reviewerAssignmentService; @@ -170,11 +166,27 @@ public class SplitOrRestartProjectServiceIntegrationTest extends IntegrationTest }); } - /* - PHASE_TWO_STARTED, - FINAL_SEMINAR_PHASE_STARTED, - OK, - */ + @Test + public void split_on_final_seminar_phase_started() { + setUpBeforePhaseTwo(); + + Optional<RoughDraftApproval> optional = this.roughDraftApprovalService.findBy(parentProject); + optional.ifPresent(rda -> rda.approve("Approve", Optional.empty())); + + FinalSeminar finalSeminar = new FinalSeminar(); + finalSeminar.setProject(parentProject); + finalSeminar.setStartDate(Date.from(ZonedDateTime.now().plusDays(10).toInstant())); + finalSeminar.setRoom("room"); + finalSeminar.setPresentationLanguage(Language.ENGLISH); + parentProject.setLanguage(Language.ENGLISH); + + save(finalSeminar); + + SplittableStatusRecord record = splitOrRestartProjectService.getSplittableStatus(parentProject.getId()); + SplittableStatus status = record.splittableStatus(); + + assertTrue(status == SplittableStatus.FINAL_SEMINAR_PHASE_STARTED); + } private Project createProject(ProjectType projectType, ProjectStatus active) { supervisor = createEmployee("Eric", "Employee", "eric@example.com", false); -- 2.39.5 From 2bb6fae1887956b73db24a9d44c518ad6e5b176b Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Mon, 14 Apr 2025 14:20:34 +0200 Subject: [PATCH 31/49] 87: Add more milestones & events to test data --- .../dsv/scipro/testdata/DataInitializer.java | 63 +++++++++++-------- 1 file changed, 37 insertions(+), 26 deletions(-) diff --git a/test-data/src/main/java/se/su/dsv/scipro/testdata/DataInitializer.java b/test-data/src/main/java/se/su/dsv/scipro/testdata/DataInitializer.java index c12cb602b2..89742860d1 100644 --- a/test-data/src/main/java/se/su/dsv/scipro/testdata/DataInitializer.java +++ b/test-data/src/main/java/se/su/dsv/scipro/testdata/DataInitializer.java @@ -73,6 +73,9 @@ public class DataInitializer implements Lifecycle, BaseData, Factory { @Inject private MilestoneActivityTemplateService milestoneActivityTemplateService; + @Inject + private EventService eventService; + @Inject private FileService fileService; @@ -2017,36 +2020,42 @@ public class DataInitializer implements Lifecycle, BaseData, Factory { "First meeting held", "First meeting with supervisor.", milestonePhaseTemplate1, - null + null, + null ); createMileStone( MilestoneActivityTemplate.Type.PROJECT, "Project plan approved", "Project plan approved by supervisor.", milestonePhaseTemplate1, - null + null, null + ); + + List<Event> events = eventService.findAll(); + + createMileStone( + MilestoneActivityTemplate.Type.PROJECT, + "Rough draft sent to reviewer for approval (Auto)", + "Rough draft sent to the reviewer for the first time.", + milestonePhaseTemplate2, + null, events.stream().filter(event -> event.getName().equals("RoughDraftApprovalRequested")).findFirst().get() ); createMileStone( MilestoneActivityTemplate.Type.PROJECT, - "Rough draft sent to reviewer for approval", - "Rough draft approved by reviewer.", - milestonePhaseTemplate2, - null - ); - createMileStone( - MilestoneActivityTemplate.Type.PROJECT, - "Rough draft approved by reviewer", + "Rough draft approved by reviewer (Auto)", "Rough draft approved.", milestonePhaseTemplate2, - null + null, events.stream().filter(event -> event.getName().equals("Step.ROUGH_DRAFT_APPROVAL")).findFirst().get() ); + createMileStone( MilestoneActivityTemplate.Type.STUDENT, - "Peer review 1", + "Peer review 1 (Auto)", "This is a recommendation of when to perform peer review 1.", milestonePhaseTemplate2, - MilestoneActivityTemplate.PEER_REVIEW_ONE + MilestoneActivityTemplate.PEER_REVIEW_ONE, + null ); createMileStone( @@ -2054,14 +2063,14 @@ public class DataInitializer implements Lifecycle, BaseData, Factory { "Result and discussion completed and approved", "Result and discussion.", milestonePhaseTemplate3, - null + null, null ); createMileStone( MilestoneActivityTemplate.Type.STUDENT, "Peer review 2", "This is a recommendation of when to perform peer review 2.", milestonePhaseTemplate3, - MilestoneActivityTemplate.PEER_REVIEW_TWO + MilestoneActivityTemplate.PEER_REVIEW_TWO, null ); createMileStone( @@ -2069,42 +2078,42 @@ public class DataInitializer implements Lifecycle, BaseData, Factory { "Thesis approved for final seminar presentation", "Thesis approved for final seminar.", milestonePhaseTemplate4, - null + null, null ); createMileStone( MilestoneActivityTemplate.Type.PROJECT, "Final seminar created", "Creation of final seminar.", milestonePhaseTemplate4, - MilestoneActivityTemplate.CREATE_SEMINAR + MilestoneActivityTemplate.CREATE_SEMINAR, null ); createMileStone( MilestoneActivityTemplate.Type.PROJECT, "Final seminar thesis uploaded", "Final seminar thesis uploaded.", milestonePhaseTemplate4, - MilestoneActivityTemplate.THESIS_UPLOADED + MilestoneActivityTemplate.THESIS_UPLOADED, null ); createMileStone( MilestoneActivityTemplate.Type.STUDENT, "Perform an oral and written opposition", "Opposition.", milestonePhaseTemplate4, - null + null, null ); createMileStone( MilestoneActivityTemplate.Type.STUDENT, "Active participation in a final seminar", "Active participation.", milestonePhaseTemplate4, - null + null, null ); createMileStone( MilestoneActivityTemplate.Type.STUDENT, "Defend the thesis in a final seminar", "Defence of final thesis.", milestonePhaseTemplate4, - null + null, null ); createMileStone( @@ -2112,28 +2121,28 @@ public class DataInitializer implements Lifecycle, BaseData, Factory { "Revised final thesis of the submitted thesis", "Revised final thesis.", milestonePhaseTemplate5, - null + null, null ); createMileStone( MilestoneActivityTemplate.Type.PROJECT, "Originality report approved", "Originality report.", milestonePhaseTemplate5, - null + null, null ); createMileStone( MilestoneActivityTemplate.Type.PROJECT, "Supervisor and reviewer final grading report submitted", "Final grading report.", milestonePhaseTemplate5, - null + null, null ); createMileStone( MilestoneActivityTemplate.Type.STUDENT, "Grading completed", "Grading completed by examiner.", milestonePhaseTemplate5, - null + null, null ); } @@ -2142,7 +2151,8 @@ public class DataInitializer implements Lifecycle, BaseData, Factory { String title, String description, MilestonePhaseTemplate milestonePhaseTemplate, - String code + String code, + Event event ) { MilestoneActivityTemplate milestoneActivityTemplate = new MilestoneActivityTemplate(type, title, description); milestoneActivityTemplate.addProjectType(bachelorClass); @@ -2150,6 +2160,7 @@ public class DataInitializer implements Lifecycle, BaseData, Factory { milestoneActivityTemplate.addProjectType(magisterClass); milestoneActivityTemplate.setMilestonePhaseTemplate(milestonePhaseTemplate); milestoneActivityTemplate.setCode(code); + milestoneActivityTemplate.setActivatedBy(event); milestoneActivityTemplateService.save(milestoneActivityTemplate, milestonePhaseTemplate); } -- 2.39.5 From ae3755af5b50429f7fbb2c83e3a3b8a8abc4851f Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Mon, 14 Apr 2025 14:21:35 +0200 Subject: [PATCH 32/49] 87: Reformat code --- .../dsv/scipro/testdata/DataInitializer.java | 49 ++++++++++++------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/test-data/src/main/java/se/su/dsv/scipro/testdata/DataInitializer.java b/test-data/src/main/java/se/su/dsv/scipro/testdata/DataInitializer.java index 89742860d1..c496d20c0c 100644 --- a/test-data/src/main/java/se/su/dsv/scipro/testdata/DataInitializer.java +++ b/test-data/src/main/java/se/su/dsv/scipro/testdata/DataInitializer.java @@ -2021,14 +2021,15 @@ public class DataInitializer implements Lifecycle, BaseData, Factory { "First meeting with supervisor.", milestonePhaseTemplate1, null, - null + null ); createMileStone( MilestoneActivityTemplate.Type.PROJECT, "Project plan approved", "Project plan approved by supervisor.", milestonePhaseTemplate1, - null, null + null, + null ); List<Event> events = eventService.findAll(); @@ -2038,7 +2039,8 @@ public class DataInitializer implements Lifecycle, BaseData, Factory { "Rough draft sent to reviewer for approval (Auto)", "Rough draft sent to the reviewer for the first time.", milestonePhaseTemplate2, - null, events.stream().filter(event -> event.getName().equals("RoughDraftApprovalRequested")).findFirst().get() + null, + events.stream().filter(event -> event.getName().equals("RoughDraftApprovalRequested")).findFirst().get() ); createMileStone( @@ -2046,7 +2048,8 @@ public class DataInitializer implements Lifecycle, BaseData, Factory { "Rough draft approved by reviewer (Auto)", "Rough draft approved.", milestonePhaseTemplate2, - null, events.stream().filter(event -> event.getName().equals("Step.ROUGH_DRAFT_APPROVAL")).findFirst().get() + null, + events.stream().filter(event -> event.getName().equals("Step.ROUGH_DRAFT_APPROVAL")).findFirst().get() ); createMileStone( @@ -2055,7 +2058,7 @@ public class DataInitializer implements Lifecycle, BaseData, Factory { "This is a recommendation of when to perform peer review 1.", milestonePhaseTemplate2, MilestoneActivityTemplate.PEER_REVIEW_ONE, - null + null ); createMileStone( @@ -2063,14 +2066,16 @@ public class DataInitializer implements Lifecycle, BaseData, Factory { "Result and discussion completed and approved", "Result and discussion.", milestonePhaseTemplate3, - null, null + null, + null ); createMileStone( MilestoneActivityTemplate.Type.STUDENT, "Peer review 2", "This is a recommendation of when to perform peer review 2.", milestonePhaseTemplate3, - MilestoneActivityTemplate.PEER_REVIEW_TWO, null + MilestoneActivityTemplate.PEER_REVIEW_TWO, + null ); createMileStone( @@ -2078,42 +2083,48 @@ public class DataInitializer implements Lifecycle, BaseData, Factory { "Thesis approved for final seminar presentation", "Thesis approved for final seminar.", milestonePhaseTemplate4, - null, null + null, + null ); createMileStone( MilestoneActivityTemplate.Type.PROJECT, "Final seminar created", "Creation of final seminar.", milestonePhaseTemplate4, - MilestoneActivityTemplate.CREATE_SEMINAR, null + MilestoneActivityTemplate.CREATE_SEMINAR, + null ); createMileStone( MilestoneActivityTemplate.Type.PROJECT, "Final seminar thesis uploaded", "Final seminar thesis uploaded.", milestonePhaseTemplate4, - MilestoneActivityTemplate.THESIS_UPLOADED, null + MilestoneActivityTemplate.THESIS_UPLOADED, + null ); createMileStone( MilestoneActivityTemplate.Type.STUDENT, "Perform an oral and written opposition", "Opposition.", milestonePhaseTemplate4, - null, null + null, + null ); createMileStone( MilestoneActivityTemplate.Type.STUDENT, "Active participation in a final seminar", "Active participation.", milestonePhaseTemplate4, - null, null + null, + null ); createMileStone( MilestoneActivityTemplate.Type.STUDENT, "Defend the thesis in a final seminar", "Defence of final thesis.", milestonePhaseTemplate4, - null, null + null, + null ); createMileStone( @@ -2121,28 +2132,32 @@ public class DataInitializer implements Lifecycle, BaseData, Factory { "Revised final thesis of the submitted thesis", "Revised final thesis.", milestonePhaseTemplate5, - null, null + null, + null ); createMileStone( MilestoneActivityTemplate.Type.PROJECT, "Originality report approved", "Originality report.", milestonePhaseTemplate5, - null, null + null, + null ); createMileStone( MilestoneActivityTemplate.Type.PROJECT, "Supervisor and reviewer final grading report submitted", "Final grading report.", milestonePhaseTemplate5, - null, null + null, + null ); createMileStone( MilestoneActivityTemplate.Type.STUDENT, "Grading completed", "Grading completed by examiner.", milestonePhaseTemplate5, - null, null + null, + null ); } -- 2.39.5 From 299c67e8dd7173935beb25c2d35b4023598e3832 Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Mon, 14 Apr 2025 16:41:16 +0200 Subject: [PATCH 33/49] 87: Add SplitProjectPopulator --- .../populators/SplitProjectPopulator.java | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 test-data/src/main/java/se/su/dsv/scipro/testdata/populators/SplitProjectPopulator.java diff --git a/test-data/src/main/java/se/su/dsv/scipro/testdata/populators/SplitProjectPopulator.java b/test-data/src/main/java/se/su/dsv/scipro/testdata/populators/SplitProjectPopulator.java new file mode 100644 index 0000000000..5c640a0ece --- /dev/null +++ b/test-data/src/main/java/se/su/dsv/scipro/testdata/populators/SplitProjectPopulator.java @@ -0,0 +1,45 @@ +package se.su.dsv.scipro.testdata.populators; + +import jakarta.inject.Inject; +import java.time.LocalDate; +import java.util.Set; +import org.springframework.stereotype.Service; +import se.su.dsv.scipro.project.Project; +import se.su.dsv.scipro.project.ProjectService; +import se.su.dsv.scipro.system.User; +import se.su.dsv.scipro.testdata.BaseData; +import se.su.dsv.scipro.testdata.Factory; +import se.su.dsv.scipro.testdata.TestDataPopulator; + +@Service +public class SplitProjectPopulator implements TestDataPopulator { + + private final ProjectService projectService; + + @Inject + public SplitProjectPopulator(ProjectService projectService) { + this.projectService = projectService; + } + + @Override + public void populate(BaseData baseData, Factory factory) { + System.out.println("How do I do?"); + User supervisor = factory.createSupervisor("Emil"); + + User author1 = factory.createAuthor("Scott"); + User author2 = factory.createAuthor("Scarlett"); + + User reviewer = factory.createReviewer("Elias"); + + Project project = Project.builder() + .title("Operating System Boot Time Security") + .projectType(baseData.bachelor()) + .startDate(LocalDate.now()) + .headSupervisor(supervisor) + .reviewers(Set.of(reviewer)) + .projectParticipants(Set.of(author1, author2)) + .build(); + + projectService.save(project); + } +} -- 2.39.5 From 25b87579eb6acd65f0b98240b8b9369272a44e96 Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Wed, 16 Apr 2025 10:28:42 +0200 Subject: [PATCH 34/49] 87: Add test data --- .../populators/SplitProjectPopulator.java | 61 ++++++++++++++++++- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/test-data/src/main/java/se/su/dsv/scipro/testdata/populators/SplitProjectPopulator.java b/test-data/src/main/java/se/su/dsv/scipro/testdata/populators/SplitProjectPopulator.java index 5c640a0ece..b170b217ca 100644 --- a/test-data/src/main/java/se/su/dsv/scipro/testdata/populators/SplitProjectPopulator.java +++ b/test-data/src/main/java/se/su/dsv/scipro/testdata/populators/SplitProjectPopulator.java @@ -1,11 +1,20 @@ package se.su.dsv.scipro.testdata.populators; import jakarta.inject.Inject; +import java.io.InputStream; import java.time.LocalDate; +import java.time.Year; +import java.util.Optional; import java.util.Set; +import java.util.function.Function; import org.springframework.stereotype.Service; +import se.su.dsv.scipro.file.FileUpload; import se.su.dsv.scipro.project.Project; import se.su.dsv.scipro.project.ProjectService; +import se.su.dsv.scipro.reviewing.ReviewerAssignmentService; +import se.su.dsv.scipro.reviewing.ReviewerCapacityService; +import se.su.dsv.scipro.reviewing.RoughDraftApproval; +import se.su.dsv.scipro.reviewing.RoughDraftApprovalService; import se.su.dsv.scipro.system.User; import se.su.dsv.scipro.testdata.BaseData; import se.su.dsv.scipro.testdata.Factory; @@ -15,15 +24,25 @@ import se.su.dsv.scipro.testdata.TestDataPopulator; public class SplitProjectPopulator implements TestDataPopulator { private final ProjectService projectService; + private final ReviewerCapacityService reviewerCapacityService; + private final RoughDraftApprovalService roughDraftApprovalService; + private final ReviewerAssignmentService reviewerAssignmentService; @Inject - public SplitProjectPopulator(ProjectService projectService) { + public SplitProjectPopulator( + ProjectService projectService, + ReviewerCapacityService reviewerCapacityService, + RoughDraftApprovalService roughDraftApprovalService, + ReviewerAssignmentService reviewerAssignmentService + ) { this.projectService = projectService; + this.reviewerCapacityService = reviewerCapacityService; + this.roughDraftApprovalService = roughDraftApprovalService; + this.reviewerAssignmentService = reviewerAssignmentService; } @Override public void populate(BaseData baseData, Factory factory) { - System.out.println("How do I do?"); User supervisor = factory.createSupervisor("Emil"); User author1 = factory.createAuthor("Scott"); @@ -31,6 +50,8 @@ public class SplitProjectPopulator implements TestDataPopulator { User reviewer = factory.createReviewer("Elias"); + reviewerCapacityService.assignTarget(reviewer, new ReviewerCapacityService.Target(Year.now(), 3, 3, "")); + Project project = Project.builder() .title("Operating System Boot Time Security") .projectType(baseData.bachelor()) @@ -41,5 +62,41 @@ public class SplitProjectPopulator implements TestDataPopulator { .build(); projectService.save(project); + + roughDraftApprovalService.requestApproval(project, dummyFile(), "Request review."); + + reviewerAssignmentService.assignReviewer(project, reviewer); + + Optional<RoughDraftApproval> optional = roughDraftApprovalService.findBy(project); + optional.ifPresent(rda -> rda.approve("Approve! Good Work!", Optional.empty())); + } + + private FileUpload dummyFile() { + return new FileUpload() { + @Override + public String getFileName() { + return "dummy.tmp"; + } + + @Override + public String getContentType() { + return "text/plain"; + } + + @Override + public User getUploader() { + return null; + } + + @Override + public long getSize() { + return 0; + } + + @Override + public <T> T handleData(Function<InputStream, T> handler) { + return handler.apply(InputStream.nullInputStream()); + } + }; } } -- 2.39.5 From 5c36cd345d664ef123c987b30f8b4486ac23f2d2 Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Wed, 23 Apr 2025 10:39:51 +0200 Subject: [PATCH 35/49] 87: Use Instant instead of Date & Remove deprecated @Temporal --- .../main/java/se/su/dsv/scipro/project/Project.java | 11 +++++------ .../split/SplitOrRestartProjectServiceImpl.java | 2 +- .../se/su/dsv/scipro/reviewing/ReviewerApproval.java | 11 +++++------ .../su/dsv/scipro/reviewing/RoughDraftApproval.java | 2 +- 4 files changed, 12 insertions(+), 14 deletions(-) diff --git a/core/src/main/java/se/su/dsv/scipro/project/Project.java b/core/src/main/java/se/su/dsv/scipro/project/Project.java index 178eed3051..d90a52ffcb 100755 --- a/core/src/main/java/se/su/dsv/scipro/project/Project.java +++ b/core/src/main/java/se/su/dsv/scipro/project/Project.java @@ -23,8 +23,8 @@ import jakarta.persistence.MapKeyJoinColumn; import jakarta.persistence.PrePersist; import jakarta.persistence.PreUpdate; import jakarta.persistence.Table; -import jakarta.persistence.Temporal; -import jakarta.persistence.TemporalType; + +import java.time.Instant; import java.time.LocalDate; import java.util.ArrayList; import java.util.Collection; @@ -125,8 +125,7 @@ public class Project extends DomainObject { @Basic @Column(name = "clone_timestamp") - @Temporal(TemporalType.TIMESTAMP) - private Date cloneTimestamp; + private Instant cloneTimestamp; // ---------------------------------------------------------------------------------- // Embedded JPA-mapping @@ -396,11 +395,11 @@ public class Project extends DomainObject { this.rootProjectId = rootProjectId; } - public Date getCloneTimestamp() { + public Instant getCloneTimestamp() { return cloneTimestamp; } - public void setCloneTimestamp(Date cloneTimestamp) { + public void setCloneTimestamp(Instant cloneTimestamp) { this.cloneTimestamp = cloneTimestamp; } diff --git a/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java index 020e649758..1947b51c9b 100644 --- a/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java +++ b/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java @@ -100,7 +100,7 @@ public class SplitOrRestartProjectServiceImpl implements SplitOrRestartProjectSe childProject.setRootProjectId( project.getRootProjectId() != null ? project.getRootProjectId() : project.getId() ); - childProject.setCloneTimestamp(Date.from(Instant.now())); + childProject.setCloneTimestamp(Instant.now()); childProject = projectService.save(childProject); diff --git a/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewerApproval.java b/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewerApproval.java index db77e685be..f07b2fd48f 100644 --- a/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewerApproval.java +++ b/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewerApproval.java @@ -13,8 +13,8 @@ import jakarta.persistence.OneToMany; import jakarta.persistence.OneToOne; import jakarta.persistence.OrderBy; import jakarta.persistence.Table; -import jakarta.persistence.Temporal; -import jakarta.persistence.TemporalType; + +import java.time.Instant; import java.util.Collections; import java.util.Date; import java.util.LinkedList; @@ -42,8 +42,7 @@ public abstract class ReviewerApproval extends DomainObject { @Basic @Column(name = "clone_timestamp") - @Temporal(TemporalType.TIMESTAMP) - protected Date cloneTimestamp; + protected Instant cloneTimestamp; // ---------------------------------------------------------------------------------- // JPA-mappings of foreign keys in this table (reviewer_approval) referencing other @@ -80,11 +79,11 @@ public abstract class ReviewerApproval extends DomainObject { isCloned = cloned; } - public Date getCloneTimestamp() { + public Instant getCloneTimestamp() { return cloneTimestamp; } - public void setCloneTimestamp(Date cloneDate) { + public void setCloneTimestamp(Instant cloneDate) { this.cloneTimestamp = cloneDate; } diff --git a/core/src/main/java/se/su/dsv/scipro/reviewing/RoughDraftApproval.java b/core/src/main/java/se/su/dsv/scipro/reviewing/RoughDraftApproval.java index 1ddc5906fb..e22ec0af44 100644 --- a/core/src/main/java/se/su/dsv/scipro/reviewing/RoughDraftApproval.java +++ b/core/src/main/java/se/su/dsv/scipro/reviewing/RoughDraftApproval.java @@ -32,7 +32,7 @@ public class RoughDraftApproval extends ReviewerApproval { this.decisions.forEach(decision -> rda.decisions.add(decision.cloneToReviewerApproval(rda))); rda.isCloned = true; - rda.cloneTimestamp = Date.from(Instant.now()); + rda.cloneTimestamp = Instant.now(); return rda; } -- 2.39.5 From 25ba68f929249639e88b36f9492412c06311dd67 Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Wed, 23 Apr 2025 14:03:13 +0200 Subject: [PATCH 36/49] 87: Replace assertTrue with assertEquals --- ...rRestartProjectServiceIntegrationTest.java | 40 ++++++++++--------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/core/src/test/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceIntegrationTest.java b/core/src/test/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceIntegrationTest.java index 6b874bd28a..c2a82bb300 100644 --- a/core/src/test/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceIntegrationTest.java +++ b/core/src/test/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceIntegrationTest.java @@ -1,6 +1,7 @@ package se.su.dsv.scipro.project.split; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import static se.su.dsv.scipro.project.split.SplitOrRestartProjectService.SplittableStatus; import static se.su.dsv.scipro.project.split.SplitOrRestartProjectService.SplittableStatusRecord; @@ -11,6 +12,7 @@ import java.time.Year; import java.time.ZonedDateTime; import java.util.*; import java.util.function.Function; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import se.su.dsv.scipro.file.FileUpload; @@ -65,7 +67,7 @@ public class SplitOrRestartProjectServiceIntegrationTest extends IntegrationTest SplittableStatusRecord record = splitOrRestartProjectService.getSplittableStatus(0); SplittableStatus status = record.splittableStatus(); - assertTrue(status == SplittableStatus.NOT_EXIST); + assertEquals(SplittableStatus.NOT_EXIST, status); } @Test @@ -76,7 +78,7 @@ public class SplitOrRestartProjectServiceIntegrationTest extends IntegrationTest SplittableStatusRecord record = splitOrRestartProjectService.getSplittableStatus(parentProject.getId()); SplittableStatus status = record.splittableStatus(); - assertTrue(status == SplittableStatus.NOT_ACTIVE); + assertEquals(SplittableStatus.NOT_ACTIVE, status); } @Test @@ -84,7 +86,7 @@ public class SplitOrRestartProjectServiceIntegrationTest extends IntegrationTest SplittableStatusRecord record = splitOrRestartProjectService.getSplittableStatus(parentProject.getId()); SplittableStatus status = record.splittableStatus(); - assertTrue(status == SplittableStatus.NOT_TWO_PARTICIPANTS); + assertEquals(SplittableStatus.NOT_TWO_PARTICIPANTS, status); } @Test @@ -94,7 +96,7 @@ public class SplitOrRestartProjectServiceIntegrationTest extends IntegrationTest SplittableStatusRecord record = splitOrRestartProjectService.getSplittableStatus(parentProject.getId()); SplittableStatus status = record.splittableStatus(); - assertTrue(status == SplittableStatus.PHASE_TWO_STARTED); + assertEquals(SplittableStatus.PHASE_TWO_STARTED, status); } @Test @@ -107,7 +109,7 @@ public class SplitOrRestartProjectServiceIntegrationTest extends IntegrationTest SplittableStatusRecord record = splitOrRestartProjectService.getSplittableStatus(parentProject.getId()); SplittableStatus status = record.splittableStatus(); - assertTrue(status == SplittableStatus.OK); + assertEquals(SplittableStatus.OK, status); splitOrRestartProjectService.splitProject(parentProject.getId()); @@ -115,17 +117,18 @@ public class SplitOrRestartProjectServiceIntegrationTest extends IntegrationTest reviewer, Year.now() ); + // Todo: improve this with higher precision by using MutabledFixedClock assertTrue(remainingTargets.spring() == REMAINING_TARGET || remainingTargets.autumn() == REMAINING_TARGET); List<Project> childProjects = splitOrRestartProjectService.getChildProjects(parentProject.getId()); - assertTrue(parentProject.getProjectStatus() == ProjectStatus.INACTIVE); - assertTrue(childProjects.size() == 2); + assertEquals(ProjectStatus.INACTIVE, parentProject.getProjectStatus()); + assertEquals(2, childProjects.size()); childProjects.forEach(project -> { - assertTrue(project.getProjectParticipants().size() == 1); - assertTrue(project.getHeadSupervisor().equals(supervisor)); - assertTrue(project.getProjectStatus() == ProjectStatus.ACTIVE); + assertEquals(1, project.getProjectParticipants().size()); + assertEquals(supervisor, project.getHeadSupervisor()); + assertEquals(ProjectStatus.ACTIVE, project.getProjectStatus()); assertTrue(roughDraftApprovalService.findBy(project).isEmpty()); }); } @@ -140,7 +143,7 @@ public class SplitOrRestartProjectServiceIntegrationTest extends IntegrationTest SplittableStatusRecord record = splitOrRestartProjectService.getSplittableStatus(parentProject.getId()); SplittableStatus status = record.splittableStatus(); - assertTrue(status == SplittableStatus.OK); + assertEquals(SplittableStatus.OK, status); splitOrRestartProjectService.splitProject(parentProject.getId()); @@ -148,17 +151,18 @@ public class SplitOrRestartProjectServiceIntegrationTest extends IntegrationTest reviewer, Year.now() ); + // todo: improve this by using MutableFixedClock assertTrue(remainingTargets.spring() == REMAINING_TARGET || remainingTargets.autumn() == REMAINING_TARGET); List<Project> childProjects = splitOrRestartProjectService.getChildProjects(parentProject.getId()); - assertTrue(parentProject.getProjectStatus() == ProjectStatus.INACTIVE); - assertTrue(childProjects.size() == 2); + assertEquals(ProjectStatus.INACTIVE, parentProject.getProjectStatus()); + assertEquals(2, childProjects.size()); childProjects.forEach(project -> { - assertTrue(project.getProjectParticipants().size() == 1); - assertTrue(project.getHeadSupervisor().equals(supervisor)); - assertTrue(project.getProjectStatus() == ProjectStatus.ACTIVE); + assertEquals(1, project.getProjectParticipants().size()); + assertEquals(supervisor, project.getHeadSupervisor()); + assertEquals(ProjectStatus.ACTIVE, project.getProjectStatus()); Optional<RoughDraftApproval> optionalRda = roughDraftApprovalService.findBy(project); assertTrue(optionalRda.isPresent()); @@ -185,7 +189,7 @@ public class SplitOrRestartProjectServiceIntegrationTest extends IntegrationTest SplittableStatusRecord record = splitOrRestartProjectService.getSplittableStatus(parentProject.getId()); SplittableStatus status = record.splittableStatus(); - assertTrue(status == SplittableStatus.FINAL_SEMINAR_PHASE_STARTED); + assertEquals(SplittableStatus.FINAL_SEMINAR_PHASE_STARTED, status); } private Project createProject(ProjectType projectType, ProjectStatus active) { -- 2.39.5 From 60d73e18e40a62242fcb2022adfc910163ce720a Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Wed, 23 Apr 2025 14:37:06 +0200 Subject: [PATCH 37/49] 87: Improve code readability in DataInitializer & Reformat code --- .../se/su/dsv/scipro/project/Project.java | 1 - .../scipro/reviewing/ReviewerApproval.java | 1 - ...rRestartProjectServiceIntegrationTest.java | 1 - .../dsv/scipro/testdata/DataInitializer.java | 97 +++++++++++-------- 4 files changed, 54 insertions(+), 46 deletions(-) diff --git a/core/src/main/java/se/su/dsv/scipro/project/Project.java b/core/src/main/java/se/su/dsv/scipro/project/Project.java index d90a52ffcb..21fceed91d 100755 --- a/core/src/main/java/se/su/dsv/scipro/project/Project.java +++ b/core/src/main/java/se/su/dsv/scipro/project/Project.java @@ -23,7 +23,6 @@ import jakarta.persistence.MapKeyJoinColumn; import jakarta.persistence.PrePersist; import jakarta.persistence.PreUpdate; import jakarta.persistence.Table; - import java.time.Instant; import java.time.LocalDate; import java.util.ArrayList; diff --git a/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewerApproval.java b/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewerApproval.java index f07b2fd48f..56e1decee9 100644 --- a/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewerApproval.java +++ b/core/src/main/java/se/su/dsv/scipro/reviewing/ReviewerApproval.java @@ -13,7 +13,6 @@ import jakarta.persistence.OneToMany; import jakarta.persistence.OneToOne; import jakarta.persistence.OrderBy; import jakarta.persistence.Table; - import java.time.Instant; import java.util.Collections; import java.util.Date; diff --git a/core/src/test/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceIntegrationTest.java b/core/src/test/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceIntegrationTest.java index c2a82bb300..cfaa8455af 100644 --- a/core/src/test/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceIntegrationTest.java +++ b/core/src/test/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceIntegrationTest.java @@ -12,7 +12,6 @@ import java.time.Year; import java.time.ZonedDateTime; import java.util.*; import java.util.function.Function; - import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import se.su.dsv.scipro.file.FileUpload; diff --git a/test-data/src/main/java/se/su/dsv/scipro/testdata/DataInitializer.java b/test-data/src/main/java/se/su/dsv/scipro/testdata/DataInitializer.java index c496d20c0c..541ed95fac 100644 --- a/test-data/src/main/java/se/su/dsv/scipro/testdata/DataInitializer.java +++ b/test-data/src/main/java/se/su/dsv/scipro/testdata/DataInitializer.java @@ -2019,17 +2019,14 @@ public class DataInitializer implements Lifecycle, BaseData, Factory { MilestoneActivityTemplate.Type.STUDENT, "First meeting held", "First meeting with supervisor.", - milestonePhaseTemplate1, - null, - null + milestonePhaseTemplate1 ); + createMileStone( MilestoneActivityTemplate.Type.PROJECT, "Project plan approved", "Project plan approved by supervisor.", - milestonePhaseTemplate1, - null, - null + milestonePhaseTemplate1 ); List<Event> events = eventService.findAll(); @@ -2039,7 +2036,6 @@ public class DataInitializer implements Lifecycle, BaseData, Factory { "Rough draft sent to reviewer for approval (Auto)", "Rough draft sent to the reviewer for the first time.", milestonePhaseTemplate2, - null, events.stream().filter(event -> event.getName().equals("RoughDraftApprovalRequested")).findFirst().get() ); @@ -2048,7 +2044,6 @@ public class DataInitializer implements Lifecycle, BaseData, Factory { "Rough draft approved by reviewer (Auto)", "Rough draft approved.", milestonePhaseTemplate2, - null, events.stream().filter(event -> event.getName().equals("Step.ROUGH_DRAFT_APPROVAL")).findFirst().get() ); @@ -2057,110 +2052,126 @@ public class DataInitializer implements Lifecycle, BaseData, Factory { "Peer review 1 (Auto)", "This is a recommendation of when to perform peer review 1.", milestonePhaseTemplate2, - MilestoneActivityTemplate.PEER_REVIEW_ONE, - null + MilestoneActivityTemplate.PEER_REVIEW_ONE ); createMileStone( MilestoneActivityTemplate.Type.PROJECT, "Result and discussion completed and approved", "Result and discussion.", - milestonePhaseTemplate3, - null, - null + milestonePhaseTemplate3 ); + createMileStone( MilestoneActivityTemplate.Type.STUDENT, "Peer review 2", "This is a recommendation of when to perform peer review 2.", milestonePhaseTemplate3, - MilestoneActivityTemplate.PEER_REVIEW_TWO, - null + MilestoneActivityTemplate.PEER_REVIEW_TWO ); createMileStone( MilestoneActivityTemplate.Type.PROJECT, "Thesis approved for final seminar presentation", "Thesis approved for final seminar.", - milestonePhaseTemplate4, - null, - null + milestonePhaseTemplate4 ); + createMileStone( MilestoneActivityTemplate.Type.PROJECT, "Final seminar created", "Creation of final seminar.", milestonePhaseTemplate4, - MilestoneActivityTemplate.CREATE_SEMINAR, - null + MilestoneActivityTemplate.CREATE_SEMINAR ); + createMileStone( MilestoneActivityTemplate.Type.PROJECT, "Final seminar thesis uploaded", "Final seminar thesis uploaded.", milestonePhaseTemplate4, - MilestoneActivityTemplate.THESIS_UPLOADED, - null + MilestoneActivityTemplate.THESIS_UPLOADED ); + createMileStone( MilestoneActivityTemplate.Type.STUDENT, "Perform an oral and written opposition", "Opposition.", - milestonePhaseTemplate4, - null, - null + milestonePhaseTemplate4 ); + createMileStone( MilestoneActivityTemplate.Type.STUDENT, "Active participation in a final seminar", "Active participation.", - milestonePhaseTemplate4, - null, - null + milestonePhaseTemplate4 ); + createMileStone( MilestoneActivityTemplate.Type.STUDENT, "Defend the thesis in a final seminar", "Defence of final thesis.", - milestonePhaseTemplate4, - null, - null + milestonePhaseTemplate4 ); createMileStone( MilestoneActivityTemplate.Type.PROJECT, "Revised final thesis of the submitted thesis", "Revised final thesis.", - milestonePhaseTemplate5, - null, - null + milestonePhaseTemplate5 ); + createMileStone( MilestoneActivityTemplate.Type.PROJECT, "Originality report approved", "Originality report.", - milestonePhaseTemplate5, - null, - null + milestonePhaseTemplate5 ); + createMileStone( MilestoneActivityTemplate.Type.PROJECT, "Supervisor and reviewer final grading report submitted", "Final grading report.", - milestonePhaseTemplate5, - null, - null + milestonePhaseTemplate5 ); + createMileStone( MilestoneActivityTemplate.Type.STUDENT, "Grading completed", "Grading completed by examiner.", - milestonePhaseTemplate5, - null, - null + milestonePhaseTemplate5 ); } + private void createMileStone( + MilestoneActivityTemplate.Type type, + String title, + String description, + MilestonePhaseTemplate milestonePhaseTemplate + ) { + createMileStone(type, title, description, milestonePhaseTemplate, null, null); + } + + private void createMileStone( + MilestoneActivityTemplate.Type type, + String title, + String description, + MilestonePhaseTemplate milestonePhaseTemplate, + Event event + ) { + createMileStone(type, title, description, milestonePhaseTemplate, null, event); + } + + private void createMileStone( + MilestoneActivityTemplate.Type type, + String title, + String description, + MilestonePhaseTemplate milestonePhaseTemplate, + String code + ) { + createMileStone(type, title, description, milestonePhaseTemplate, code, null); + } + private void createMileStone( MilestoneActivityTemplate.Type type, String title, -- 2.39.5 From 142c8355895b053170b9c221aab0227fed235c66 Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Thu, 24 Apr 2025 11:01:16 +0200 Subject: [PATCH 38/49] 87: Set Milestone Activity Template creation date earlier to activate them --- .../java/se/su/dsv/scipro/testdata/DataInitializer.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/test-data/src/main/java/se/su/dsv/scipro/testdata/DataInitializer.java b/test-data/src/main/java/se/su/dsv/scipro/testdata/DataInitializer.java index 541ed95fac..3c8d7a78d5 100644 --- a/test-data/src/main/java/se/su/dsv/scipro/testdata/DataInitializer.java +++ b/test-data/src/main/java/se/su/dsv/scipro/testdata/DataInitializer.java @@ -6,10 +6,7 @@ import jakarta.persistence.EntityManager; import jakarta.transaction.Transactional; import java.io.ByteArrayInputStream; import java.io.InputStream; -import java.time.LocalDate; -import java.time.LocalTime; -import java.time.Month; -import java.time.ZonedDateTime; +import java.time.*; import java.util.*; import java.util.function.Function; import se.su.dsv.scipro.checklist.ChecklistCategory; @@ -2181,6 +2178,8 @@ public class DataInitializer implements Lifecycle, BaseData, Factory { Event event ) { MilestoneActivityTemplate milestoneActivityTemplate = new MilestoneActivityTemplate(type, title, description); + LocalDate ld = LocalDate.now().minusYears(1); + milestoneActivityTemplate.setDateCreated(Date.from(ld.atStartOfDay(ZoneId.systemDefault()).toInstant())); milestoneActivityTemplate.addProjectType(bachelorClass); milestoneActivityTemplate.addProjectType(masterClass); milestoneActivityTemplate.addProjectType(magisterClass); -- 2.39.5 From 18689dca2430548bfde57c372028bffa0da0da7c Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Thu, 24 Apr 2025 16:10:09 +0200 Subject: [PATCH 39/49] 87: Improve test code, milestones work now correctly. --- .../se/su/dsv/scipro/testdata/DataInitializer.java | 10 ++++++++-- .../testdata/populators/SplitProjectPopulator.java | 8 ++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/test-data/src/main/java/se/su/dsv/scipro/testdata/DataInitializer.java b/test-data/src/main/java/se/su/dsv/scipro/testdata/DataInitializer.java index 3c8d7a78d5..318054f0c1 100644 --- a/test-data/src/main/java/se/su/dsv/scipro/testdata/DataInitializer.java +++ b/test-data/src/main/java/se/su/dsv/scipro/testdata/DataInitializer.java @@ -25,6 +25,7 @@ import se.su.dsv.scipro.match.TholanderBox; import se.su.dsv.scipro.milestones.dataobjects.MilestoneActivityTemplate; import se.su.dsv.scipro.milestones.dataobjects.MilestonePhaseTemplate; import se.su.dsv.scipro.milestones.service.MilestoneActivityTemplateService; +import se.su.dsv.scipro.milestones.service.MilestonePhaseTemplateService; import se.su.dsv.scipro.notifications.dataobject.CustomEvent; import se.su.dsv.scipro.notifications.dataobject.GroupEvent; import se.su.dsv.scipro.notifications.dataobject.IdeaEvent; @@ -70,6 +71,9 @@ public class DataInitializer implements Lifecycle, BaseData, Factory { @Inject private MilestoneActivityTemplateService milestoneActivityTemplateService; + @Inject + private MilestonePhaseTemplateService milestonePhaseTemplateService; + @Inject private EventService eventService; @@ -2190,8 +2194,10 @@ public class DataInitializer implements Lifecycle, BaseData, Factory { } private MilestonePhaseTemplate createMileStonePhase(String title, String description) { - MilestonePhaseTemplate milestonePhaseTemplate1 = new MilestonePhaseTemplate(title, description); - return save(milestonePhaseTemplate1); + MilestonePhaseTemplate milestonePhaseTemplate = new MilestonePhaseTemplate(title, description); + LocalDate ld = LocalDate.now().minusYears(1); + milestonePhaseTemplate.setDateCreated(Date.from(ld.atStartOfDay(ZoneId.systemDefault()).toInstant())); + return milestonePhaseTemplateService.save(milestonePhaseTemplate); } private <T> T save(T entity) { diff --git a/test-data/src/main/java/se/su/dsv/scipro/testdata/populators/SplitProjectPopulator.java b/test-data/src/main/java/se/su/dsv/scipro/testdata/populators/SplitProjectPopulator.java index b170b217ca..7aa4cc629e 100644 --- a/test-data/src/main/java/se/su/dsv/scipro/testdata/populators/SplitProjectPopulator.java +++ b/test-data/src/main/java/se/su/dsv/scipro/testdata/populators/SplitProjectPopulator.java @@ -13,6 +13,7 @@ import se.su.dsv.scipro.project.Project; import se.su.dsv.scipro.project.ProjectService; import se.su.dsv.scipro.reviewing.ReviewerAssignmentService; import se.su.dsv.scipro.reviewing.ReviewerCapacityService; +import se.su.dsv.scipro.reviewing.ReviewerDecisionService; import se.su.dsv.scipro.reviewing.RoughDraftApproval; import se.su.dsv.scipro.reviewing.RoughDraftApprovalService; import se.su.dsv.scipro.system.User; @@ -27,18 +28,21 @@ public class SplitProjectPopulator implements TestDataPopulator { private final ReviewerCapacityService reviewerCapacityService; private final RoughDraftApprovalService roughDraftApprovalService; private final ReviewerAssignmentService reviewerAssignmentService; + private final ReviewerDecisionService reviewerDecisionService; @Inject public SplitProjectPopulator( ProjectService projectService, ReviewerCapacityService reviewerCapacityService, RoughDraftApprovalService roughDraftApprovalService, - ReviewerAssignmentService reviewerAssignmentService + ReviewerAssignmentService reviewerAssignmentService, + ReviewerDecisionService reviewerDecisionService ) { this.projectService = projectService; this.reviewerCapacityService = reviewerCapacityService; this.roughDraftApprovalService = roughDraftApprovalService; this.reviewerAssignmentService = reviewerAssignmentService; + this.reviewerDecisionService = reviewerDecisionService; } @Override @@ -68,7 +72,7 @@ public class SplitProjectPopulator implements TestDataPopulator { reviewerAssignmentService.assignReviewer(project, reviewer); Optional<RoughDraftApproval> optional = roughDraftApprovalService.findBy(project); - optional.ifPresent(rda -> rda.approve("Approve! Good Work!", Optional.empty())); + optional.ifPresent(rda -> reviewerDecisionService.approve(rda, "Approved! Good Work!", Optional.empty())); } private FileUpload dummyFile() { -- 2.39.5 From d0ee2fc98b55155000dfc25e3d6c1668e515e436 Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Mon, 28 Apr 2025 10:43:37 +0200 Subject: [PATCH 40/49] 87: Improve model handling --- .../se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.java | 2 +- .../su/dsv/scipro/admin/pages/AdminViewParentProjectPage.java | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.java b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.java index ba322bf238..bdd6c0bffa 100644 --- a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.java +++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.java @@ -56,7 +56,7 @@ public class AdminSplitProjectPanel extends Panel { }; add(splitProjectLink); - add(new Label("splitInfo", Model.of(getStatusMessage(ldModel.getObject())))); + add(new Label("splitInfo", ldModel.map(this::getStatusMessage))); } @Override diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminViewParentProjectPage.java b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminViewParentProjectPage.java index 36ec634056..3b1dacca7e 100644 --- a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminViewParentProjectPage.java +++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminViewParentProjectPage.java @@ -56,8 +56,7 @@ public class AdminViewParentProjectPage AdminEditProjectPage.class, pp ); - link.setBody(Model.of(project.getTitle() + " - " + project.getAuthorNames())); - + link.setBody(item.getModel().map(p -> p.getTitle() + " - " + p.getAuthorNames())); item.add(link); } } -- 2.39.5 From ec5c7f8b1db054c8690789a153106e4e8a6d3c15 Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Mon, 28 Apr 2025 12:05:44 +0200 Subject: [PATCH 41/49] 87: Use injected Clock in SplitOrRestartProjectServiceImpl instead of hardwired clock --- core/src/main/java/se/su/dsv/scipro/CoreConfig.java | 2 ++ .../split/SplitOrRestartProjectServiceImpl.java | 10 ++++++++-- .../se/su/dsv/scipro/reviewing/RoughDraftApproval.java | 4 ++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/se/su/dsv/scipro/CoreConfig.java b/core/src/main/java/se/su/dsv/scipro/CoreConfig.java index 134606ed7f..14f17f7ec9 100644 --- a/core/src/main/java/se/su/dsv/scipro/CoreConfig.java +++ b/core/src/main/java/se/su/dsv/scipro/CoreConfig.java @@ -820,12 +820,14 @@ public class CoreConfig { ProjectService projectService, FinalSeminarService finalSeminarService, RoughDraftApprovalService roughDraftApprovalService, + Clock clock, EventBus eventBus ) { return new SplitOrRestartProjectServiceImpl( projectService, finalSeminarService, roughDraftApprovalService, + clock, eventBus ); } diff --git a/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java index 1947b51c9b..a720f76827 100644 --- a/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java +++ b/core/src/main/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceImpl.java @@ -3,6 +3,7 @@ package se.su.dsv.scipro.project.split; import com.google.common.eventbus.EventBus; import jakarta.inject.Inject; import jakarta.transaction.Transactional; +import java.time.Clock; import java.time.Instant; import java.util.Date; import java.util.List; @@ -22,6 +23,7 @@ public class SplitOrRestartProjectServiceImpl implements SplitOrRestartProjectSe private final ProjectService projectService; private final FinalSeminarService finalSeminarService; private final RoughDraftApprovalService roughDraftApprovalService; + private final Clock clock; private final EventBus eventBus; @Inject @@ -29,11 +31,13 @@ public class SplitOrRestartProjectServiceImpl implements SplitOrRestartProjectSe ProjectService projectService, FinalSeminarService finalSeminarService, RoughDraftApprovalService roughDraftApprovalService, + Clock clock, EventBus eventBus ) { this.projectService = projectService; this.finalSeminarService = finalSeminarService; this.roughDraftApprovalService = roughDraftApprovalService; + this.clock = clock; this.eventBus = eventBus; } @@ -73,7 +77,7 @@ public class SplitOrRestartProjectServiceImpl implements SplitOrRestartProjectSe SplittableStatusRecord result = getSplittableStatus(projectId); if (result.splittableStatus() != SplittableStatus.OK) { throw new IllegalStateException( - "Project must to be verified to be able to split " + "before this method can be called." + "Project must to be verified to be able to split before this method can be called." ); } @@ -107,7 +111,9 @@ public class SplitOrRestartProjectServiceImpl implements SplitOrRestartProjectSe // Add cloned RoughDraftApproval if it's 'APPROVED' RoughDraftApproval rda = result.roughDraftApproval(); if (rda != null && rda.isApproved()) { - RoughDraftApproval clonedRda = roughDraftApprovalService.saveCloned(rda.cloneToProject(childProject)); + RoughDraftApproval clonedRda = roughDraftApprovalService.saveCloned( + rda.cloneToProject(childProject, clock.instant()) + ); // Send event to eventBus to synchronize eventual Phase Two Approval with MileStone eventBus.post(new RoughDraftApprovalApprovedClonedEvent(clonedRda)); diff --git a/core/src/main/java/se/su/dsv/scipro/reviewing/RoughDraftApproval.java b/core/src/main/java/se/su/dsv/scipro/reviewing/RoughDraftApproval.java index e22ec0af44..d31d847cc5 100644 --- a/core/src/main/java/se/su/dsv/scipro/reviewing/RoughDraftApproval.java +++ b/core/src/main/java/se/su/dsv/scipro/reviewing/RoughDraftApproval.java @@ -26,13 +26,13 @@ public class RoughDraftApproval extends ReviewerApproval { return Step.ROUGH_DRAFT_APPROVAL; } - public RoughDraftApproval cloneToProject(final Project newProject) { + public RoughDraftApproval cloneToProject(final Project newProject, Instant instant) { RoughDraftApproval rda = new RoughDraftApproval(); rda.project = newProject; this.decisions.forEach(decision -> rda.decisions.add(decision.cloneToReviewerApproval(rda))); rda.isCloned = true; - rda.cloneTimestamp = Instant.now(); + rda.cloneTimestamp = instant; return rda; } -- 2.39.5 From 7ea749e4f3b0afe17ae32873f05e55cdbc2f82b1 Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Mon, 28 Apr 2025 13:15:34 +0200 Subject: [PATCH 42/49] 87: Improve test precision with MutableFixedClock --- ...rRestartProjectServiceIntegrationTest.java | 42 ++++++++++++++----- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/core/src/test/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceIntegrationTest.java b/core/src/test/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceIntegrationTest.java index cfaa8455af..c90ba5eaaf 100644 --- a/core/src/test/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceIntegrationTest.java +++ b/core/src/test/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceIntegrationTest.java @@ -9,8 +9,12 @@ import jakarta.inject.Inject; import java.io.InputStream; import java.time.LocalDate; import java.time.Year; -import java.time.ZonedDateTime; -import java.util.*; +import java.time.ZoneId; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; import java.util.function.Function; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -18,10 +22,19 @@ import se.su.dsv.scipro.file.FileUpload; import se.su.dsv.scipro.finalseminar.FinalSeminar; import se.su.dsv.scipro.project.Project; import se.su.dsv.scipro.project.ProjectStatus; -import se.su.dsv.scipro.reviewing.*; +import se.su.dsv.scipro.reviewing.FinalSeminarApprovalService; +import se.su.dsv.scipro.reviewing.ReviewerAssignmentService; +import se.su.dsv.scipro.reviewing.ReviewerCapacityService; +import se.su.dsv.scipro.reviewing.RoughDraftApproval; +import se.su.dsv.scipro.reviewing.RoughDraftApprovalService; import se.su.dsv.scipro.security.auth.roles.Roles; -import se.su.dsv.scipro.system.*; +import se.su.dsv.scipro.system.DegreeType; +import se.su.dsv.scipro.system.Language; +import se.su.dsv.scipro.system.ProjectType; +import se.su.dsv.scipro.system.ResearchArea; +import se.su.dsv.scipro.system.User; import se.su.dsv.scipro.test.IntegrationTest; +import se.su.dsv.scipro.test.MutableFixedClock; public class SplitOrRestartProjectServiceIntegrationTest extends IntegrationTest { @@ -37,6 +50,9 @@ public class SplitOrRestartProjectServiceIntegrationTest extends IntegrationTest @Inject private FinalSeminarApprovalService finalSeminarApprovalService; + @Inject + MutableFixedClock clock; + @Inject private ReviewerAssignmentService reviewerAssignmentService; @@ -116,8 +132,9 @@ public class SplitOrRestartProjectServiceIntegrationTest extends IntegrationTest reviewer, Year.now() ); - // Todo: improve this with higher precision by using MutabledFixedClock - assertTrue(remainingTargets.spring() == REMAINING_TARGET || remainingTargets.autumn() == REMAINING_TARGET); + + assertEquals(REMAINING_TARGET, remainingTargets.spring()); + assertEquals(TARGET, remainingTargets.autumn()); List<Project> childProjects = splitOrRestartProjectService.getChildProjects(parentProject.getId()); @@ -150,8 +167,9 @@ public class SplitOrRestartProjectServiceIntegrationTest extends IntegrationTest reviewer, Year.now() ); - // todo: improve this by using MutableFixedClock - assertTrue(remainingTargets.spring() == REMAINING_TARGET || remainingTargets.autumn() == REMAINING_TARGET); + + assertEquals(REMAINING_TARGET, remainingTargets.spring()); + assertEquals(TARGET, remainingTargets.autumn()); List<Project> childProjects = splitOrRestartProjectService.getChildProjects(parentProject.getId()); @@ -178,7 +196,9 @@ public class SplitOrRestartProjectServiceIntegrationTest extends IntegrationTest FinalSeminar finalSeminar = new FinalSeminar(); finalSeminar.setProject(parentProject); - finalSeminar.setStartDate(Date.from(ZonedDateTime.now().plusDays(10).toInstant())); + + LocalDate plus30Days = LocalDate.now(clock).plusDays(30); + finalSeminar.setStartDate(Date.from(plus30Days.atStartOfDay(ZoneId.systemDefault()).toInstant())); finalSeminar.setRoom("room"); finalSeminar.setPresentationLanguage(Language.ENGLISH); parentProject.setLanguage(Language.ENGLISH); @@ -199,7 +219,7 @@ public class SplitOrRestartProjectServiceIntegrationTest extends IntegrationTest project.setProjectType(projectType); project.setProjectStatus(active); project.setHeadSupervisor(supervisor); - project.setStartDate(LocalDate.now()); + project.setStartDate(LocalDate.now().withMonth(1).withDayOfMonth(1)); author1 = createUser("Adam", "Student", "adam.student@example.com"); project.addProjectParticipant(author1); @@ -233,6 +253,8 @@ public class SplitOrRestartProjectServiceIntegrationTest extends IntegrationTest parentProject.addProjectParticipant(author2); save(parentProject); + clock.setDate(LocalDate.now().withMonth(3).withDayOfMonth(1)); + roughDraftApprovalService.requestApproval(parentProject, dummyFile(), "comment"); reviewer = createEmployee("Lisa", "Employee", "lisa.employee@example.com", true); -- 2.39.5 From 2b5da3fee1ad7b9ee513b52208f6aa28f432fd16 Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Mon, 28 Apr 2025 13:19:13 +0200 Subject: [PATCH 43/49] 87: Update migration db-script to higher version --- ...ent_phase2_review.sql => V8__project_parent_phase2_review.sql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename core/src/main/resources/db/migration/{V7__project_parent_phase2_review.sql => V8__project_parent_phase2_review.sql} (100%) diff --git a/core/src/main/resources/db/migration/V7__project_parent_phase2_review.sql b/core/src/main/resources/db/migration/V8__project_parent_phase2_review.sql similarity index 100% rename from core/src/main/resources/db/migration/V7__project_parent_phase2_review.sql rename to core/src/main/resources/db/migration/V8__project_parent_phase2_review.sql -- 2.39.5 From 0f1d58686347f530803bda71a7a8307cb98557cf Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Mon, 28 Apr 2025 15:13:23 +0200 Subject: [PATCH 44/49] 87: Add info about if RoughDraftApproval is cloned --- .../se/su/dsv/scipro/reviewer/ApprovalReviewerPanel.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/view/src/main/java/se/su/dsv/scipro/reviewer/ApprovalReviewerPanel.java b/view/src/main/java/se/su/dsv/scipro/reviewer/ApprovalReviewerPanel.java index 37e981c569..05ae98907e 100644 --- a/view/src/main/java/se/su/dsv/scipro/reviewer/ApprovalReviewerPanel.java +++ b/view/src/main/java/se/su/dsv/scipro/reviewer/ApprovalReviewerPanel.java @@ -83,10 +83,11 @@ public abstract class ApprovalReviewerPanel extends Panel { LinkWrapper.apply(componentId, id -> { AbstractLink link = newDecisionLink(id, rowModel.map(Decision::getReviewerApproval)); link.setBody( - rowModel - .map(Decision::getReviewerApproval) - .map(ReviewerApproval::getProject) - .map(Project::getTitle) + rowModel.map(dec -> { + ReviewerApproval ra = dec.getReviewerApproval(); + Project p = ra.getProject(); + return ra.getCloned() ? p.getTitle() + " (Cloned)" : p.getTitle(); + }) ); return link; }) -- 2.39.5 From 8d7a0f6eb1670b9bb6dd9ab048e531f964277e3b Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Mon, 5 May 2025 14:44:23 +0200 Subject: [PATCH 45/49] 87: Improve test by using fixed year (2025) --- .../SplitOrRestartProjectServiceIntegrationTest.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/core/src/test/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceIntegrationTest.java b/core/src/test/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceIntegrationTest.java index c90ba5eaaf..f8551c5596 100644 --- a/core/src/test/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceIntegrationTest.java +++ b/core/src/test/java/se/su/dsv/scipro/project/split/SplitOrRestartProjectServiceIntegrationTest.java @@ -41,6 +41,8 @@ public class SplitOrRestartProjectServiceIntegrationTest extends IntegrationTest private static int TARGET = 3; private static int REMAINING_TARGET = 2; + private static int FIXED_YEAR = 2025; + @Inject private SplitOrRestartProjectService splitOrRestartProjectService; @@ -130,7 +132,7 @@ public class SplitOrRestartProjectServiceIntegrationTest extends IntegrationTest ReviewerCapacityService.RemainingTargets remainingTargets = reviewerCapacityService.getRemainingTargets( reviewer, - Year.now() + Year.of(FIXED_YEAR) ); assertEquals(REMAINING_TARGET, remainingTargets.spring()); @@ -165,7 +167,7 @@ public class SplitOrRestartProjectServiceIntegrationTest extends IntegrationTest ReviewerCapacityService.RemainingTargets remainingTargets = reviewerCapacityService.getRemainingTargets( reviewer, - Year.now() + Year.of(FIXED_YEAR) ); assertEquals(REMAINING_TARGET, remainingTargets.spring()); @@ -253,14 +255,14 @@ public class SplitOrRestartProjectServiceIntegrationTest extends IntegrationTest parentProject.addProjectParticipant(author2); save(parentProject); - clock.setDate(LocalDate.now().withMonth(3).withDayOfMonth(1)); + clock.setDate(LocalDate.now().withYear(FIXED_YEAR).withMonth(3).withDayOfMonth(1)); roughDraftApprovalService.requestApproval(parentProject, dummyFile(), "comment"); reviewer = createEmployee("Lisa", "Employee", "lisa.employee@example.com", true); reviewerCapacityService.assignTarget( reviewer, - new ReviewerCapacityService.Target(Year.now(), TARGET, TARGET, "") + new ReviewerCapacityService.Target(Year.of(FIXED_YEAR), TARGET, TARGET, "") ); reviewerAssignmentService.assignReviewer(parentProject, reviewer); } -- 2.39.5 From 821b53476320e04d74b68c942223f78ae85559a1 Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Mon, 5 May 2025 15:49:17 +0200 Subject: [PATCH 46/49] 87: Remove unnecessary authorization check --- .../su/dsv/scipro/admin/pages/AdminSplitProjectPanel.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.java b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.java index bdd6c0bffa..83a50b64c2 100644 --- a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.java +++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPanel.java @@ -14,8 +14,6 @@ import org.apache.wicket.model.Model; import org.apache.wicket.request.mapper.parameter.PageParameters; import se.su.dsv.scipro.project.Project; import se.su.dsv.scipro.project.split.SplitOrRestartProjectService; -import se.su.dsv.scipro.security.auth.roles.Roles; -import se.su.dsv.scipro.session.SciProSession; import se.su.dsv.scipro.util.PageParameterKeys; public class AdminSplitProjectPanel extends Panel { @@ -59,12 +57,6 @@ public class AdminSplitProjectPanel extends Panel { add(new Label("splitInfo", ldModel.map(this::getStatusMessage))); } - @Override - protected void onConfigure() { - super.onConfigure(); - setVisibilityAllowed(SciProSession.get().authorizedForRole(Roles.ADMIN)); - } - private String getStatusMessage(SplittableStatus status) { return switch (status) { case OK -> ""; -- 2.39.5 From ca6f580006154f354a429bdceca000d8ad9c6370 Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Mon, 5 May 2025 15:57:43 +0200 Subject: [PATCH 47/49] 87: Use UserLabel component --- .../se/su/dsv/scipro/admin/pages/AdminSplitProjectPage.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPage.java b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPage.java index 4145fdd056..8a02d457aa 100644 --- a/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPage.java +++ b/view/src/main/java/se/su/dsv/scipro/admin/pages/AdminSplitProjectPage.java @@ -12,6 +12,7 @@ import org.apache.wicket.model.IModel; import org.apache.wicket.request.mapper.parameter.PageParameters; import se.su.dsv.scipro.components.menuhighlighting.MenuHighlightAdminProjectManagement; import se.su.dsv.scipro.data.DetachableServiceModel; +import se.su.dsv.scipro.profile.UserLabel; import se.su.dsv.scipro.project.Project; import se.su.dsv.scipro.project.ProjectService; import se.su.dsv.scipro.project.split.SplitOrRestartProjectService; @@ -50,7 +51,7 @@ public class AdminSplitProjectPage extends AbstractAdminProjectPage implements M new ListView<>("authorList", model.map(Project::getProjectParticipants).map(ArrayList::new)) { @Override protected void populateItem(ListItem<User> item) { - item.add(new Label("author", item.getModel().map(User::getFullName))); + item.add(new UserLabel("author", item.getModel())); } } ); -- 2.39.5 From 8f4d2620be94d15382d0573c62c905e04d4ff516 Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Wed, 7 May 2025 13:31:16 +0200 Subject: [PATCH 48/49] 87: Improve testdata, it's now possible to follow up individual milestones as peer review --- .../dsv/scipro/testdata/DataInitializer.java | 8 ++- .../populators/SplitProjectPopulator.java | 65 +++++++++++++++++-- 2 files changed, 64 insertions(+), 9 deletions(-) diff --git a/test-data/src/main/java/se/su/dsv/scipro/testdata/DataInitializer.java b/test-data/src/main/java/se/su/dsv/scipro/testdata/DataInitializer.java index 318054f0c1..8a4fca0ffe 100644 --- a/test-data/src/main/java/se/su/dsv/scipro/testdata/DataInitializer.java +++ b/test-data/src/main/java/se/su/dsv/scipro/testdata/DataInitializer.java @@ -2053,7 +2053,8 @@ public class DataInitializer implements Lifecycle, BaseData, Factory { "Peer review 1 (Auto)", "This is a recommendation of when to perform peer review 1.", milestonePhaseTemplate2, - MilestoneActivityTemplate.PEER_REVIEW_ONE + MilestoneActivityTemplate.PEER_REVIEW_ONE, + events.stream().filter(event -> event.getName().equals("FirstPeerReviewCompleted")).findFirst().get() ); createMileStone( @@ -2065,10 +2066,11 @@ public class DataInitializer implements Lifecycle, BaseData, Factory { createMileStone( MilestoneActivityTemplate.Type.STUDENT, - "Peer review 2", + "Peer review 2 (Auto)", "This is a recommendation of when to perform peer review 2.", milestonePhaseTemplate3, - MilestoneActivityTemplate.PEER_REVIEW_TWO + MilestoneActivityTemplate.PEER_REVIEW_TWO, + events.stream().filter(event -> event.getName().equals("SecondPeerReviewCompleted")).findFirst().get() ); createMileStone( diff --git a/test-data/src/main/java/se/su/dsv/scipro/testdata/populators/SplitProjectPopulator.java b/test-data/src/main/java/se/su/dsv/scipro/testdata/populators/SplitProjectPopulator.java index 7aa4cc629e..33f0fd7e2a 100644 --- a/test-data/src/main/java/se/su/dsv/scipro/testdata/populators/SplitProjectPopulator.java +++ b/test-data/src/main/java/se/su/dsv/scipro/testdata/populators/SplitProjectPopulator.java @@ -9,6 +9,8 @@ import java.util.Set; import java.util.function.Function; import org.springframework.stereotype.Service; import se.su.dsv.scipro.file.FileUpload; +import se.su.dsv.scipro.peer.PeerPortal; +import se.su.dsv.scipro.peer.PeerRequest; import se.su.dsv.scipro.project.Project; import se.su.dsv.scipro.project.ProjectService; import se.su.dsv.scipro.reviewing.ReviewerAssignmentService; @@ -16,6 +18,7 @@ import se.su.dsv.scipro.reviewing.ReviewerCapacityService; import se.su.dsv.scipro.reviewing.ReviewerDecisionService; import se.su.dsv.scipro.reviewing.RoughDraftApproval; import se.su.dsv.scipro.reviewing.RoughDraftApprovalService; +import se.su.dsv.scipro.system.Language; import se.su.dsv.scipro.system.User; import se.su.dsv.scipro.testdata.BaseData; import se.su.dsv.scipro.testdata.Factory; @@ -29,6 +32,9 @@ public class SplitProjectPopulator implements TestDataPopulator { private final RoughDraftApprovalService roughDraftApprovalService; private final ReviewerAssignmentService reviewerAssignmentService; private final ReviewerDecisionService reviewerDecisionService; + private final PeerPortal peerPortal; + + private User otherSupervisorAndReviewer; @Inject public SplitProjectPopulator( @@ -36,45 +42,92 @@ public class SplitProjectPopulator implements TestDataPopulator { ReviewerCapacityService reviewerCapacityService, RoughDraftApprovalService roughDraftApprovalService, ReviewerAssignmentService reviewerAssignmentService, - ReviewerDecisionService reviewerDecisionService + ReviewerDecisionService reviewerDecisionService, + PeerPortal peerPortal ) { this.projectService = projectService; this.reviewerCapacityService = reviewerCapacityService; this.roughDraftApprovalService = roughDraftApprovalService; this.reviewerAssignmentService = reviewerAssignmentService; this.reviewerDecisionService = reviewerDecisionService; + this.peerPortal = peerPortal; } @Override public void populate(BaseData baseData, Factory factory) { + setUpOtherProjects(baseData, factory); + User supervisor = factory.createSupervisor("Emil"); User author1 = factory.createAuthor("Scott"); User author2 = factory.createAuthor("Scarlett"); - User reviewer = factory.createReviewer("Elias"); - - reviewerCapacityService.assignTarget(reviewer, new ReviewerCapacityService.Target(Year.now(), 3, 3, "")); + reviewerCapacityService.assignTarget( + otherSupervisorAndReviewer, + new ReviewerCapacityService.Target(Year.now(), 3, 3, "") + ); Project project = Project.builder() .title("Operating System Boot Time Security") .projectType(baseData.bachelor()) .startDate(LocalDate.now()) .headSupervisor(supervisor) - .reviewers(Set.of(reviewer)) + .reviewers(Set.of(otherSupervisorAndReviewer)) .projectParticipants(Set.of(author1, author2)) .build(); projectService.save(project); + setUpPeerRequest(project, author2, "Please checkout the OS Boot Time Security. Draft 1"); + setUpPeerRequest(project, author2, "Please checkout the OS Boot Time Security. Draft 2"); + roughDraftApprovalService.requestApproval(project, dummyFile(), "Request review."); - reviewerAssignmentService.assignReviewer(project, reviewer); + reviewerAssignmentService.assignReviewer(project, otherSupervisorAndReviewer); Optional<RoughDraftApproval> optional = roughDraftApprovalService.findBy(project); optional.ifPresent(rda -> reviewerDecisionService.approve(rda, "Approved! Good Work!", Optional.empty())); } + private void setUpOtherProjects(BaseData baseData, Factory factory) { + otherSupervisorAndReviewer = factory.createReviewer("Elias"); + + User author1 = factory.createAuthor("Sebastian"); + + Project otherProject1 = Project.builder() + .title("The CISC Architecture") + .projectType(baseData.bachelor()) + .startDate(LocalDate.now()) + .headSupervisor(otherSupervisorAndReviewer) + .projectParticipants(Set.of(author1)) + .build(); + + projectService.save(otherProject1); + + setUpPeerRequest(otherProject1, author1, "Please checkout the CISC Architecture."); + + User author2 = factory.createAuthor("Sven"); + Project otherProject2 = Project.builder() + .title("The RISC Architecture") + .projectType(baseData.bachelor()) + .startDate(LocalDate.now()) + .headSupervisor(otherSupervisorAndReviewer) + .projectParticipants(Set.of(author2)) + .build(); + projectService.save(otherProject2); + + setUpPeerRequest(otherProject2, author2, "Please checkout the RISC Architecture."); + } + + private void setUpPeerRequest(Project project, User requester, String comment) { + PeerRequest peerRequest = new PeerRequest(); + peerRequest.setProject(project); + peerRequest.setRequester(requester); + peerRequest.setComment(comment); + peerRequest.setLanguage(Language.ENGLISH); + peerPortal.storePeerRequest(dummyFile(), peerRequest); + } + private FileUpload dummyFile() { return new FileUpload() { @Override -- 2.39.5 From f87b0d70e83f5ffe915b96badb17abeebe576f94 Mon Sep 17 00:00:00 2001 From: Tom Zhao <tom.zhao@dsv.su.se> Date: Fri, 9 May 2025 14:00:37 +0200 Subject: [PATCH 49/49] 87: Use single import --- .../dsv/scipro/testdata/DataInitializer.java | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/test-data/src/main/java/se/su/dsv/scipro/testdata/DataInitializer.java b/test-data/src/main/java/se/su/dsv/scipro/testdata/DataInitializer.java index 99139fde92..e61f794393 100644 --- a/test-data/src/main/java/se/su/dsv/scipro/testdata/DataInitializer.java +++ b/test-data/src/main/java/se/su/dsv/scipro/testdata/DataInitializer.java @@ -9,7 +9,14 @@ import java.time.LocalTime; import java.time.Month; import java.time.ZoneId; import java.time.ZonedDateTime; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; import se.su.dsv.scipro.checklist.ChecklistCategory; import se.su.dsv.scipro.data.dataobjects.Member; import se.su.dsv.scipro.file.FileReference; @@ -46,7 +53,20 @@ import se.su.dsv.scipro.report.GradingReportTemplate; import se.su.dsv.scipro.reviewing.ReviewerAssignmentService; import se.su.dsv.scipro.reviewing.RoughDraftApprovalService; import se.su.dsv.scipro.security.auth.roles.Roles; -import se.su.dsv.scipro.system.*; +import se.su.dsv.scipro.system.Event; +import se.su.dsv.scipro.system.EventService; +import se.su.dsv.scipro.system.Language; +import se.su.dsv.scipro.system.Lifecycle; +import se.su.dsv.scipro.system.Password; +import se.su.dsv.scipro.system.PasswordHandler; +import se.su.dsv.scipro.system.PasswordService; +import se.su.dsv.scipro.system.Program; +import se.su.dsv.scipro.system.ProjectType; +import se.su.dsv.scipro.system.ResearchArea; +import se.su.dsv.scipro.system.Unit; +import se.su.dsv.scipro.system.User; +import se.su.dsv.scipro.system.UserService; +import se.su.dsv.scipro.system.Username; import se.su.dsv.scipro.util.Pair; public class DataInitializer implements Lifecycle, BaseData, Factory { -- 2.39.5