3204 Start of managing reviewer capacity
This commit is contained in:
parent
7f999022c8
commit
a123fd1932
core/src/main/java/se/su/dsv/scipro/reviewing
view/src/main
@ -1,9 +1,17 @@
|
||||
package se.su.dsv.scipro.reviewing;
|
||||
|
||||
import se.su.dsv.scipro.system.Unit;
|
||||
import se.su.dsv.scipro.system.User;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
public interface ReviewerCapacityService {
|
||||
void assignTarget(User reviewer, DateRange dateRange, int target);
|
||||
|
||||
List<Unit> getUnitsWithReviewers();
|
||||
|
||||
List<User> getAllActiveReviewers();
|
||||
|
||||
List<User> getActiveReviewersOnUnit(Unit unit);
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package se.su.dsv.scipro.reviewing;
|
||||
|
||||
import se.su.dsv.scipro.project.Project;
|
||||
import se.su.dsv.scipro.system.Unit;
|
||||
import se.su.dsv.scipro.system.User;
|
||||
import se.su.dsv.scipro.system.UserService;
|
||||
|
||||
@ -9,7 +10,9 @@ import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
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;
|
||||
@ -37,6 +40,29 @@ class ReviewerCapacityServiceImpl implements ReviewerCapacityService, ReviewerAs
|
||||
reviewerTargetRepository.save(reviewerTarget);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Unit> getUnitsWithReviewers() {
|
||||
return userService.findActiveReviewers()
|
||||
.stream()
|
||||
.map(User::getUnit)
|
||||
.distinct()
|
||||
.sorted(Comparator.comparing(Unit::getTitle))
|
||||
.toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<User> getAllActiveReviewers() {
|
||||
return userService.findActiveReviewers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<User> getActiveReviewersOnUnit(Unit unit) {
|
||||
return userService.findActiveReviewers()
|
||||
.stream()
|
||||
.filter(reviewer -> Objects.equals(reviewer.getUnit(), unit))
|
||||
.toList();
|
||||
}
|
||||
|
||||
private Optional<ReviewerTarget> getTarget(User reviewer, LocalDate date) {
|
||||
return reviewerTargetRepository.getReviewerTargets(reviewer, date)
|
||||
.stream()
|
||||
|
@ -274,6 +274,7 @@ public class SciProApplication extends LifecycleManagedWebApplication {
|
||||
mountPage("admin/project/create", AdminCreateProjectPage.class);
|
||||
mountPage("admin/project/survey", AdminSurveyPage.class);
|
||||
mountPage("admin/project/reviewer", AdminAssignReviewerPage.class);
|
||||
mountPage("admin/project/reviewer/capacity", AdminReviewerCapacityManagementPage.class);
|
||||
mountPage("admin/edit", AdminEditProjectPage.class);
|
||||
mountPage("admin/finalseminars", AdminFinalSeminarPage.class);
|
||||
mountPage("admin/finalseminars/exemptions", AdminFinalSeminarExemptionPage.class);
|
||||
|
@ -27,7 +27,8 @@ public class AbstractAdminProjectPage extends AbstractAdminPage {
|
||||
new MenuItem("Activity plan templates", AdminActivityPlanTemplatesPage.class, MenuHighlightAdminActivityPlanTemplates.class),
|
||||
new MenuItem("Checklist templates", AdminChecklistPage.class, MenuHighlightAdminChecklist.class),
|
||||
new MenuItem("Non work days", NonWorkDaysPage.class),
|
||||
new MenuItem("Survey", AdminSurveyPage.class)
|
||||
new MenuItem("Survey", AdminSurveyPage.class),
|
||||
new MenuItem("Reviewer targets", AdminReviewerCapacityManagementPage.class)
|
||||
);
|
||||
}
|
||||
|
||||
|
80
view/src/main/java/se/su/dsv/scipro/admin/pages/AdminReviewerCapacityManagementPage.html
Normal file
80
view/src/main/java/se/su/dsv/scipro/admin/pages/AdminReviewerCapacityManagementPage.html
Normal file
@ -0,0 +1,80 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
|
||||
<body>
|
||||
<wicket:extend>
|
||||
<h2>Reviewer targets</h2>
|
||||
<div class="row">
|
||||
<div class="col-12 col-xl-4">
|
||||
<p>
|
||||
On this page you can set the number of reviews (target) that each reviewer should do in any
|
||||
given period. The number is not a strict cap but acts as a very strong hint to the
|
||||
administrators during the assignment process.
|
||||
</p>
|
||||
<p>
|
||||
You can use the below "default period" to quickly set the number of reviews for all
|
||||
reviewers within a single period. If you want to set the number of reviews for a single
|
||||
reviewer, you can expand the reviewer by clicking on their box and add individual targets
|
||||
for each period.
|
||||
</p>
|
||||
<p>
|
||||
You also have the ability to mark reviewers as completely unavailable for a given period.
|
||||
It can be useful if a reviewer is on vacation, sick, busy with course work, or otherwise
|
||||
unavailable for a period. This is intended to prevent the reviewer from being assigned and
|
||||
holding up the thesis process since they are unable to perform the review.
|
||||
</p>
|
||||
|
||||
<h3>Filter</h3>
|
||||
<form>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Unit</label>
|
||||
<select class="form-select" wicket:id="units">
|
||||
<option>All</option>
|
||||
<optgroup label="Units">
|
||||
<option>ACT</option>
|
||||
<option>IDEAL</option>
|
||||
<option>IS</option>
|
||||
<option>SAS</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
<small class="form-text text-muted">Only show reviewers on the selected unit</small>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<h3>Default period</h3>
|
||||
<div class="mb-3 row">
|
||||
<div class="col">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" wicket:id="default_from_date">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto align-self-center">
|
||||
—
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" wicket:id="default_to_date">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-xl-6" wicket:id="reviewers_container">
|
||||
<div class="card mb-3" wicket:id="reviewers">
|
||||
<div class="row g-0">
|
||||
<div class="col-auto">
|
||||
<img class="img-fluid rounded-start profile-picture-md" wicket:id="profile_image">
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="card-body">
|
||||
<h4 class="card-title" wicket:id="name">[John Doe]</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto w-64-px align-self-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22"><defs><clipPath><path fill="#00f" fill-opacity=".514" d="m-7 1024.36h34v34h-34z"/></clipPath><clipPath><path fill="#aade87" fill-opacity=".472" d="m-6 1028.36h32v32h-32z"/></clipPath></defs><path d="m345.44 248.29l-194.29 194.28c-12.359 12.365-32.397 12.365-44.75 0-12.354-12.354-12.354-32.391 0-44.744l171.91-171.91-171.91-171.9c-12.354-12.359-12.354-32.394 0-44.748 12.354-12.359 32.391-12.359 44.75 0l194.29 194.28c6.177 6.18 9.262 14.271 9.262 22.366 0 8.099-3.091 16.196-9.267 22.373" transform="matrix(.03541-.00013.00013.03541 2.98 3.02)" fill="#4d4d4d"/></svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</wicket:extend>
|
||||
</body>
|
||||
</html>
|
86
view/src/main/java/se/su/dsv/scipro/admin/pages/AdminReviewerCapacityManagementPage.java
Normal file
86
view/src/main/java/se/su/dsv/scipro/admin/pages/AdminReviewerCapacityManagementPage.java
Normal file
@ -0,0 +1,86 @@
|
||||
package se.su.dsv.scipro.admin.pages;
|
||||
|
||||
import org.apache.wicket.ajax.AjaxRequestTarget;
|
||||
import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
|
||||
import org.apache.wicket.markup.html.WebMarkupContainer;
|
||||
import org.apache.wicket.markup.html.form.DropDownChoice;
|
||||
import org.apache.wicket.markup.html.form.FormComponent;
|
||||
import org.apache.wicket.markup.html.form.LambdaChoiceRenderer;
|
||||
import org.apache.wicket.markup.html.form.TextField;
|
||||
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.LoadableDetachableModel;
|
||||
import org.apache.wicket.model.Model;
|
||||
import se.su.dsv.scipro.components.BootstrapDatePicker;
|
||||
import se.su.dsv.scipro.data.DetachableServiceModel;
|
||||
import se.su.dsv.scipro.profile.UserLabel;
|
||||
import se.su.dsv.scipro.profile.UserProfileImage;
|
||||
import se.su.dsv.scipro.reviewing.ReviewerCapacityService;
|
||||
import se.su.dsv.scipro.springdata.services.UnitService;
|
||||
import se.su.dsv.scipro.system.Unit;
|
||||
import se.su.dsv.scipro.system.User;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
public class AdminReviewerCapacityManagementPage extends AbstractAdminProjectPage {
|
||||
@Inject
|
||||
ReviewerCapacityService reviewerCapacityService;
|
||||
@Inject
|
||||
UnitService unitService;
|
||||
|
||||
private final WebMarkupContainer reviewerList;
|
||||
|
||||
private IModel<Unit> selectedUnit;
|
||||
private IModel<LocalDate> fromDate = new Model<>();
|
||||
private IModel<LocalDate> toDate = new Model<>();
|
||||
|
||||
public AdminReviewerCapacityManagementPage() {
|
||||
IModel<List<Unit>> units = LoadableDetachableModel.of(() ->
|
||||
reviewerCapacityService.getUnitsWithReviewers());
|
||||
selectedUnit = new DetachableServiceModel<>(unitService);
|
||||
|
||||
DropDownChoice<Unit> unitDropDownChoice = new DropDownChoice<>(
|
||||
"units",
|
||||
selectedUnit,
|
||||
units,
|
||||
new LambdaChoiceRenderer<>(Unit::getTitle, Unit::getId));
|
||||
unitDropDownChoice.setNullValid(true);
|
||||
unitDropDownChoice.add(new AjaxFormComponentUpdatingBehavior("change") {
|
||||
@Override
|
||||
protected void onUpdate(AjaxRequestTarget target) {
|
||||
target.add(reviewerList);
|
||||
}
|
||||
});
|
||||
add(unitDropDownChoice);
|
||||
|
||||
FormComponent<LocalDate> fromDateField = new TextField<>("default_from_date", fromDate, LocalDate.class);
|
||||
fromDateField.add(new BootstrapDatePicker());
|
||||
add(fromDateField);
|
||||
|
||||
FormComponent<LocalDate> toDateField = new TextField<>("default_to_date", toDate, LocalDate.class);
|
||||
toDateField.add(new BootstrapDatePicker());
|
||||
add(toDateField);
|
||||
|
||||
IModel<List<User>> reviewers = LoadableDetachableModel.of(() -> {
|
||||
if (selectedUnit.getObject() == null) {
|
||||
return reviewerCapacityService.getAllActiveReviewers();
|
||||
}
|
||||
else {
|
||||
return reviewerCapacityService.getActiveReviewersOnUnit(selectedUnit.getObject());
|
||||
}
|
||||
});
|
||||
reviewerList = new WebMarkupContainer("reviewers_container");
|
||||
reviewerList.add(new ListView<>("reviewers", reviewers) {
|
||||
@Override
|
||||
protected void populateItem(ListItem<User> item) {
|
||||
item.add(new UserProfileImage("profile_image", item.getModel(), UserProfileImage.Size.MEDIUM));
|
||||
item.add(new UserLabel("name", item.getModel()));
|
||||
}
|
||||
});
|
||||
reviewerList.setOutputMarkupId(true);
|
||||
add(reviewerList);
|
||||
}
|
||||
}
|
1
view/src/main/java/se/su/dsv/scipro/admin/pages/AdminReviewerCapacityManagementPage.utf8.properties
Normal file
1
view/src/main/java/se/su/dsv/scipro/admin/pages/AdminReviewerCapacityManagementPage.utf8.properties
Normal file
@ -0,0 +1 @@
|
||||
units.nullValid=All units
|
@ -538,6 +538,9 @@ th.wicket_orderUp, th.sorting_asc {
|
||||
.w-200-p {
|
||||
width: 200px;
|
||||
}
|
||||
.w-64-px {
|
||||
width: 64px;
|
||||
}
|
||||
|
||||
.read-state {
|
||||
text-shadow: -1px 0 black, 0 1px black, 1px 0 black, 0 -1px black;
|
||||
|
Loading…
x
Reference in New Issue
Block a user