3321 PO 4) Show current reviewer and allow un-assigning

This commit is contained in:
Andreas Svanberg 2024-02-01 15:40:39 +01:00
parent b4edcf50c7
commit 36d3ab35ad
7 changed files with 123 additions and 1 deletions

@ -0,0 +1,4 @@
package se.su.dsv.scipro.project;
public record ReviewerUnassignedEvent(Project project) {
}

@ -10,6 +10,8 @@ public interface ReviewerAssignmentService {
ReviewerAssignment assignReviewer(Project project, User reviewer);
void unassignReviewer(Project project);
enum ReviewerAssignment {
OK, ERROR_IS_SUPERVISOR, ERROR_IS_NOT_REVIEWER
}

@ -1,9 +1,11 @@
package se.su.dsv.scipro.reviewing;
import com.google.common.eventbus.EventBus;
import com.google.inject.persist.Transactional;
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.project.ReviewerUnassignedEvent;
import se.su.dsv.scipro.security.auth.roles.Roles;
import se.su.dsv.scipro.system.Language;
import se.su.dsv.scipro.system.Unit;
@ -20,6 +22,7 @@ import java.util.EnumSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Stream;
class ReviewerCapacityServiceImpl implements ReviewerCapacityService, ReviewerAssignmentService {
@ -194,6 +197,14 @@ class ReviewerCapacityServiceImpl implements ReviewerCapacityService, ReviewerAs
}
}
@Override
@Transactional
public void unassignReviewer(Project project) {
project.setReviewers(Set.of());
projectService.save(project);
eventBus.post(new ReviewerUnassignedEvent(project));
}
private int countAssignedReviews(User reviewer, LocalDate fromDate) {
if (fromDate.getMonthValue() <= Month.JUNE.getValue()) {
return decisionRepository.countDecisions(reviewer,

@ -25,6 +25,44 @@
<div class="col-12 col-xl-4 col-md-6" wicket:id="reviewers">
<div wicket:id="feedback"></div>
<wicket:enclosure child="current_reviewer">
<h3>Current reviewer</h3>
<div class="card mb-3 bg-info bg-opacity-25" wicket:id="current_reviewer">
<div class="row g-0">
<div class="col-auto">
<img class="img-fluid rounded-start" wicket:id="image">
</div>
<div class="col my-auto">
<div class="card-body">
<h4 class="card-title" wicket:id="name"></h4>
<div>
Research areas:
<ul>
<li wicket:id="research_areas">
<wicket:container wicket:id="research_area"></wicket:container>
</li>
<li wicket:id="none">None specified</li>
</ul>
</div>
<div>
Languages:
<ul class="mb-0">
<li wicket:id="languages">
<wicket:container wicket:id="language"></wicket:container>
</li>
</ul>
</div>
</div>
</div>
<div class="col-auto my-auto">
<div class="card-body">
<button class="btn btn-success" wicket:id="unassign">Un-assign</button>
</div>
</div>
</div>
</div>
</wicket:enclosure>
<wicket:enclosure child="good_candidates">
<h3>Suitable candidates</h3>
<p>

@ -31,6 +31,9 @@ import javax.inject.Inject;
import java.time.Clock;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;
public class AdminAssignReviewerPage extends AbstractAdminProjectPage {
@Inject
@ -88,6 +91,8 @@ public class AdminAssignReviewerPage extends AbstractAdminProjectPage {
reviewerCandidates = LoadableDetachableModel.of(() ->
reviewerAssignmentService.getCandidatesToReview(projectModel.getObject(), LocalDate.now(clock)));
add(new CurrentReviewerCard("current_reviewer", projectModel.map(Project::getReviewer)));
add(new AutoHidingListView<>("good_candidates", reviewerCandidates.map(ReviewerCandidates::good)) {
@Override
protected void populateItem(ListItem<ReviewerCandidates.Candidate> item) {
@ -127,6 +132,46 @@ public class AdminAssignReviewerPage extends AbstractAdminProjectPage {
});
}
private class CurrentReviewerCard extends GenericWebMarkupContainer<User> {
public CurrentReviewerCard(String id, IModel<User> reviewer) {
super(id, reviewer);
add(new UserProfileImage("image", reviewer, UserProfileImage.Size.MEDIUM));
add(new UserLabel("name", reviewer));
add(new AutoHidingListView<>("research_areas", reviewer.map(User::getResearchAreas).map(ArrayList::new)) {
@Override
protected void populateItem(ListItem<ResearchArea> item) {
item.add(new Label("research_area", item.getModel().map(ResearchArea::getTitle)));
}
});
add(new WebMarkupContainer("none") {
@Override
protected void onConfigure() {
super.onConfigure();
setVisible(reviewer.getObject().getResearchAreas().isEmpty());
}
});
add(new AutoHidingListView<>("languages", reviewer.map(User::getLanguages).map(ArrayList::new)) {
@Override
protected void populateItem(ListItem<Language> item) {
item.add(new EnumLabel<>("language", item.getModel()));
}
});
add(new Link<>("unassign", reviewer) {
@Override
public void onClick() {
reviewerAssignmentService.unassignReviewer(AvailableReviewersPanel.this.getModelObject());
AvailableReviewersPanel.this.success(getString("reviewer_unassigned"));
}
});
}
@Override
protected void onConfigure() {
super.onConfigure();
setVisible(getModelObject() != null);
}
}
private class ReviewerCard extends Fragment {
public ReviewerCard(String id, IModel<ReviewerCandidates.Candidate> candidate) {
super(id, "reviewer_details", AvailableReviewersPanel.this, candidate);

@ -1,3 +1,4 @@
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
reviewer_unassigned=Reviewer un-assigned

@ -4,6 +4,7 @@ import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import se.su.dsv.scipro.misc.DaysService;
import se.su.dsv.scipro.project.ReviewerAssignedEvent;
import se.su.dsv.scipro.project.ReviewerUnassignedEvent;
import se.su.dsv.scipro.reviewing.*;
import javax.inject.Inject;
@ -11,7 +12,6 @@ import javax.inject.Singleton;
import java.time.Clock;
import java.time.LocalDate;
import java.util.Date;
import java.util.function.Consumer;
@Singleton
public class ReviewerAssignedDeadline {
@ -73,6 +73,27 @@ public class ReviewerAssignedDeadline {
});
}
@Subscribe
public void reviewerUnassigned(ReviewerUnassignedEvent event) {
roughDraftApprovalService.findBy(event.project())
.filter(rda -> !rda.isDecided())
.map(ReviewerApproval::getCurrentDecision)
.ifPresent(currentDecision -> {
currentDecision.setDeadline(null);
currentDecision.setAssignedReviewer(null);
currentDecision.setReviewerAssignedAt(null);
});
finalSeminarApprovalService.findBy(event.project())
.filter(fsa -> !fsa.isDecided())
.map(ReviewerApproval::getCurrentDecision)
.ifPresent(currentDecision -> {
currentDecision.setDeadline(null);
currentDecision.setAssignedReviewer(null);
currentDecision.setReviewerAssignedAt(null);
});
}
protected Date getDeadline(int days) {
return daysService.workDaysAfter(new Date(), days);
}