Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
2da13d9
Fixed issues with modification of planning and approval mode review p…
ocielliottc Sep 20, 2024
cdeab10
#2550 - Implemented an "open" mode for TeamReviews so that open revie…
ocielliottc Sep 23, 2024
da0cb76
#2550 - Added the ability for a manager to see the submitted reviews …
ocielliottc Sep 23, 2024
d505208
Create feedback requests for review periods moving into the Open state.
ocielliottc Sep 24, 2024
a0fde6a
Added a test for verifying feedback request when changing review peri…
ocielliottc Sep 24, 2024
ca2939b
Merge branch 'develop' into feature-2550/rewire-review-page
ocielliottc Sep 24, 2024
c7424eb
Create self-review requests based on the reviewee of each review assi…
ocielliottc Sep 24, 2024
832b343
Modified findReviewRequestsByPeriod to get all review requests associ…
ocielliottc Sep 25, 2024
ec44af5
Only display alert when not reviewing.
ocielliottc Sep 25, 2024
528b373
Put the self-review tab back in.
ocielliottc Sep 25, 2024
74df01b
Display self-review status (for the manager) and corrected display of…
ocielliottc Sep 25, 2024
f862cf0
Added the ability to just review self-reviews.
ocielliottc Sep 25, 2024
2230cc5
#2550 - Ensure current user is shown in reviewer list so that links c…
ocielliottc Sep 25, 2024
f31a059
Merge branch 'develop' into feature-2550/rewire-review-page
ocielliottc Sep 26, 2024
b6ee191
Ensure that all dates for the review period are valid before creating…
ocielliottc Sep 26, 2024
b2cbf5c
Create sane review period dates for tests.
ocielliottc Sep 27, 2024
ebdc1ea
Added tests for review period time validation.
ocielliottc Sep 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,17 @@
import com.objectcomputing.checkins.notifications.email.EmailSender;
import com.objectcomputing.checkins.notifications.email.MailJetFactory;
import com.objectcomputing.checkins.services.feedback_request.FeedbackRequestServices;
import com.objectcomputing.checkins.services.feedback_request.FeedbackRequest;
import com.objectcomputing.checkins.services.memberprofile.MemberProfileRepository;
import com.objectcomputing.checkins.services.memberprofile.MemberProfile;
import io.micronaut.context.env.Environment;
import jakarta.inject.Named;
import jakarta.inject.Singleton;
import jakarta.validation.constraints.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
Expand Down Expand Up @@ -69,6 +73,7 @@ public ReviewPeriod save(ReviewPeriod reviewPeriod) {
throw new AlreadyExistsException(String.format("Review Period \"%s\" already exists.", reviewPeriod.getName()));
}

validateDates(reviewPeriod);
newPeriod = reviewPeriodRepository.save(reviewPeriod);
}
return newPeriod;
Expand Down Expand Up @@ -126,11 +131,75 @@ public ReviewPeriod update(@NotNull ReviewPeriod reviewPeriod) {
throw new BadArgException(String.format("Invalid status transition from %s to %s", currentStatus, newStatus));
}

if (newStatus == ReviewStatus.OPEN) {
// Ensure that the launch date is in the future.
LocalDateTime launchDate = reviewPeriod.getLaunchDate();
if (launchDate == null) {
throw new BadArgException("Cannot open a review period without a launch date.");
}
if (launchDate.isBefore(LocalDateTime.now())) {
throw new BadArgException("Cannot open a review period with a launch date in the past.");
}
}

validateDates(reviewPeriod);

if (newStatus == ReviewStatus.AWAITING_APPROVAL) {
notifyRevieweeSupervisorsByReviewPeriod(reviewPeriod.getId(), reviewPeriod.getName());
}

return reviewPeriodRepository.update(reviewPeriod);
// Update the review period and get the updated period.
ReviewPeriod period = reviewPeriodRepository.update(reviewPeriod);

