3204 Set reviewer targets by calendar year

This commit is contained in:
Andreas Svanberg 2024-01-12 15:13:27 +01:00
parent f2cfb07a29
commit 0d1650f3f3
6 changed files with 122 additions and 77 deletions

@ -3,11 +3,19 @@ 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.time.Year;
import java.util.List;
public interface ReviewerCapacityService {
void assignTarget(User reviewer, DateRange dateRange, int target);
record Target(int spring, int autumn, String note) {}
default void assignTarget(User reviewer, DateRange dateRange, int target) {
Year year = Year.from(dateRange.from());
int spring = dateRange.from().getMonthValue() < 7 ? target : 0;
int autumn = dateRange.from().getMonthValue() > 6 ? target : 0;
assignTarget(reviewer, year, new Target(spring, autumn, ""));
}
void assignTarget(User reviewer, Year year, Target target);
List<Unit> getUnitsWithReviewers();
@ -15,7 +23,12 @@ public interface ReviewerCapacityService {
List<User> getActiveReviewersOnUnit(Unit unit);
int getTarget(User reviewerObject, DateRange dateRange);
default int getTarget(User reviewerObject, DateRange dateRange) {
Year year = Year.from(dateRange.from());
return getTarget(reviewerObject, year).spring();
}
Target getTarget(User reviewer, Year year);
String getNote(User reviewer, DateRange dateRange);

@ -12,6 +12,8 @@ import se.su.dsv.scipro.system.UserService;
import javax.inject.Inject;
import java.time.LocalDate;
import java.time.Month;
import java.time.Year;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
@ -41,20 +43,23 @@ class ReviewerCapacityServiceImpl implements ReviewerCapacityService, ReviewerAs
}
@Override
public void assignTarget(User reviewer, DateRange dateRange, int target) {
Optional<ReviewerTarget> currentTarget = reviewerTargetRepository.getExactReviewTarget(reviewer, dateRange);
if (currentTarget.isPresent()) {
ReviewerTarget targetToUpdate = currentTarget.get();
targetToUpdate.setTarget(target);
public void assignTarget(User reviewer, Year year, Target target) {
Optional<ReviewerTarget> reviewerTarget = reviewerTargetRepository.getReviewerTarget(reviewer, year);
if (reviewerTarget.isPresent()) {
ReviewerTarget targetToUpdate = reviewerTarget.get();
targetToUpdate.setSpring(target.spring());
targetToUpdate.setAutumn(target.autumn());
targetToUpdate.setNote(target.note());
reviewerTargetRepository.save(targetToUpdate);
}
else {
ReviewerTarget reviewerTarget = new ReviewerTarget();
reviewerTarget.setReviewer(reviewer);
reviewerTarget.setFromDate(dateRange.from());
reviewerTarget.setToDate(dateRange.to());
reviewerTarget.setTarget(target);
reviewerTargetRepository.save(reviewerTarget);
ReviewerTarget reviewerTargetToSave = new ReviewerTarget();
reviewerTargetToSave.setReviewer(reviewer);
reviewerTargetToSave.setYear(year.getValue());
reviewerTargetToSave.setSpring(target.spring());
reviewerTargetToSave.setAutumn(target.autumn());
reviewerTargetToSave.setNote(target.note());
reviewerTargetRepository.save(reviewerTargetToSave);
}
}
@ -82,15 +87,15 @@ class ReviewerCapacityServiceImpl implements ReviewerCapacityService, ReviewerAs
}
@Override
public int getTarget(User reviewerObject, DateRange dateRange) {
return getTarget(reviewerObject, dateRange.from())
.map(ReviewerTarget::getTarget)
.orElse(0);
public Target getTarget(User reviewer, Year year) {
return reviewerTargetRepository.getReviewerTarget(reviewer, year)
.map(reviewerTarget -> new Target(reviewerTarget.getSpring(), reviewerTarget.getAutumn(), reviewerTarget.getNote()))
.orElse(new Target(0, 0, ""));
}
@Override
public String getNote(User reviewer, DateRange dateRange) {
return reviewerTargetRepository.getExactReviewTarget(reviewer, dateRange)
return reviewerTargetRepository.getReviewerTarget(reviewer, Year.from(dateRange.from()))
.map(ReviewerTarget::getNote)
.orElse("");
}
@ -98,7 +103,7 @@ class ReviewerCapacityServiceImpl implements ReviewerCapacityService, ReviewerAs
@Override
@Transactional
public void setNote(User reviewer, DateRange dateRange, String note) {
Optional<ReviewerTarget> currentTarget = reviewerTargetRepository.getExactReviewTarget(reviewer, dateRange);
Optional<ReviewerTarget> currentTarget = reviewerTargetRepository.getReviewerTarget(reviewer, Year.from(dateRange.from()));
if (currentTarget.isPresent()) {
ReviewerTarget targetToUpdate = currentTarget.get();
targetToUpdate.setNote(note);
@ -107,17 +112,14 @@ class ReviewerCapacityServiceImpl implements ReviewerCapacityService, ReviewerAs
else {
ReviewerTarget reviewerTarget = new ReviewerTarget();
reviewerTarget.setReviewer(reviewer);
reviewerTarget.setFromDate(dateRange.from());
reviewerTarget.setToDate(dateRange.to());
reviewerTarget.setYear(dateRange.from().getYear());
reviewerTarget.setNote(note);
reviewerTargetRepository.save(reviewerTarget);
}
}
private Optional<ReviewerTarget> getTarget(User reviewer, LocalDate date) {
return reviewerTargetRepository.getReviewerTargets(reviewer, date)
.stream()
.max(Comparator.comparing(ReviewerTarget::getTarget));
return reviewerTargetRepository.getReviewerTarget(reviewer, Year.from(date));
}
@Override
@ -130,8 +132,8 @@ class ReviewerCapacityServiceImpl implements ReviewerCapacityService, ReviewerAs
List<ReviewerCandidates.Candidate> unavailable = new ArrayList<>();
for (User reviewer : reviewers) {
Optional<ReviewerTarget> reviewerPeriodTarget = getTarget(reviewer, date);
int target = reviewerPeriodTarget.map(ReviewerTarget::getTarget).orElse(0);
int assigned = reviewerPeriodTarget.map(this::countAssignedReviews).orElse(0);
int target = reviewerPeriodTarget.map(rt -> getPeriodTarget(rt, date)).orElse(0);
int assigned = countAssignedReviews(reviewer, date);
String note = reviewerPeriodTarget.map(ReviewerTarget::getNote).orElse("");
ReviewerCandidates.Candidate candidate = new ReviewerCandidates.Candidate(reviewer, target, assigned, note);
@ -158,6 +160,10 @@ class ReviewerCapacityServiceImpl implements ReviewerCapacityService, ReviewerAs
return new ReviewerCandidates(good, mismatched, busy, unavailable);
}
private int getPeriodTarget(ReviewerTarget reviewerTarget, LocalDate date) {
return date.getMonthValue() <= Month.JUNE.getValue() ? reviewerTarget.getSpring() : reviewerTarget.getAutumn();
}
@Override
public ReviewerAssignment assignReviewer(Project project, User reviewer) {
if (project.getHeadSupervisor().equals(reviewer)) {
@ -175,11 +181,12 @@ class ReviewerCapacityServiceImpl implements ReviewerCapacityService, ReviewerAs
}
}
private int countAssignedReviews(ReviewerTarget reviewerTarget) {
return countAssignedReviews(reviewerTarget.getReviewer(), reviewerTarget.getFromDate(), reviewerTarget.getToDate());
}
private int countAssignedReviews(User reviewer, LocalDate fromDate, LocalDate toDate) {
return decisionRepository.countDecisions(reviewer, fromDate, toDate);
private int countAssignedReviews(User reviewer, LocalDate fromDate) {
if (fromDate.getMonthValue() <= Month.JUNE.getValue()) {
return decisionRepository.countDecisions(reviewer, LocalDate.of(fromDate.getYear(), Month.JANUARY, 1), LocalDate.of(fromDate.getYear(), Month.JUNE, 30));
}
else {
return decisionRepository.countDecisions(reviewer, LocalDate.of(fromDate.getYear(), Month.JULY, 1), LocalDate.of(fromDate.getYear(), Month.DECEMBER, 31));
}
}
}

@ -24,14 +24,14 @@ public class ReviewerTarget extends DomainObject {
@JoinColumn(name = "reviewer_id", nullable = false)
private User reviewer;
@Column(name = "target", nullable = false)
private int target;
@Column(name = "year", nullable = false)
private int year;
@Column(name = "from_date", nullable = false)
private java.time.LocalDate fromDate;
@Column(name = "spring", nullable = false)
private int spring;
@Column(name = "to_date", nullable = false)
private java.time.LocalDate toDate;
@Column(name = "autumn", nullable = false)
private int autumn;
@Column(name = "note")
private String note;
@ -53,28 +53,28 @@ public class ReviewerTarget extends DomainObject {
this.reviewer = reviewer;
}
public int getTarget() {
return target;
public int getYear() {
return year;
}
public void setTarget(int target) {
this.target = target;
public void setYear(int year) {
this.year = year;
}
public LocalDate getFromDate() {
return fromDate;
public int getSpring() {
return spring;
}
public void setFromDate(LocalDate fromDate) {
this.fromDate = fromDate;
public void setSpring(int spring) {
this.spring = spring;
}
public LocalDate getToDate() {
return toDate;
public int getAutumn() {
return autumn;
}
public void setToDate(LocalDate toDate) {
this.toDate = toDate;
public void setAutumn(int autumn) {
this.autumn = autumn;
}
public String getNote() {
@ -87,22 +87,29 @@ public class ReviewerTarget extends DomainObject {
@Override
public String toString() {
return "ReviewerTarget{" + "id=" + id + ", reviewer=" + reviewer + ", target=" + target + ", fromDate=" + fromDate + ", toDate=" + toDate + ", note=" + note + '}';
return "ReviewerTarget{" +
"id=" + id +
", reviewer=" + reviewer +
", year=" + year +
", spring=" + spring +
", autumn=" + autumn +
", note='" + note + '\'' +
'}';
}
@Override
public boolean equals(Object o) {
return o instanceof ReviewerTarget that
&& target == that.target
&& year == that.year
&& spring == that.spring
&& autumn == that.autumn
&& Objects.equals(id, that.id)
&& Objects.equals(reviewer, that.reviewer)
&& Objects.equals(fromDate, that.fromDate)
&& Objects.equals(toDate, that.toDate)
&& Objects.equals(note, that.note);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), id, reviewer, target, fromDate, toDate, note);
return Objects.hash(super.hashCode(), id, reviewer, year, spring, autumn, note);
}
}

@ -2,14 +2,11 @@ package se.su.dsv.scipro.reviewing;
import se.su.dsv.scipro.system.User;
import java.time.LocalDate;
import java.util.List;
import java.time.Year;
import java.util.Optional;
public interface ReviewerTargetRepository {
void save(ReviewerTarget reviewerTarget);
List<ReviewerTarget> getReviewerTargets(User reviewer, LocalDate date);
Optional<ReviewerTarget> getExactReviewTarget(User reviewer, DateRange dateRange);
Optional<ReviewerTarget> getReviewerTarget(User reviewer, Year year);
}

@ -7,8 +7,7 @@ import se.su.dsv.scipro.system.User;
import javax.inject.Inject;
import javax.inject.Provider;
import java.time.LocalDate;
import java.util.List;
import java.time.Year;
import java.util.Optional;
public class ReviewerTargetRepositoryImpl extends AbstractRepository implements ReviewerTargetRepository {
@ -29,22 +28,12 @@ public class ReviewerTargetRepositoryImpl extends AbstractRepository implements
}
@Override
public List<ReviewerTarget> getReviewerTargets(User reviewer, LocalDate date) {
return from(QReviewerTarget.reviewerTarget)
.where(QReviewerTarget.reviewerTarget.reviewer.eq(reviewer)
.and(QReviewerTarget.reviewerTarget.fromDate.loe(date))
.and(QReviewerTarget.reviewerTarget.toDate.goe(date)))
.fetch();
}
@Override
public Optional<ReviewerTarget> getExactReviewTarget(User reviewer, DateRange dateRange) {
public Optional<ReviewerTarget> getReviewerTarget(User reviewer, Year year) {
ReviewerTarget current =
from(QReviewerTarget.reviewerTarget)
.where(QReviewerTarget.reviewerTarget.reviewer.eq(reviewer)
.and(QReviewerTarget.reviewerTarget.fromDate.eq(dateRange.from()))
.and(QReviewerTarget.reviewerTarget.toDate.eq(dateRange.to())))
.fetchOne();
.where(QReviewerTarget.reviewerTarget.reviewer.eq(reviewer))
.where(QReviewerTarget.reviewerTarget.year.eq(year.getValue()))
.fetchOne();
return Optional.ofNullable(current);
}
}

@ -0,0 +1,32 @@
CREATE TABLE `reviewer_target_migration`
(
`id` BIGINT NOT NULL AUTO_INCREMENT,
`dateCreated` DATETIME NOT NULL,
`lastModified` DATETIME NOT NULL,
`version` INT NOT NULL,
`reviewer_id` BIGINT NOT NULL,
`year` INT NOT NULL,
`spring` INT NOT NULL,
`autumn` INT NOT NULL,
`note` TEXT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `UK_ReviewerTarget_ReviewerId_Year` (`reviewer_id`, `year`),
CONSTRAINT `FK_ReviewerTarget_ReviewerId` FOREIGN KEY (`reviewer_id`) REFERENCES `user` (`id`)
);
INSERT INTO `reviewer_target_migration`
(`dateCreated`, `lastModified`, `version`, `reviewer_id`, `year`, `spring`, `autumn`, `note`)
SELECT MIN(`dateCreated`), MAX(`lastModified`), MAX(`version`), `reviewer_id`, YEAR(`from_date`), 0, 0, NULL
FROM `reviewer_target`
GROUP BY `reviewer_id`, YEAR(`from_date`);
UPDATE `reviewer_target_migration`
SET `spring` = COALESCE((SELECT SUM(`target`) FROM `reviewer_target` WHERE `reviewer_target`.`reviewer_id` = `reviewer_target_migration`.`reviewer_id` AND `reviewer_target`.`from_date` >= CONCAT(`year`, '-01-01') AND `reviewer_target`.`from_date` < CONCAT(`year`, '-07-01')), 0),
`autumn` = COALESCE((SELECT SUM(`target`) FROM `reviewer_target` WHERE `reviewer_target`.`reviewer_id` = `reviewer_target_migration`.`reviewer_id` AND `reviewer_target`.`from_date` >= CONCAT(`year`, '-07-01') AND `reviewer_target`.`from_date` < CONCAT(`year` + 1, '-01-01')), 0);
UPDATE `reviewer_target_migration`
SET note = (SELECT GROUP_CONCAT(`note`) FROM `reviewer_target` WHERE `reviewer_target`.`reviewer_id` = `reviewer_target_migration`.`reviewer_id` AND YEAR(`from_date`) = `year` AND `note` IS NOT NULL)
WHERE `note` IS NULL;
DROP TABLE `reviewer_target`;
RENAME TABLE `reviewer_target_migration` TO `reviewer_target`;