diff --git a/server/build.gradle b/server/build.gradle index 5879128f30..0eb3bf25da 100755 --- a/server/build.gradle +++ b/server/build.gradle @@ -7,7 +7,7 @@ plugins { id "jacoco" } -version "0.8.7" +version "0.8.8" group "com.objectcomputing.checkins" repositories { diff --git a/server/src/main/java/com/objectcomputing/checkins/services/feedback_answer/FeedbackAnswerServices.java b/server/src/main/java/com/objectcomputing/checkins/services/feedback_answer/FeedbackAnswerServices.java index bdb6d3becb..b6e15e2ba4 100644 --- a/server/src/main/java/com/objectcomputing/checkins/services/feedback_answer/FeedbackAnswerServices.java +++ b/server/src/main/java/com/objectcomputing/checkins/services/feedback_answer/FeedbackAnswerServices.java @@ -1,5 +1,6 @@ package com.objectcomputing.checkins.services.feedback_answer; +import com.objectcomputing.checkins.services.feedback_request.FeedbackRequest; import io.micronaut.core.annotation.Nullable; import java.util.List; @@ -14,4 +15,6 @@ public interface FeedbackAnswerServices { FeedbackAnswer getById(UUID id); List findByValues(@Nullable UUID questionId, @Nullable UUID requestId); + + boolean getIsPermitted(FeedbackRequest feedbackRequest); } diff --git a/server/src/main/java/com/objectcomputing/checkins/services/feedback_answer/FeedbackAnswerServicesImpl.java b/server/src/main/java/com/objectcomputing/checkins/services/feedback_answer/FeedbackAnswerServicesImpl.java index d32806b246..f2eab96940 100644 --- a/server/src/main/java/com/objectcomputing/checkins/services/feedback_answer/FeedbackAnswerServicesImpl.java +++ b/server/src/main/java/com/objectcomputing/checkins/services/feedback_answer/FeedbackAnswerServicesImpl.java @@ -113,7 +113,7 @@ public List findByValues(@Nullable UUID questionId, @Nullable UU boolean isRequesteesSupervisor = requesteeId != null ? memberProfileServices.getSupervisorsForId(requesteeId).stream().anyMatch(profile -> currentUserId.equals(profile.getId())) : false; MemberProfile requestee = memberProfileServices.getById(requesteeId); final UUID requesteePDL = requestee.getPdlId(); - if (currentUserServices.isAdmin() || currentUserId.equals(requesteePDL) || isRequesteesSupervisor || requestCreatorId.equals(currentUserId) || recipientId.equals(currentUserId)) { + if (currentUserServices.isAdmin() || currentUserId.equals(requesteePDL) || isRequesteesSupervisor || requestCreatorId.equals(currentUserId) || recipientId.equals(currentUserId) || feedbackRequestServices.selfRevieweeIsCurrentUserReviewee(feedbackRequest, currentUserId)) { response.addAll(feedbackAnswerRepository.getByQuestionIdAndRequestId(Util.nullSafeUUIDToString(questionId), Util.nullSafeUUIDToString(requestId))); return response; } @@ -145,15 +145,36 @@ public boolean updateIsPermitted(FeedbackRequest feedbackRequest) { } public boolean getIsPermitted(FeedbackRequest feedbackRequest) { - final boolean isAdmin = currentUserServices.isAdmin(); - final UUID requestCreatorId = feedbackRequest.getCreatorId(); - UUID requesteeId = feedbackRequest.getRequesteeId(); - MemberProfile requestee = memberProfileServices.getById(requesteeId); + // Admins can always get questions and answers. + if (currentUserServices.isAdmin()) { + return true; + } + + // See if the current user is the requestee's supervisor. + final UUID requesteeId = feedbackRequest.getRequesteeId(); final UUID currentUserId = currentUserServices.getCurrentUser().getId(); + if (requesteeId != null && + memberProfileServices.getSupervisorsForId(requesteeId).stream().anyMatch(profile -> currentUserId.equals(profile.getId()))) { + return true; + } + + // See if the current user is the requestee's PDL. + final MemberProfile requestee = memberProfileServices.getById(requesteeId); + if (currentUserId.equals(requestee.getPdlId())) { + return true; + } + + // See if the current user is the request creator or the recipient of + // the request. + final UUID requestCreatorId = feedbackRequest.getCreatorId(); final UUID recipientId = feedbackRequest.getRecipientId(); - boolean isRequesteesSupervisor = requesteeId != null ? memberProfileServices.getSupervisorsForId(requesteeId).stream().anyMatch(profile -> currentUserId.equals(profile.getId())) : false; - final UUID requesteePDL = requestee.getPdlId(); + if (requestCreatorId.equals(currentUserId) || + recipientId.equals(currentUserId)) { + return true; + } + - return isAdmin || currentUserId.equals(requesteePDL) || isRequesteesSupervisor || requestCreatorId.equals(currentUserId) || recipientId.equals(currentUserId); + return feedbackRequestServices.selfRevieweeIsCurrentUserReviewee( + feedbackRequest, currentUserId); } } diff --git a/server/src/main/java/com/objectcomputing/checkins/services/feedback_answer/question_and_answer/QuestionAndAnswerServicesImpl.java b/server/src/main/java/com/objectcomputing/checkins/services/feedback_answer/question_and_answer/QuestionAndAnswerServicesImpl.java index bdff6d7e25..180b399879 100644 --- a/server/src/main/java/com/objectcomputing/checkins/services/feedback_answer/question_and_answer/QuestionAndAnswerServicesImpl.java +++ b/server/src/main/java/com/objectcomputing/checkins/services/feedback_answer/question_and_answer/QuestionAndAnswerServicesImpl.java @@ -42,7 +42,7 @@ public QuestionAndAnswerServicesImpl(FeedbackAnswerServices feedbackAnswerServic @Override public List getAllQuestionsAndAnswers(UUID requestId) { FeedbackRequest feedbackRequest = feedbackRequestServices.getById(requestId); - if (!getIsPermitted(feedbackRequest)) { + if (!feedbackAnswerServices.getIsPermitted(feedbackRequest)) { throw new PermissionException(NOT_AUTHORIZED_MSG); } List templateQuestions = templateQuestionServices.findByFields(feedbackRequest.getTemplateId()); @@ -77,7 +77,7 @@ public Tuple getQuestionAndAnswer(@Nullable UUID requestId, @Nullable UUID quest TemplateQuestion question = new TemplateQuestion(); FeedbackRequest feedbackRequest = feedbackRequestServices.getById(requestId); - if (!getIsPermitted(feedbackRequest)) { + if (!feedbackAnswerServices.getIsPermitted(feedbackRequest)) { throw new PermissionException(NOT_AUTHORIZED_MSG); } @@ -101,18 +101,4 @@ public Tuple getQuestionAndAnswer(@Nullable UUID requestId, @Nullable UUID quest } return new Tuple(question, returnedAnswer); } - - public boolean getIsPermitted(FeedbackRequest feedbackRequest) { - final boolean isAdmin = currentUserServices.isAdmin(); - final UUID requestCreatorId = feedbackRequest.getCreatorId(); - UUID requesteeId = feedbackRequest.getRequesteeId(); - MemberProfile requestee = memberProfileServices.getById(requesteeId); - final UUID currentUserId = currentUserServices.getCurrentUser().getId(); - final UUID recipientId = feedbackRequest.getRecipientId(); - boolean isRequesteesSupervisor = requesteeId != null ? memberProfileServices.getSupervisorsForId(requesteeId).stream().anyMatch(profile -> currentUserId.equals(profile.getId())) : false; - final UUID requesteePDL = requestee.getPdlId(); - - return isAdmin || currentUserId.equals(requesteePDL) || isRequesteesSupervisor || requestCreatorId.equals(currentUserId) || recipientId.equals(currentUserId); - } - } diff --git a/server/src/main/java/com/objectcomputing/checkins/services/feedback_request/FeedbackRequestServices.java b/server/src/main/java/com/objectcomputing/checkins/services/feedback_request/FeedbackRequestServices.java index 90cfd09ce3..31d0f7dfec 100644 --- a/server/src/main/java/com/objectcomputing/checkins/services/feedback_request/FeedbackRequestServices.java +++ b/server/src/main/java/com/objectcomputing/checkins/services/feedback_request/FeedbackRequestServices.java @@ -14,4 +14,7 @@ public interface FeedbackRequestServices { FeedbackRequest getById(UUID id); List findByValues(UUID creatorId, UUID requesteeId, UUID recipientId, LocalDate oldestDate, UUID reviewPeriodId, UUID templateId, List requesteeIds); -} \ No newline at end of file + + boolean selfRevieweeIsCurrentUserReviewee(FeedbackRequest request, + UUID currentUserId); +} diff --git a/server/src/main/java/com/objectcomputing/checkins/services/feedback_request/FeedbackRequestServicesImpl.java b/server/src/main/java/com/objectcomputing/checkins/services/feedback_request/FeedbackRequestServicesImpl.java index 180d8fef35..4d07ab3778 100644 --- a/server/src/main/java/com/objectcomputing/checkins/services/feedback_request/FeedbackRequestServicesImpl.java +++ b/server/src/main/java/com/objectcomputing/checkins/services/feedback_request/FeedbackRequestServicesImpl.java @@ -272,10 +272,8 @@ public FeedbackRequest getById(UUID id) { if (feedbackReq.isEmpty()) { throw new NotFoundException("No feedback req with id " + id); } - final LocalDate sendDate = feedbackReq.get().getSendDate(); - final UUID requesteeId = feedbackReq.get().getRequesteeId(); - final UUID recipientId = feedbackReq.get().getRecipientId(); - if (!getIsPermitted(requesteeId, recipientId, sendDate)) { + + if (!getIsPermitted(feedbackReq.get())) { throw new PermissionException(NOT_AUTHORIZED_MSG); } @@ -303,9 +301,12 @@ public List findByValues(UUID creatorId, UUID requesteeId, UUID if (currentUserServices.isAdmin()) { visible = true; } else if (request != null) { - if (currentUserId.equals(request.getCreatorId())) visible = true; - if (isSupervisor(request.getRequesteeId(), currentUserId)) visible = true; - if (currentUserId.equals(request.getRecipientId())) visible = true; + if (currentUserId.equals(request.getCreatorId()) || + isSupervisor(request.getRequesteeId(), currentUserId) || + currentUserId.equals(request.getRecipientId()) || + selfRevieweeIsCurrentUserReviewee(request, currentUserId)) { + visible = true; + } } return visible; }).toList(); @@ -318,6 +319,23 @@ private boolean isSupervisor(UUID requesteeId, UUID currentUserId) { && memberProfileServices.getSupervisorsForId(requesteeId).stream().anyMatch(profile -> currentUserId.equals(profile.getId())); } + public boolean selfRevieweeIsCurrentUserReviewee(FeedbackRequest request, + UUID currentUserId) { + // If we are looking at a self-review request, see if there is a review + // request in the same review period that is assigned to the current + // user and the requestee is the same as the self-review request. If + // so, this user is allowed to see the self-review request. + if (request.getRecipientId().equals(request.getRequesteeId())) { + List other = feedbackReqRepository.findByValues( + null, request.getRecipientId().toString(), + currentUserId.toString(), null, + Util.nullSafeUUIDToString(request.getReviewPeriodId()), + null); + return (other.size() == 1); + } + return false; + } + private boolean createIsPermitted(UUID requesteeId) { final boolean isAdmin = currentUserServices.isAdmin(); final UUID currentUserId = currentUserServices.getCurrentUser().getId(); @@ -329,8 +347,11 @@ private boolean createIsPermitted(UUID requesteeId) { return isAdmin || currentUserId.equals(requesteePDL) || isRequesteesSupervisor || currentUserId.equals(requesteeId); } - private boolean getIsPermitted(UUID requesteeId, UUID recipientId, LocalDate sendDate) { - LocalDate today = LocalDate.now(); + private boolean getIsPermitted(FeedbackRequest feedbackReq) { + final LocalDate sendDate = feedbackReq.getSendDate(); + final UUID requesteeId = feedbackReq.getRequesteeId(); + final UUID recipientId = feedbackReq.getRecipientId(); + final LocalDate today = LocalDate.now(); final UUID currentUserId = currentUserServices.getCurrentUser().getId(); // The recipient can only access the feedback request after it has been sent @@ -338,7 +359,9 @@ private boolean getIsPermitted(UUID requesteeId, UUID recipientId, LocalDate sen throw new PermissionException("You are not permitted to access this request before the send date."); } - return createIsPermitted(requesteeId) || currentUserId.equals(recipientId); + return createIsPermitted(requesteeId) || + currentUserId.equals(recipientId) || + selfRevieweeIsCurrentUserReviewee(feedbackReq, currentUserId); } private boolean updateDueDateIsPermitted(FeedbackRequest feedbackRequest) { diff --git a/server/src/main/java/com/objectcomputing/checkins/services/memberprofile/MemberProfileServices.java b/server/src/main/java/com/objectcomputing/checkins/services/memberprofile/MemberProfileServices.java index 211418808c..5643e30769 100644 --- a/server/src/main/java/com/objectcomputing/checkins/services/memberprofile/MemberProfileServices.java +++ b/server/src/main/java/com/objectcomputing/checkins/services/memberprofile/MemberProfileServices.java @@ -9,6 +9,8 @@ public interface MemberProfileServices { MemberProfile getById(UUID id); + MemberProfile findByWorkEmail(@NotNull String workEmail); + Set findByValues(String firstName, String lastName, String title, UUID pdlId, String workEmail, UUID supervisorId, Boolean terminated); diff --git a/server/src/main/java/com/objectcomputing/checkins/services/memberprofile/MemberProfileServicesImpl.java b/server/src/main/java/com/objectcomputing/checkins/services/memberprofile/MemberProfileServicesImpl.java index 9d1321dd38..667606949a 100644 --- a/server/src/main/java/com/objectcomputing/checkins/services/memberprofile/MemberProfileServicesImpl.java +++ b/server/src/main/java/com/objectcomputing/checkins/services/memberprofile/MemberProfileServicesImpl.java @@ -13,6 +13,7 @@ import com.objectcomputing.checkins.services.role.RoleServices; import com.objectcomputing.checkins.services.team.member.TeamMemberServices; import io.micronaut.cache.annotation.CacheConfig; +import io.micronaut.cache.annotation.CacheInvalidate; import io.micronaut.cache.annotation.Cacheable; import io.micronaut.core.annotation.Nullable; import jakarta.inject.Named; @@ -55,6 +56,7 @@ public MemberProfileServicesImpl(MemberProfileRepository memberProfileRepository } @Override + @Cacheable public MemberProfile getById(@NotNull UUID id) { Optional optional = memberProfileRepository.findById(id); if (optional.isEmpty()) { @@ -67,6 +69,14 @@ public MemberProfile getById(@NotNull UUID id) { return memberProfile; } + @Cacheable + @Override + public MemberProfile findByWorkEmail(@NotNull String workEmail) { + return memberProfileRepository.findByWorkEmail(workEmail).orElseThrow(() -> + new NotFoundException("Member not found") + ); + } + @Override public Set findByValues(@Nullable String firstName, @Nullable String lastName, @@ -86,6 +96,7 @@ public Set findByValues(@Nullable String firstName, } @Override + @CacheInvalidate(cacheNames = {"member-cache"}) public MemberProfile saveProfile(MemberProfile memberProfile) { MemberProfile emailProfile = memberProfileRepository.findByWorkEmail(memberProfile.getWorkEmail()).orElse(null); @@ -152,6 +163,7 @@ public void emailAssignment(MemberProfile member, boolean isPDL) { } @Override + @CacheInvalidate(cacheNames = {"member-cache"}) public boolean deleteProfile(@NotNull UUID id) { if (!currentUserServices.isAdmin()) { throw new PermissionException("Requires admin privileges"); @@ -183,6 +195,7 @@ public boolean deleteProfile(@NotNull UUID id) { } @Override + @Cacheable(parameters = {"firstName", "lastName"}) public MemberProfile findByName(@NotNull String firstName, @NotNull String lastName) { List searchResult = memberProfileRepository.search(firstName, null, lastName, null, null, null, null, null, null); @@ -222,6 +235,7 @@ public List getSubordinatesForId(UUID id) { } @Override + @CacheInvalidate(cacheNames = {"member-cache"}) public MemberProfile updateProfile(MemberProfile memberProfile) { return memberProfileRepository.update(memberProfile); } diff --git a/server/src/main/java/com/objectcomputing/checkins/services/reports/CSVProcessor.java b/server/src/main/java/com/objectcomputing/checkins/services/reports/CSVProcessor.java index b75cd91d34..b169c0ff42 100644 --- a/server/src/main/java/com/objectcomputing/checkins/services/reports/CSVProcessor.java +++ b/server/src/main/java/com/objectcomputing/checkins/services/reports/CSVProcessor.java @@ -1,8 +1,8 @@ package com.objectcomputing.checkins.services.reports; -import com.objectcomputing.checkins.services.memberprofile.MemberProfileRepository; import com.objectcomputing.checkins.exceptions.BadArgException; +import com.objectcomputing.checkins.services.memberprofile.MemberProfileServices; import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVParser; @@ -18,7 +18,7 @@ abstract class CSVProcessor { - public void load(MemberProfileRepository memberProfileRepository, + public void load(MemberProfileServices memberProfileServices, ByteBuffer dataSource) throws IOException, BadArgException { ByteArrayInputStream stream = new ByteArrayInputStream(dataSource.array()); @@ -30,10 +30,10 @@ public void load(MemberProfileRepository memberProfileRepository, .setNullString("") .build() .parse(input); - loadImpl(memberProfileRepository, csvParser); + loadImpl(memberProfileServices, csvParser); } - protected abstract void loadImpl(MemberProfileRepository memberProfileRepository, CSVParser csvParser) throws BadArgException; + protected abstract void loadImpl(MemberProfileServices memberProfileServices, CSVParser csvParser) throws BadArgException; protected LocalDate parseDate(String date) { List formatStrings = List.of("yyyy", "M/d/yyyy"); diff --git a/server/src/main/java/com/objectcomputing/checkins/services/reports/CompensationHistory.java b/server/src/main/java/com/objectcomputing/checkins/services/reports/CompensationHistory.java index e7cc69e186..15a1cca597 100644 --- a/server/src/main/java/com/objectcomputing/checkins/services/reports/CompensationHistory.java +++ b/server/src/main/java/com/objectcomputing/checkins/services/reports/CompensationHistory.java @@ -1,9 +1,10 @@ package com.objectcomputing.checkins.services.reports; +import com.objectcomputing.checkins.exceptions.NotFoundException; import com.objectcomputing.checkins.services.memberprofile.MemberProfile; -import com.objectcomputing.checkins.services.memberprofile.MemberProfileRepository; import com.objectcomputing.checkins.exceptions.BadArgException; +import com.objectcomputing.checkins.services.memberprofile.MemberProfileServices; import io.micronaut.core.annotation.Introspected; import org.apache.commons.csv.CSVParser; import org.apache.commons.csv.CSVRecord; @@ -12,10 +13,7 @@ import org.slf4j.LoggerFactory; import java.time.LocalDate; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; -import java.util.Optional; +import java.util.*; public class CompensationHistory extends CSVProcessor { @@ -32,41 +30,39 @@ public record Compensation( private final List history = new ArrayList<>(); @Override - protected void loadImpl(MemberProfileRepository memberProfileRepository, + protected void loadImpl(MemberProfileServices memberProfileServices, CSVParser csvParser) throws BadArgException { - history.clear(); - for (CSVRecord csvRecord : csvParser) { - try { - String emailAddress = csvRecord.get("emailAddress"); - Optional memberProfile = - memberProfileRepository.findByWorkEmail(emailAddress); - if (memberProfile.isPresent()) { - String startDate = csvRecord.get("startDate"); - LocalDate date = parseDate(startDate); - if (date == null) { - LOG.error("Unable to parse date: {}", startDate); - } else { - String value = csvRecord.get("compensation"); - Compensation comp = new Compensation( - memberProfile.get().getId(), - date, - value == null ? null : value.replaceAll("[^\\d\\.,]", ""), - csvRecord.get("totalComp") - ); - history.add(comp); + history.clear(); + for (CSVRecord csvRecord : csvParser) { + String emailAddress = null; + try { + emailAddress = csvRecord.get("emailAddress"); + MemberProfile memberProfile = memberProfileServices.findByWorkEmail(emailAddress); + String startDate = csvRecord.get("startDate"); + LocalDate date = parseDate(startDate); + if (date == null) { + LOG.error("Unable to parse date: {}", startDate); + } else { + String value = csvRecord.get("compensation"); + Compensation comp = new Compensation( + memberProfile.getId(), + date, + value == null ? null : value.replaceAll("[^\\d\\.,]", ""), + csvRecord.get("totalComp") + ); + history.add(comp); + } + } catch (NotFoundException nfe) { + LOG.error("Unable to find a profile for {}", emailAddress); + } catch (IllegalArgumentException ex) { + throw new BadArgException("Unable to parse the compensation history"); } - } else { - LOG.error("Unable to find a profile for {}", emailAddress); - } - } catch(IllegalArgumentException ex) { - throw new BadArgException("Unable to parse the compensation history"); } - } } public List getHistory(UUID memberId) { - return history.stream() - .filter(entry -> entry.memberId().equals(memberId)) - .toList(); + return history.stream() + .filter(entry -> entry.memberId().equals(memberId)) + .toList(); } } diff --git a/server/src/main/java/com/objectcomputing/checkins/services/reports/CurrentInformation.java b/server/src/main/java/com/objectcomputing/checkins/services/reports/CurrentInformation.java index 2391b0a534..081cf10d9a 100644 --- a/server/src/main/java/com/objectcomputing/checkins/services/reports/CurrentInformation.java +++ b/server/src/main/java/com/objectcomputing/checkins/services/reports/CurrentInformation.java @@ -2,9 +2,9 @@ import com.objectcomputing.checkins.exceptions.NotFoundException; import com.objectcomputing.checkins.services.memberprofile.MemberProfile; -import com.objectcomputing.checkins.services.memberprofile.MemberProfileRepository; import com.objectcomputing.checkins.exceptions.BadArgException; +import com.objectcomputing.checkins.services.memberprofile.MemberProfileServices; import io.micronaut.core.annotation.Introspected; import org.apache.commons.csv.CSVParser; import org.apache.commons.csv.CSVRecord; @@ -12,61 +12,56 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; -import java.util.Optional; +import java.util.*; public class CurrentInformation extends CSVProcessor { - @Introspected - public record Information( - UUID memberId, - float salary, - String range, - String nationalRange, - String biography, - String commitments - ) { - } + @Introspected + public record Information( + UUID memberId, + float salary, + String range, + String nationalRange, + String biography, + String commitments + ) { + } - private static final Logger LOG = LoggerFactory.getLogger(CurrentInformation.class); - private final List information = new ArrayList<>(); + private static final Logger LOG = LoggerFactory.getLogger(CurrentInformation.class); + private final List information = new ArrayList<>(); - @Override - protected void loadImpl(MemberProfileRepository memberProfileRepository, - CSVParser csvParser) throws BadArgException { - information.clear(); - for (CSVRecord csvRecord : csvParser) { - try { - String emailAddress = csvRecord.get("emailAddress"); - Optional memberProfile = - memberProfileRepository.findByWorkEmail(emailAddress); - if (memberProfile.isPresent()) { - Information comp = new Information( - memberProfile.get().getId(), - Float.parseFloat(csvRecord.get("salary") - .replaceAll("[^\\d\\.,]", "")), - csvRecord.get("range"), - csvRecord.get("nationalRange"), - csvRecord.get("biography"), - csvRecord.get("commitments") - ); - information.add(comp); - } else { - LOG.error("Unable to find a profile for {}", emailAddress); + @Override + protected void loadImpl(MemberProfileServices memberProfileServices, + CSVParser csvParser) throws BadArgException { + information.clear(); + for (CSVRecord csvRecord : csvParser) { + String emailAddress = null; + try { + emailAddress = csvRecord.get("emailAddress"); + MemberProfile memberProfile = memberProfileServices.findByWorkEmail(emailAddress); + Information comp = new Information( + memberProfile.getId(), + Float.parseFloat(csvRecord.get("salary") + .replaceAll("[^\\d\\.,]", "")), + csvRecord.get("range"), + csvRecord.get("nationalRange"), + csvRecord.get("biography"), + csvRecord.get("commitments") + ); + information.add(comp); + } catch (NotFoundException nfe) { + LOG.error("Unable to find a profile for {}", emailAddress); + } catch (IllegalArgumentException ex) { + throw new BadArgException("Unable to parse the current information"); + } } - } catch(IllegalArgumentException ex) { - throw new BadArgException("Unable to parse the current information"); - } } - } - public Information getInformation(UUID memberId) { - // There should only be one entry per member. - return information.stream() - .filter(entry -> entry.memberId().equals(memberId)) - .findFirst() - .orElseThrow(() -> new NotFoundException("Current Information not found for member: " + memberId)); - } + public Information getInformation(UUID memberId) { + // There should only be one entry per member. + return information.stream() + .filter(entry -> entry.memberId().equals(memberId)) + .findFirst() + .orElseThrow(() -> new NotFoundException("Current Information not found for member: " + memberId)); + } } diff --git a/server/src/main/java/com/objectcomputing/checkins/services/reports/PositionHistory.java b/server/src/main/java/com/objectcomputing/checkins/services/reports/PositionHistory.java index e3426680d3..87ab04d2ab 100644 --- a/server/src/main/java/com/objectcomputing/checkins/services/reports/PositionHistory.java +++ b/server/src/main/java/com/objectcomputing/checkins/services/reports/PositionHistory.java @@ -1,9 +1,11 @@ package com.objectcomputing.checkins.services.reports; +import com.objectcomputing.checkins.exceptions.NotFoundException; import com.objectcomputing.checkins.services.memberprofile.MemberProfile; import com.objectcomputing.checkins.services.memberprofile.MemberProfileRepository; import com.objectcomputing.checkins.exceptions.BadArgException; +import com.objectcomputing.checkins.services.memberprofile.MemberProfileServices; import io.micronaut.core.annotation.Introspected; import org.apache.commons.csv.CSVParser; import org.apache.commons.csv.CSVRecord; @@ -12,57 +14,52 @@ import org.slf4j.LoggerFactory; import java.time.LocalDate; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; -import java.util.Optional; +import java.util.*; public class PositionHistory extends CSVProcessor { - @Introspected - public record Position( - UUID memberId, - LocalDate date, - String title - ) { - } + @Introspected + public record Position( + UUID memberId, + LocalDate date, + String title + ) { + } - private static final Logger LOG = LoggerFactory.getLogger(PositionHistory.class); - private final List history = new ArrayList<>(); + private static final Logger LOG = LoggerFactory.getLogger(PositionHistory.class); + private final List history = new ArrayList<>(); - @Override - protected void loadImpl(MemberProfileRepository memberProfileRepository, - CSVParser csvParser) throws BadArgException { - history.clear(); - for (CSVRecord csvRecord : csvParser) { - try { - String emailAddress = csvRecord.get("emailAddress"); - Optional memberProfile = - memberProfileRepository.findByWorkEmail(emailAddress); - if (memberProfile.isPresent()) { - String csvDate = csvRecord.get("date"); - LocalDate date = parseDate(csvDate); - if (date == null) { - LOG.error("Unable to parse date: {}", csvDate); - } else { - Position position = new Position( - memberProfile.get().getId(), - date, - csvRecord.get("title")); - history.add(position); - } - } else { - LOG.error("Unable to find a profile for {}", emailAddress); + @Override + protected void loadImpl(MemberProfileServices memberProfileServices, + CSVParser csvParser) throws BadArgException { + history.clear(); + for (CSVRecord csvRecord : csvParser) { + String emailAddress = null; + try { + emailAddress = csvRecord.get("emailAddress"); + MemberProfile memberProfile = memberProfileServices.findByWorkEmail(emailAddress); + String csvDate = csvRecord.get("date"); + LocalDate date = parseDate(csvDate); + if (date == null) { + LOG.error("Unable to parse date: {}", csvDate); + } else { + Position position = new Position( + memberProfile.getId(), + date, + csvRecord.get("title")); + history.add(position); + } + } catch (NotFoundException nfe) { + LOG.error("Unable to find a profile for {}", emailAddress); + } catch (IllegalArgumentException ex) { + throw new BadArgException("Unable to parse the position history"); + } } - } catch(IllegalArgumentException ex) { - throw new BadArgException("Unable to parse the position history"); - } } - } - public List getHistory(UUID memberId) { - return history.stream() - .filter(entry -> entry.memberId().equals(memberId)) - .toList(); - } + public List getHistory(UUID memberId) { + return history.stream() + .filter(entry -> entry.memberId().equals(memberId)) + .toList(); + } } diff --git a/server/src/main/java/com/objectcomputing/checkins/services/reports/ReportDataCollation.java b/server/src/main/java/com/objectcomputing/checkins/services/reports/ReportDataCollation.java index 969aa5aea5..d762263318 100644 --- a/server/src/main/java/com/objectcomputing/checkins/services/reports/ReportDataCollation.java +++ b/server/src/main/java/com/objectcomputing/checkins/services/reports/ReportDataCollation.java @@ -1,6 +1,7 @@ package com.objectcomputing.checkins.services.reports; import com.objectcomputing.checkins.exceptions.NotFoundException; +import com.objectcomputing.checkins.services.memberprofile.MemberProfileServices; import com.objectcomputing.checkins.services.memberprofile.MemberProfileUtils; import com.objectcomputing.checkins.services.kudos.kudos_recipient.KudosRecipientRepository; import com.objectcomputing.checkins.services.kudos.kudos_recipient.KudosRecipient; @@ -65,7 +66,7 @@ private enum FeedbackType { private PositionHistory positionHistory; private KudosRepository kudosRepository; private KudosRecipientRepository kudosRecipientRepository; - private MemberProfileRepository memberProfileRepository; + private MemberProfileServices memberProfileServices; private ReviewPeriodServices reviewPeriodServices; private ReportDataServices reportDataServices; private FeedbackTemplateServices feedbackTemplateServices; @@ -78,7 +79,7 @@ public ReportDataCollation( UUID memberId, UUID reviewPeriodId, KudosRepository kudosRepository, KudosRecipientRepository kudosRecipientRepository, - MemberProfileRepository memberProfileRepository, + MemberProfileServices memberProfileServices, ReviewPeriodServices reviewPeriodServices, ReportDataServices reportDataServices, FeedbackTemplateServices feedbackTemplateServices, @@ -93,7 +94,7 @@ public ReportDataCollation( this.positionHistory = new PositionHistory(); this.kudosRepository = kudosRepository; this.kudosRecipientRepository = kudosRecipientRepository; - this.memberProfileRepository = memberProfileRepository; + this.memberProfileServices = memberProfileServices; this.reviewPeriodServices = reviewPeriodServices; this.reportDataServices = reportDataServices; this.feedbackTemplateServices = feedbackTemplateServices; @@ -125,7 +126,7 @@ public List getKudos() { LocalDate created = kudos.getDateCreated(); if (dateInRange(created, startDate, endDate)) { MemberProfile senderProfile = - memberProfileRepository.findById(kudos.getSenderId()).orElse(null); + memberProfileServices.getById(kudos.getSenderId()); String sender = senderProfile == null ? "Unknown" : MemberProfileUtils.getFullName(senderProfile); @@ -139,9 +140,7 @@ public List getKudos() { /// Get the member name, title, and start date among others. public MemberProfile getMemberProfile() { - return memberProfileRepository.findById(memberId).orElseThrow(() -> - new NotFoundException("Member not found") - ); + return memberProfileServices.getById(memberId); } /// Get the compensation history for the designated member. @@ -149,7 +148,7 @@ public List getCompensationHistory() { try { ByteBuffer buffer = reportDataServices.get( ReportDataServices.DataType.compensationHistory); - compensationHistory.load(memberProfileRepository, buffer); + compensationHistory.load(memberProfileServices, buffer); } catch(IOException ex) { } return compensationHistory.getHistory(memberId); @@ -160,7 +159,7 @@ public CurrentInformation.Information getCurrentInformation() { try { ByteBuffer buffer = reportDataServices.get( ReportDataServices.DataType.currentInformation); - currentInformation.load(memberProfileRepository, buffer); + currentInformation.load(memberProfileServices, buffer); } catch(IOException ex) { } return currentInformation.getInformation(memberId); @@ -171,7 +170,7 @@ public List getPositionHistory() { try { ByteBuffer buffer = reportDataServices.get( ReportDataServices.DataType.positionHistory); - positionHistory.load(memberProfileRepository, buffer); + positionHistory.load(memberProfileServices, buffer); } catch(IOException ex) { } return positionHistory.getHistory(memberId); @@ -282,8 +281,8 @@ private List getFeedbackType(FeedbackType type) { for (FeedbackRequest request: requests) { if (request.getTemplateId().equals(templateId)) { UUID recipientId = request.getRecipientId(); - MemberProfile recipient = memberProfileRepository.findById( - recipientId).orElse(null); + MemberProfile recipient = memberProfileServices.getById( + recipientId); String recipientName = (recipient == null ? recipientId.toString() : MemberProfileUtils.getFullName(recipient)); @@ -307,12 +306,7 @@ private List getFeedbackType(FeedbackType type) { feedbackAnswers.add( new Feedback.Answer( recipientName, request.getSubmitDate(), questionText, - questionType.equals(textQuestion) || - questionType.equals(radioQuestion) ? - answer.getAnswer() : - String.valueOf(answer.getSentiment()), - questionType, - questionNumber)); + answer.getAnswer(), questionType, questionNumber)); } } } diff --git a/server/src/main/java/com/objectcomputing/checkins/services/reports/ReportDataController.java b/server/src/main/java/com/objectcomputing/checkins/services/reports/ReportDataController.java index d616748ce4..15647cede2 100644 --- a/server/src/main/java/com/objectcomputing/checkins/services/reports/ReportDataController.java +++ b/server/src/main/java/com/objectcomputing/checkins/services/reports/ReportDataController.java @@ -1,5 +1,6 @@ package com.objectcomputing.checkins.services.reports; +import com.objectcomputing.checkins.services.memberprofile.MemberProfileServices; import com.objectcomputing.checkins.services.permissions.Permission; import com.objectcomputing.checkins.services.permissions.RequiredPermission; import com.objectcomputing.checkins.services.kudos.KudosRepository; @@ -43,7 +44,7 @@ public class ReportDataController { private final ReportDataServices reportDataServices; private final KudosRepository kudosRepository; private final KudosRecipientRepository kudosRecipientRepository; - private final MemberProfileRepository memberProfileRepository; + private final MemberProfileServices memberProfileServices; private final ReviewPeriodServices reviewPeriodServices; private final FeedbackTemplateServices feedbackTemplateServices; private final FeedbackRequestServices feedbackRequestServices; @@ -54,7 +55,7 @@ public class ReportDataController { public ReportDataController(ReportDataServices reportDataServices, KudosRepository kudosRepository, KudosRecipientRepository kudosRecipientRepository, - MemberProfileRepository memberProfileRepository, + MemberProfileServices memberProfileServices, ReviewPeriodServices reviewPeriodServices, FeedbackTemplateServices feedbackTemplateServices, FeedbackRequestServices feedbackRequestServices, @@ -64,7 +65,7 @@ public ReportDataController(ReportDataServices reportDataServices, this.reportDataServices = reportDataServices; this.kudosRepository = kudosRepository; this.kudosRecipientRepository = kudosRecipientRepository; - this.memberProfileRepository = memberProfileRepository; + this.memberProfileServices = memberProfileServices; this.reviewPeriodServices = reviewPeriodServices; this.feedbackTemplateServices = feedbackTemplateServices; this.feedbackRequestServices = feedbackRequestServices; @@ -124,7 +125,7 @@ public List get(@NotNull List memberIds, memberId, reviewPeriodId, kudosRepository, kudosRecipientRepository, - memberProfileRepository, + memberProfileServices, reviewPeriodServices, reportDataServices, feedbackTemplateServices, diff --git a/web-ui/package.json b/web-ui/package.json index 975b2f8e64..4ff698e894 100644 --- a/web-ui/package.json +++ b/web-ui/package.json @@ -1,6 +1,6 @@ { "name": "web-ui", - "version": "0.8.7", + "version": "0.8.8", "private": true, "type": "module", "dependencies": { diff --git a/web-ui/src/components/reviews/TeamReviews.jsx b/web-ui/src/components/reviews/TeamReviews.jsx index 5a6d92235f..418518416d 100644 --- a/web-ui/src/components/reviews/TeamReviews.jsx +++ b/web-ui/src/components/reviews/TeamReviews.jsx @@ -55,6 +55,7 @@ import { } from '../../context/actions'; import { AppContext } from '../../context/AppContext'; import { + selectIsAdmin, selectCsrfToken, selectCurrentMembers, selectCurrentUser, @@ -125,7 +126,7 @@ const ReviewStatus = { const TeamReviews = ({ onBack, periodId }) => { const { state, dispatch } = useContext(AppContext); const location = useLocation(); - + const isAdmin = selectIsAdmin(state); const [openMode, setOpenMode] = useState(false); const [approvalState, setApprovalState] = useState(false); const [assignments, setAssignments] = useState([]); @@ -796,7 +797,7 @@ const TeamReviews = ({ onBack, periodId }) => { const url = getReviewerURL(request, selfReviewRequest); return (url ? - + { const manages = recipientProfile.supervisorid == currentUser?.id; const request = getReviewRequest(member, currentUser); const isReviewer = request?.recipientId == currentUser?.id; - if (manages || isReviewer) { + if (isAdmin || manages || isReviewer) { const selfReviewRequest = getSelfReviewRequest(member); return ( { - const agreeMarks = [ - 'Strongly Disagree', - 'Disagree', - 'Neither Agree nor Disagree', - 'Agree', - 'Strongly Agree' - ]; - const frequencyMarks = [ - 'Very Infrequently', - 'Infrequently', - 'Neither Frequently nor Infrequently', - 'Frequently', - 'Very Frequently' - ]; - const { state, dispatch } = useContext(AppContext); const csrf = selectCsrfToken(state); @@ -303,7 +288,7 @@ const MeritReportPage = () => { const questions = getUniqueQuestions(feedback.answers); for(let question of Object.keys(questions)) { - text += markdown.headers.h3(question) + "\n"; + text += markdown.headers.h4(question) + "\n"; for(let answer of questions[question]) { if (listMembers) { text += answer[0] + ": "; @@ -338,22 +323,6 @@ const MeritReportPage = () => { }; const getAnswerText = (answer) => { - if (answer.type == "SLIDER" || answer.type == "FREQ") { - const sentiment = parseFloat(answer.answer); - if (!isNaN(sentiment)) { - if (answer.type == "SLIDER") { - const index = sentiment * agreeMarks.length; - if (index >= 0 && index < agreeMarks.length) { - return agreeMarks[index]; - } - } else if (answer.type == "FREQ") { - const index = sentiment * frequencyMarks.length; - if (index >= 0 && index < frequencyMarks.length) { - return frequencyMarks[index]; - } - } - } - } return answer.answer; }; @@ -388,7 +357,7 @@ const MeritReportPage = () => { const questions = getUniqueQuestions(feedback.answers); for(let question of Object.keys(questions)) { - text += markdown.headers.h3(question) + "\n"; + text += markdown.headers.h4(question) + "\n"; for(let answer of questions[question]) { text += answer[0] + ": " + answer[1] + "\n\n"; } @@ -434,14 +403,13 @@ const MeritReportPage = () => { const prepareCompensationHistory = (data, fn) => { return data.compensationHistory.filter(fn).sort((a, b) => { - for(let i = 0; i < a.length; i++) { + for(let i = 0; i < a.startDate.length; i++) { if (a.startDate[i] != b.startDate[i]) { return b.startDate[i] - a.startDate[i]; } } return 0; }).slice(0, 3); - }; const markdownCompensationHistory = (data) => { @@ -450,12 +418,17 @@ const MeritReportPage = () => { const compTotal = prepareCompensationHistory(data, (comp) => !!comp.totalComp); let text = markdown.headers.h2("Compensation History"); + text += markdown.headers.h3("Base Compensation (annual or hourly)"); text += markdown.lists.ul(compBase, (comp) => formatDate(dateFromArray(comp.startDate)) + " - " + - "$" + parseFloat(comp.amount).toFixed(2) + " (base)"); + "$" + parseFloat(comp.amount).toFixed(2)); + text += markdown.headers.h3("Total Compensation") text += markdown.lists.ul(compTotal, - (comp) => dateFromArray(comp.startDate).getFullYear() + " - " + - comp.totalComp); + (comp) => { + var date = dateFromArray(comp.startDate); + date = date.getMonth() === 0 && date.getDate() === 1 ? date.getFullYear() : formatDate(date); + return date + " - " + comp.totalComp; + }); return text; }; @@ -512,7 +485,7 @@ const MeritReportPage = () => {