3204 Assign the reviewer
This commit is contained in:
parent
6d076b2b77
commit
db612da854
core/src/main/java/se/su/dsv/scipro
notifications
reviewing
view/src/main/java/se/su/dsv/scipro
admin/pages
datatables/project
@ -19,6 +19,7 @@ import se.su.dsv.scipro.peer.PeerRequestExpiredEvent;
|
||||
import se.su.dsv.scipro.project.ProjectActivatedEvent;
|
||||
import se.su.dsv.scipro.project.ProjectCompletedEvent;
|
||||
import se.su.dsv.scipro.project.ProjectDeactivatedEvent;
|
||||
import se.su.dsv.scipro.project.ReviewerAssignedEvent;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
@ -120,4 +121,9 @@ public class Notifications {
|
||||
source.setMessage(event.getFinalSeminar().getProjectTitle());
|
||||
notificationController.notifyCustomProject(event.getProject(), ProjectEvent.Event.PARTICIPATION_FAILED, source, recipients);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void reviewersChanged(ReviewerAssignedEvent event) {
|
||||
notificationController.notifyProject(event.getProject(), ProjectEvent.Event.REVIEWERS_CHANGED, new NotificationSource());
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,16 @@
|
||||
package se.su.dsv.scipro.reviewing;
|
||||
|
||||
import se.su.dsv.scipro.project.Project;
|
||||
import se.su.dsv.scipro.system.User;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
public interface ReviewerAssignmentService {
|
||||
ReviewerCandidates getCandidatesToReview(Project project, LocalDate date);
|
||||
|
||||
ReviewerAssignment assignReviewer(Project project, User reviewer);
|
||||
|
||||
enum ReviewerAssignment {
|
||||
OK, ERROR_IS_SUPERVISOR, ERROR_IS_NOT_REVIEWER
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,10 @@
|
||||
package se.su.dsv.scipro.reviewing;
|
||||
|
||||
import com.google.common.eventbus.EventBus;
|
||||
import se.su.dsv.scipro.project.Project;
|
||||
import se.su.dsv.scipro.project.ProjectService;
|
||||
import se.su.dsv.scipro.project.ReviewerAssignedEvent;
|
||||
import se.su.dsv.scipro.security.auth.roles.Roles;
|
||||
import se.su.dsv.scipro.system.Unit;
|
||||
import se.su.dsv.scipro.system.User;
|
||||
import se.su.dsv.scipro.system.UserService;
|
||||
@ -12,22 +16,27 @@ import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
class ReviewerCapacityServiceImpl implements ReviewerCapacityService, ReviewerAssignmentService {
|
||||
private final ReviewerTargetRepository reviewerTargetRepository;
|
||||
private final DecisionRepository decisionRepository;
|
||||
private final UserService userService;
|
||||
private final ProjectService projectService;
|
||||
private final EventBus eventBus;
|
||||
|
||||
@Inject
|
||||
ReviewerCapacityServiceImpl(
|
||||
ReviewerTargetRepository reviewerTargetRepository,
|
||||
DecisionRepository decisionRepository,
|
||||
UserService userService)
|
||||
UserService userService,
|
||||
ProjectService projectService,
|
||||
EventBus eventBus)
|
||||
{
|
||||
this.reviewerTargetRepository = reviewerTargetRepository;
|
||||
this.decisionRepository = decisionRepository;
|
||||
this.userService = userService;
|
||||
this.projectService = projectService;
|
||||
this.eventBus = eventBus;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -121,6 +130,23 @@ class ReviewerCapacityServiceImpl implements ReviewerCapacityService, ReviewerAs
|
||||
return new ReviewerCandidates(good, mismatched, busy, unavailable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReviewerAssignment assignReviewer(Project project, User reviewer) {
|
||||
if (project.getHeadSupervisor().equals(reviewer)) {
|
||||
return ReviewerAssignment.ERROR_IS_SUPERVISOR;
|
||||
} else if (!reviewer.getRoles().contains(Roles.REVIEWER)) {
|
||||
return ReviewerAssignment.ERROR_IS_NOT_REVIEWER;
|
||||
} else {
|
||||
for (User currentReviewer : project.getReviewers()) {
|
||||
project.removeReviewer(currentReviewer);
|
||||
}
|
||||
project.addReviewer(reviewer);
|
||||
projectService.save(project);
|
||||
eventBus.post(new ReviewerAssignedEvent(project, reviewer));
|
||||
return ReviewerAssignment.OK;
|
||||
}
|
||||
}
|
||||
|
||||
private int countAssignedReviews(ReviewerTarget reviewerTarget) {
|
||||
return countAssignedReviews(reviewerTarget.getReviewer(), reviewerTarget.getFromDate(), reviewerTarget.getToDate());
|
||||
}
|
||||
|
@ -17,10 +17,14 @@
|
||||
|
||||
<dt>Language</dt>
|
||||
<dd wicket:id="language"></dd>
|
||||
|
||||
<dt>Reviewer</dt>
|
||||
<dd wicket:id="reviewer"></dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-xl-4 col-md-6" wicket:id="reviewers">
|
||||
<div wicket:id="feedback"></div>
|
||||
<wicket:enclosure child="good_candidates">
|
||||
<h3>Good candidates</h3>
|
||||
<p>
|
||||
@ -40,7 +44,7 @@
|
||||
</div>
|
||||
<div class="col-auto my-auto">
|
||||
<div class="card-body py-0">
|
||||
<button class="btn btn-success">Assign</button>
|
||||
<button class="btn btn-success" wicket:id="assign">Assign</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -81,7 +85,7 @@
|
||||
</div>
|
||||
<div class="col-auto my-auto">
|
||||
<div class="card-body py-0">
|
||||
<button class="btn btn-success">Assign</button>
|
||||
<button class="btn btn-success" wicket:id="assign">Assign</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -106,7 +110,7 @@
|
||||
</div>
|
||||
<div class="col-auto my-auto">
|
||||
<div class="card-body py-0">
|
||||
<button class="btn btn-success">Assign</button>
|
||||
<button class="btn btn-success" wicket:id="assign">Assign</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -130,7 +134,7 @@
|
||||
</div>
|
||||
<div class="col-auto my-auto">
|
||||
<div class="card-body py-0">
|
||||
<button class="btn btn-secondary">Assign</button>
|
||||
<button class="btn btn-secondary" wicket:id="assign">Assign</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,11 +1,14 @@
|
||||
package se.su.dsv.scipro.admin.pages;
|
||||
|
||||
import org.apache.wicket.RestartResponseException;
|
||||
import org.apache.wicket.markup.html.GenericWebMarkupContainer;
|
||||
import org.apache.wicket.markup.html.WebMarkupContainer;
|
||||
import org.apache.wicket.markup.html.basic.EnumLabel;
|
||||
import org.apache.wicket.markup.html.basic.Label;
|
||||
import org.apache.wicket.markup.html.link.Link;
|
||||
import org.apache.wicket.markup.html.list.ListItem;
|
||||
import org.apache.wicket.markup.html.list.ListView;
|
||||
import org.apache.wicket.markup.html.panel.FeedbackPanel;
|
||||
import org.apache.wicket.model.IModel;
|
||||
import org.apache.wicket.model.LoadableDetachableModel;
|
||||
import org.apache.wicket.request.mapper.parameter.PageParameters;
|
||||
@ -64,19 +67,24 @@ public class AdminAssignReviewerPage extends AbstractAdminProjectPage {
|
||||
add(new Label("research_area", projectModel.map(Project::getResearchArea).map(ResearchArea::getTitle)));
|
||||
add(new UserLinkPanel("supervisor", projectModel.map(Project::getHeadSupervisor)));
|
||||
add(new EnumLabel<>("language", projectModel.map(Project::getLanguage)));
|
||||
add(new UserLinkPanel("reviewer", projectModel.map(Project::getReviewer)));
|
||||
}
|
||||
}
|
||||
|
||||
private static class AvailableReviewersPanel extends WebMarkupContainer {
|
||||
private static class AvailableReviewersPanel extends GenericWebMarkupContainer<Project> {
|
||||
@Inject
|
||||
private ReviewerAssignmentService reviewerAssignmentService;
|
||||
@Inject
|
||||
private Clock clock;
|
||||
|
||||
private final IModel<ReviewerCandidates> reviewerCandidates;
|
||||
|
||||
public AvailableReviewersPanel(String id, IModel<Project> projectModel) {
|
||||
super(id, projectModel);
|
||||
|
||||
IModel<ReviewerCandidates> reviewerCandidates = LoadableDetachableModel.of(() ->
|
||||
add(new FeedbackPanel("feedback"));
|
||||
|
||||
reviewerCandidates = LoadableDetachableModel.of(() ->
|
||||
reviewerAssignmentService.getCandidatesToReview(projectModel.getObject(), LocalDate.now(clock)));
|
||||
|
||||
add(new AutoHidingListView<>("good_candidates", reviewerCandidates.map(ReviewerCandidates::good)) {
|
||||
@ -86,6 +94,7 @@ public class AdminAssignReviewerPage extends AbstractAdminProjectPage {
|
||||
item.add(new UserLabel("user", item.getModel().map(ReviewerCandidates.Candidate::reviewer)));
|
||||
item.add(new Label("target", item.getModel().map(ReviewerCandidates.Candidate::target)));
|
||||
item.add(new Label("assigned", item.getModel().map(ReviewerCandidates.Candidate::assigned)));
|
||||
item.add(new AssignReviewerLink("assign", item.getModel().map(ReviewerCandidates.Candidate::reviewer)));
|
||||
}
|
||||
});
|
||||
add(new AutoHidingListView<>("mismatched_candidates", reviewerCandidates.map(ReviewerCandidates::mismatched)) {
|
||||
@ -96,6 +105,7 @@ public class AdminAssignReviewerPage extends AbstractAdminProjectPage {
|
||||
item.add(new UserLabel("user", reviewerModel));
|
||||
item.add(new Label("target", item.getModel().map(ReviewerCandidates.Candidate::target)));
|
||||
item.add(new Label("assigned", item.getModel().map(ReviewerCandidates.Candidate::assigned)));
|
||||
item.add(new AssignReviewerLink("assign", reviewerModel));
|
||||
item.add(new ListView<>("research_areas", reviewerModel.map(User::getResearchAreas).map(ArrayList::new)) {
|
||||
@Override
|
||||
protected void populateItem(ListItem<ResearchArea> item) {
|
||||
@ -117,6 +127,7 @@ public class AdminAssignReviewerPage extends AbstractAdminProjectPage {
|
||||
item.add(new UserLabel("user", item.getModel().map(ReviewerCandidates.Candidate::reviewer)));
|
||||
item.add(new Label("target", item.getModel().map(ReviewerCandidates.Candidate::target)));
|
||||
item.add(new Label("assigned", item.getModel().map(ReviewerCandidates.Candidate::assigned)));
|
||||
item.add(new AssignReviewerLink("assign", item.getModel().map(ReviewerCandidates.Candidate::reviewer)));
|
||||
}
|
||||
});
|
||||
add(new AutoHidingListView<>("unavailable_candidates", reviewerCandidates.map(ReviewerCandidates::unavailable)) {
|
||||
@ -124,8 +135,34 @@ public class AdminAssignReviewerPage extends AbstractAdminProjectPage {
|
||||
protected void populateItem(ListItem<ReviewerCandidates.Candidate> item) {
|
||||
item.add(new UserProfileImage("image", item.getModel().map(ReviewerCandidates.Candidate::reviewer), UserProfileImage.Size.MEDIUM));
|
||||
item.add(new UserLabel("user", item.getModel().map(ReviewerCandidates.Candidate::reviewer)));
|
||||
IModel<User> reviewer = item.getModel().map(ReviewerCandidates.Candidate::reviewer);
|
||||
item.add(new AssignReviewerLink("assign", reviewer));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private class AssignReviewerLink extends Link<User> {
|
||||
public AssignReviewerLink(String id, IModel<User> reviewer) {
|
||||
super(id, reviewer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick() {
|
||||
ReviewerAssignmentService.ReviewerAssignment reviewerAssignment = reviewerAssignmentService.assignReviewer(
|
||||
AvailableReviewersPanel.this.getModelObject(),
|
||||
getModelObject());
|
||||
switch (reviewerAssignment) {
|
||||
case OK -> AvailableReviewersPanel.this.success(AvailableReviewersPanel.this.getString("reviewer_assigned"));
|
||||
case ERROR_IS_SUPERVISOR -> AvailableReviewersPanel.this.error(AvailableReviewersPanel.this.getString("error_reviewer_is_supervisor"));
|
||||
case ERROR_IS_NOT_REVIEWER -> AvailableReviewersPanel.this.error(AvailableReviewersPanel.this.getString("error_reviewer_is_not_reviewer"));
|
||||
}
|
||||
// have to detach the model since fetching the model object for this component will
|
||||
// cause the model object for the ListItem which will cause the ListView to be loaded and
|
||||
// that in turn will load the reviewer candidates. This means the candidates will be loaded
|
||||
// before the re-assignment has taken place. Forcibly detach the model so that it will have
|
||||
// to be loaded again before rendering.
|
||||
AvailableReviewersPanel.this.reviewerCandidates.detach();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
3
view/src/main/java/se/su/dsv/scipro/admin/pages/AdminAssignReviewerPage.utf8.properties
Normal file
3
view/src/main/java/se/su/dsv/scipro/admin/pages/AdminAssignReviewerPage.utf8.properties
Normal file
@ -0,0 +1,3 @@
|
||||
reviewer_assigned=Reviewer assigned
|
||||
error_reviewer_is_supervisor=The selected reviewer is the supervisor of the project
|
||||
error_reviewer_is_not_reviewer=The selected reviewer does not have the reviewer role
|
@ -22,7 +22,6 @@ import org.apache.wicket.request.mapper.parameter.PageParameters;
|
||||
import se.su.dsv.scipro.admin.pages.AdminAssignReviewerPage;
|
||||
import se.su.dsv.scipro.admin.pages.AdminCreateProjectPage;
|
||||
import se.su.dsv.scipro.admin.pages.AdminEditProjectPage;
|
||||
import se.su.dsv.scipro.components.EmployeeAutoCompleteDivPanel;
|
||||
import se.su.dsv.scipro.components.EnumLambdaColumn;
|
||||
import se.su.dsv.scipro.components.ExportableDataPanel;
|
||||
import se.su.dsv.scipro.components.LinkWrapper;
|
||||
@ -30,17 +29,13 @@ import se.su.dsv.scipro.components.ListAdapterModel;
|
||||
import se.su.dsv.scipro.components.TemporalColumn;
|
||||
import se.su.dsv.scipro.components.datatables.MultipleUsersColumn;
|
||||
import se.su.dsv.scipro.components.datatables.UserColumn;
|
||||
import se.su.dsv.scipro.data.DetachableServiceModel;
|
||||
import se.su.dsv.scipro.dataproviders.FilteredDataProvider;
|
||||
import se.su.dsv.scipro.datatables.AjaxCheckboxWrapper;
|
||||
import se.su.dsv.scipro.notifications.NotificationController;
|
||||
import se.su.dsv.scipro.notifications.dataobject.NotificationSource;
|
||||
import se.su.dsv.scipro.notifications.dataobject.ProjectEvent;
|
||||
import se.su.dsv.scipro.profile.UserLinkPanel;
|
||||
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.ReviewerAssignedEvent;
|
||||
import se.su.dsv.scipro.security.auth.roles.Roles;
|
||||
import se.su.dsv.scipro.session.SciProSession;
|
||||
import se.su.dsv.scipro.system.DegreeType;
|
||||
@ -60,12 +55,6 @@ public class ProjectDataPanel extends Panel {
|
||||
private ProjectTypeService projectTypeService;
|
||||
@Inject
|
||||
private ProjectService projectService;
|
||||
@Inject
|
||||
private EventBus eventBus;
|
||||
@Inject
|
||||
private NotificationController notificationController;
|
||||
@Inject
|
||||
private UserService userService;
|
||||
|
||||
private ExportableDataPanel<Project, String> exportableDataPanel;
|
||||
private ProjectService.Filter filter;
|
||||
@ -193,10 +182,6 @@ public class ProjectDataPanel extends Panel {
|
||||
};
|
||||
}
|
||||
|
||||
private void notifyReviewersChanged(Project project) {
|
||||
notificationController.notifyProject(project, ProjectEvent.Event.REVIEWERS_CHANGED, new NotificationSource());
|
||||
}
|
||||
|
||||
private void addFeedbackPanel() {
|
||||
feedback = new FencedFeedbackPanel("feedback", this);
|
||||
feedback.setOutputMarkupId(true);
|
||||
|
Loading…
x
Reference in New Issue
Block a user