if (period.getReviewStatus() == ReviewStatus.OPEN) {
// If the review period has been updated and is now open, we need
// to create feedback requests for all involved in the review period
Set<ReviewAssignment> assignments =
reviewAssignmentRepository.findByReviewPeriodId(period.getId());
UUID reviewTemplateId = period.getReviewTemplateId();
UUID selfReviewTemplateId = period.getSelfReviewTemplateId();
LocalDate closeDate = period.getCloseDate().toLocalDate();
LocalDate selfReviewCloseDate =
period.getSelfReviewCloseDate().toLocalDate();

// Log template id's that were not provided to the review period.
// This is the reason a feedback request will not be created.
if (reviewTemplateId == null) {
LOG.warn("Review Period: " + period.getId().toString() +
" does not have a review template.");
}
if (selfReviewTemplateId == null) {
LOG.warn("Review Period: " + period.getId().toString() +
" does not have a self-review template.");
}

Set<UUID> selfRevieweeIds = new HashSet<>();
for (ReviewAssignment assignment : assignments) {
// This person is being reviewed and will need a self-review
// request.
selfRevieweeIds.add(assignment.getRevieweeId());

// Create the review feedback request.
if (reviewTemplateId != null) {
createReviewRequest(
period, findCreatorId(assignment.getReviewerId()),
assignment.getRevieweeId(), assignment.getReviewerId(),
reviewTemplateId, closeDate);
}
}

if (selfReviewTemplateId != null) {
for(UUID memberId : selfRevieweeIds) {
// Create the self-review feedback request.
createReviewRequest(period, findCreatorId(memberId),
memberId, memberId,
selfReviewTemplateId,
selfReviewCloseDate);
}
}
}

return period;
}

private void notifyRevieweeSupervisorsByReviewPeriod(UUID reviewPeriodId, String reviewPeriodName) {
Expand Down Expand Up @@ -158,4 +227,61 @@ private String constructEmailContent (UUID reviewPeriodId, String reviewPeriodNa
<a href="%s/feedback/reviews?period=%s">Click here</a> to review and approve reviewer assignments in the Check-Ins app.""".formatted(reviewPeriodName, webAddress, reviewPeriodId);
}

private void validateDates(ReviewPeriod period) {
// Check the self-review close date.
LocalDateTime launchDate = period.getLaunchDate();
LocalDateTime selfReviewCloseDate = period.getSelfReviewCloseDate();
if (launchDate != null && selfReviewCloseDate != null &&
!selfReviewCloseDate.isAfter(launchDate)) {
throw new BadArgException("The review period self-review close date must be after the launch date.");
}

// Check the close date.
LocalDateTime closeDate = period.getCloseDate();
if (closeDate != null && selfReviewCloseDate != null &&
!closeDate.isAfter(selfReviewCloseDate)) {
throw new BadArgException("The review period close date must be after the self-review close date.");
}

// Check the period start date.
LocalDateTime startDate = period.getPeriodStartDate();
if (startDate != null && launchDate != null &&
!startDate.isBefore(launchDate)) {
throw new BadArgException("The review period start date must be before the launch date.");
}

// Check the period end date.
LocalDateTime endDate = period.getPeriodEndDate();
if (endDate != null && startDate != null &&
!endDate.isAfter(startDate)) {
throw new BadArgException("The review period end date must be after the start date.");
}
if (endDate != null && closeDate != null &&
endDate.isAfter(closeDate)) {
throw new BadArgException("The review period end date must be on or before the close date.");
}
}

private UUID findCreatorId(UUID memberId) {
List<MemberProfile> profile =
memberProfileRepository.findSupervisorsForId(memberId);
return profile.isEmpty() ? memberId : profile.get(0).getId();
}

private void createReviewRequest(ReviewPeriod period,
UUID creatorId,
UUID revieweeId,
UUID reviewerId,
UUID templateId,
LocalDate dueDate) {
try {
LocalDate sendDate = LocalDate.now();
FeedbackRequest request = new FeedbackRequest(
creatorId, revieweeId, reviewerId, templateId, sendDate,
dueDate, "sent", null, period.getId());
feedbackRequestServices.save(request);
} catch(Exception ex) {
LOG.error(ex.toString());
}
}
}
20 changes: 20 additions & 0 deletions server/src/main/resources/db/dev/R__Load_testing_data.sql
Original file line number Diff line number Diff line change
Expand Up @@ -1310,6 +1310,26 @@ INSERT INTO feedback_requests
VALUES
('98390c09-7121-110a-bfee-9380a470a7f3', '72655c4f-1fb8-4514-b31e-7f7e19fa9bd7', 'c7406157-a38f-4d48-aaed-04018d846727', 'dfe2f986-fac0-11eb-9a03-0242ac130003', 'd1e94b60-47c4-4945-87d1-4dc88f088e57', '2024-09-04', '2024-09-30', '2024-09-05', 'sent', '12345678-e29c-4cf4-9ea4-6baa09405c57');

INSERT INTO feedback_requests
(id, creator_id, requestee_id, recipient_id, template_id, send_date, due_date, submit_date, status, review_period_id)
VALUES
('98390c09-7121-110a-bfee-9380a470a7f4', '72655c4f-1fb8-4514-b31e-7f7e19fa9bd7', 'dfe2f986-fac0-11eb-9a03-0242ac130003', '6207b3fd-042d-49aa-9e28-dcc04f537c2d', 'd1e94b60-47c4-4945-87d1-4dc88f088e57', '2024-09-04', '2024-09-30', null, 'sent', '12345678-e29c-4cf4-9ea4-6baa09405c57');

INSERT INTO feedback_requests
(id, creator_id, requestee_id, recipient_id, template_id, send_date, due_date, submit_date, status, review_period_id)
VALUES
('98390c09-7121-110a-bfee-9380a470a7f5', '72655c4f-1fb8-4514-b31e-7f7e19fa9bd7', '2dee821c-de32-4d9c-9ecb-f73e5903d17a', '6207b3fd-042d-49aa-9e28-dcc04f537c2d', 'd1e94b60-47c4-4945-87d1-4dc88f088e57', '2024-09-04', '2024-09-30', null, 'sent', '12345678-e29c-4cf4-9ea4-6baa09405c57');

INSERT INTO feedback_requests
(id, creator_id, requestee_id, recipient_id, template_id, send_date, due_date, submit_date, status, review_period_id)
VALUES
('98390c09-7121-110a-bfee-9380a470a7f6', '72655c4f-1fb8-4514-b31e-7f7e19fa9bd7', '105f2968-a182-45a3-892c-eeff76383fe0', 'dfe2f986-fac0-11eb-9a03-0242ac130003', 'd1e94b60-47c4-4945-87d1-4dc88f088e57', '2024-09-04', '2024-09-30', null, 'sent', '12345678-e29c-4cf4-9ea4-6baa09405c57');

INSERT INTO feedback_requests
(id, creator_id, requestee_id, recipient_id, template_id, send_date, due_date, submit_date, status, review_period_id)
VALUES
('98390c09-7121-110a-bfee-9380a470a7f7', '72655c4f-1fb8-4514-b31e-7f7e19fa9bd7', '5b90beb2-0e96-438b-bfd6-1487a89b339b', '72655c4f-1fb8-4514-b31e-7f7e19fa9bd7', 'd1e94b60-47c4-4945-87d1-4dc88f088e57', '2024-09-04', '2024-09-30', null, 'sent', '12345678-e29c-4cf4-9ea4-6baa09405c57');

-- CAE - Feedback request, Creator: Big Boss
INSERT INTO feedback_requests
(id, creator_id, requestee_id, recipient_id, template_id, send_date, due_date, submit_date, status, review_period_id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import java.time.LocalDate;

import java.util.UUID;
import java.util.List;

public interface FeedbackRequestFixture extends RepositoryFixture, FeedbackTemplateFixture {

Expand Down Expand Up @@ -111,4 +112,9 @@ default FeedbackRequest saveFeedbackRequest(MemberProfile creator, MemberProfile
feedbackRequest.setSendDate(sendDate);
return getFeedbackRequestRepository().save(feedbackRequest);
}

default List<FeedbackRequest> getFeedbackRequests(MemberProfile recipient) {
return getFeedbackRequestRepository()
.findByValues(null, null, recipient.getId().toString(), null, null, null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,47 +3,75 @@
import com.objectcomputing.checkins.services.reviews.ReviewPeriod;
import com.objectcomputing.checkins.services.reviews.ReviewStatus;

import java.util.UUID;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;

public interface ReviewPeriodFixture extends RepositoryFixture {

default ReviewPeriod createADefaultReviewPeriod() {
return getReviewPeriodRepository().save(new ReviewPeriod("Period of Time", ReviewStatus.OPEN, null, null,
LocalDateTime.now().truncatedTo(ChronoUnit.MILLIS), LocalDateTime.now().plusDays(1).truncatedTo(ChronoUnit.MILLIS),
LocalDateTime.now().plusDays(2).truncatedTo(ChronoUnit.MILLIS)
, LocalDateTime.now().minusDays(30).truncatedTo(ChronoUnit.MILLIS), LocalDateTime.now().minusDays(1).truncatedTo(ChronoUnit.MILLIS)));
LocalDateTime launchDate = LocalDateTime.now().plusMinutes(1)
.truncatedTo(ChronoUnit.MILLIS);
LocalDateTime selfReviewCloseDate = launchDate.plusDays(1);
LocalDateTime closeDate = selfReviewCloseDate.plusDays(1);
LocalDateTime startDate = launchDate.minusDays(30);
LocalDateTime endDate = closeDate.minusDays(1);
return getReviewPeriodRepository().save(
new ReviewPeriod("Period of Time", ReviewStatus.OPEN, null, null,
launchDate, selfReviewCloseDate, closeDate,
startDate, endDate));
}

default ReviewPeriod createADefaultReviewPeriod(ReviewStatus reviewStatus) {
return getReviewPeriodRepository().save(new ReviewPeriod("Period of Time", reviewStatus, null, null,
LocalDateTime.now().truncatedTo(ChronoUnit.MILLIS), LocalDateTime.now().plusDays(1).truncatedTo(ChronoUnit.MILLIS),
LocalDateTime.now().plusDays(2).truncatedTo(ChronoUnit.MILLIS)
, LocalDateTime.now().minusDays(30).truncatedTo(ChronoUnit.MILLIS), LocalDateTime.now().minusDays(1).truncatedTo(ChronoUnit.MILLIS)));
return createADefaultReviewPeriod(reviewStatus, null);
}

default ReviewPeriod createADefaultReviewPeriod(ReviewStatus reviewStatus, UUID templateId) {
LocalDateTime launchDate = LocalDateTime.now().plusMinutes(1)
.truncatedTo(ChronoUnit.MILLIS);
LocalDateTime selfReviewCloseDate = launchDate.plusDays(1);
LocalDateTime closeDate = selfReviewCloseDate.plusDays(1);
LocalDateTime startDate = launchDate.minusDays(30);
LocalDateTime endDate = closeDate.minusDays(1);
return getReviewPeriodRepository().save(
new ReviewPeriod("Period of Time", reviewStatus, templateId, null,
launchDate, selfReviewCloseDate, closeDate,
startDate, endDate));
}

default ReviewPeriod createASecondaryReviewPeriod() {
return getReviewPeriodRepository().save(new ReviewPeriod("Period of Play", ReviewStatus.OPEN, null, null,
LocalDateTime.now().truncatedTo(ChronoUnit.MILLIS), LocalDateTime.now().plusDays(1).truncatedTo(ChronoUnit.MILLIS),
LocalDateTime.now().plusDays(2).truncatedTo(ChronoUnit.MILLIS)
, LocalDateTime.now().minusDays(30).truncatedTo(ChronoUnit.MILLIS), LocalDateTime.now().minusDays(1).truncatedTo(ChronoUnit.MILLIS)));
LocalDateTime launchDate = LocalDateTime.now().plusMinutes(1)
.truncatedTo(ChronoUnit.MILLIS);
LocalDateTime selfReviewCloseDate = launchDate.plusDays(1);
LocalDateTime closeDate = selfReviewCloseDate.plusDays(1);
LocalDateTime startDate = launchDate.minusDays(30);
LocalDateTime endDate = closeDate.minusDays(1);
return getReviewPeriodRepository().save(
new ReviewPeriod("Period of Play", ReviewStatus.OPEN, null, null,
launchDate, selfReviewCloseDate, closeDate,
startDate, endDate));
}

default ReviewPeriod createAClosedReviewPeriod() {
return getReviewPeriodRepository().save(new ReviewPeriod("Period of Closure", ReviewStatus.CLOSED, null, null,
LocalDateTime.now().truncatedTo(ChronoUnit.MILLIS), LocalDateTime.now().plusDays(1).truncatedTo(ChronoUnit.MILLIS),
LocalDateTime.now().plusDays(2).truncatedTo(ChronoUnit.MILLIS)
, LocalDateTime.now().minusDays(30).truncatedTo(ChronoUnit.MILLIS), LocalDateTime.now().minusDays(1).truncatedTo(ChronoUnit.MILLIS)));
LocalDateTime launchDate = LocalDateTime.now().plusMinutes(1)
.truncatedTo(ChronoUnit.MILLIS);
LocalDateTime selfReviewCloseDate = launchDate.plusDays(1);
LocalDateTime closeDate = selfReviewCloseDate.plusDays(1);
LocalDateTime startDate = launchDate.minusDays(30);
LocalDateTime endDate = closeDate.minusDays(1);
return createAClosedReviewPeriod(startDate, endDate);
}

default ReviewPeriod createAClosedReviewPeriod(
LocalDateTime periodStart, LocalDateTime periodEnd) {
LocalDateTime launchDate = LocalDateTime.now().plusMinutes(1)
.truncatedTo(ChronoUnit.MILLIS);
LocalDateTime selfReviewCloseDate = launchDate.plusDays(1);
LocalDateTime closeDate = selfReviewCloseDate.plusDays(1);
return getReviewPeriodRepository().save(
new ReviewPeriod(
"Period of Closure", ReviewStatus.CLOSED, null, null,
LocalDateTime.now().truncatedTo(ChronoUnit.MILLIS),
LocalDateTime.now().plusDays(1).truncatedTo(ChronoUnit.MILLIS),
LocalDateTime.now().plusDays(2).truncatedTo(ChronoUnit.MILLIS),
launchDate, selfReviewCloseDate, closeDate,
periodStart, periodEnd));
}
}
Loading