Skip to content

Commit

Permalink
OAM-17: Homepage UI alerts (#95)
Browse files Browse the repository at this point in the history
* OAM-17: Added GET /api/requisitions/statusesStatsData endpoint

* OAM-17: Added tests for getStatusesStatsData method and GET api/requisitions/statusesStatsData endpoint

* OAM-17: Changed params order, split method into smaller ones
  • Loading branch information
sradziszewski authored Mar 27, 2024
1 parent 3f5e847 commit fcd98f8
Show file tree
Hide file tree
Showing 12 changed files with 423 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Matchers.any;
Expand Down Expand Up @@ -76,6 +77,7 @@
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.openlmis.requisition.domain.RequisitionStatsData;
import org.openlmis.requisition.domain.RequisitionTemplate;
import org.openlmis.requisition.domain.requisition.Requisition;
import org.openlmis.requisition.domain.requisition.RequisitionStatus;
Expand Down Expand Up @@ -109,6 +111,7 @@
import org.openlmis.requisition.testutils.OrderableDtoDataBuilder;
import org.openlmis.requisition.testutils.ProgramDtoDataBuilder;
import org.openlmis.requisition.testutils.ReleasableRequisitionDtoDataBuilder;
import org.openlmis.requisition.testutils.UserDtoDataBuilder;
import org.openlmis.requisition.utils.DateHelper;
import org.openlmis.requisition.utils.Message;
import org.openlmis.requisition.utils.Pagination;
Expand Down Expand Up @@ -147,6 +150,8 @@ public class RequisitionControllerIntegrationTest extends BaseRequisitionWebInte
+ "/requisitionsForConvert";
private static final String NUMBER_OF_REQ_FOR_APPROVAL_URL = RESOURCE_URL
+ "/numberOfRequisitionsForApproval";
private static final String STATUSES_STATS_DATA_URL = RESOURCE_URL
+ "/statusesStatsData";

private static final String FACILITY = "facility";
private static final String PROGRAM = "program";
Expand Down Expand Up @@ -1966,6 +1971,57 @@ public void shouldGetNumberOfRequisitionsForApprovalForSpecificUserAndProgram()
assertThat(RAML_ASSERT_MESSAGE, restAssured.getLastReport(), RamlMatchers.hasNoViolations());
}

// GET /api/requisitions/statusesStatsData

@Test
public void shouldGetRequisitionStatsData() {
// given
RequisitionStatsData requisitionStatsData = new RequisitionStatsData();
requisitionStatsData.setRequisitionsToBeCreated(10L);
FacilityDto facility = mockFacility();

given(facilityReferenceDataService.findOne(eq(user.getHomeFacilityId())))
.willReturn(facility);
given(requisitionService.getStatusesStatsData(facility))
.willReturn(requisitionStatsData);

// when
RequisitionStatsData result = restAssured.given()
.header(HttpHeaders.AUTHORIZATION, getTokenHeader())
.contentType(MediaType.APPLICATION_JSON_VALUE)
.when()
.get(STATUSES_STATS_DATA_URL)
.then()
.statusCode(200)
.extract().as(RequisitionStatsData.class);

// then
assertEquals(Long.valueOf(10), result.getRequisitionsToBeCreated());
assertThat(RAML_ASSERT_MESSAGE, restAssured.getLastReport(), RamlMatchers.hasNoViolations());
}

@Test
public void shouldGetRequisitionStatsDataIfNoHomeFacilityAssigned() {
// given
UserDto userWithoutHomeFacility = new UserDtoDataBuilder().withoutHomeFacility().buildAsDto();

given(authenticationHelper.getCurrentUser()).willReturn(userWithoutHomeFacility);

// when
RequisitionStatsData result = restAssured.given()
.header(HttpHeaders.AUTHORIZATION, getTokenHeader())
.contentType(MediaType.APPLICATION_JSON_VALUE)
.when()
.get(STATUSES_STATS_DATA_URL)
.then()
.statusCode(200)
.extract().as(RequisitionStatsData.class);

// then
assertNull(result.getFacilityId());
assertThat(RAML_ASSERT_MESSAGE, restAssured.getLastReport(), RamlMatchers.hasNoViolations());
}

// GET /api/requisitions/requisitionsForConvert

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* This program is part of the OpenLMIS logistics management information system platform software.
* Copyright © 2017 VillageReach
*
* This program is free software: you can redistribute it and/or modify it under the terms
* of the GNU Affero General Public License as published by the Free Software Foundation, either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Affero General Public License for more details. You should have received a copy of
* the GNU Affero General Public License along with this program. If not, see
* http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.
*/

package org.openlmis.requisition.domain;

import java.util.Map;
import java.util.UUID;
import lombok.Getter;
import lombok.Setter;

/**
* The object contains statistical data about requisitions:
* number of requisitions to be created for current periods
* and number of requisitions with each status available in the system.
*/
@Getter
@Setter
public class RequisitionStatsData {

private UUID facilityId;
private Long requisitionsToBeCreated;
private Map<String, Long> statusesStats;

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.apache.commons.lang3.tuple.Pair;
import org.openlmis.requisition.domain.requisition.Requisition;
import org.openlmis.requisition.domain.requisition.RequisitionPeriod;
import org.openlmis.requisition.domain.requisition.RequisitionStatus;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

Expand All @@ -34,6 +35,12 @@ Page<Requisition> searchRequisitions(RequisitionSearchParams params,
List<Requisition> searchRequisitions(UUID processingPeriod,
UUID facility, UUID program, Boolean emergency);

Long countRequisitions(UUID processingPeriod, UUID facility, UUID program, Boolean emergency,
RequisitionStatus status);

Long countRequisitions(List<UUID> processingPeriods, UUID facility, List<UUID> programs,
Boolean emergency, List<RequisitionStatus> statuses);

Optional<Requisition> findRegularRequisition(UUID processingPeriod, UUID facility, UUID program);

List<RequisitionPeriod> searchRequisitionIdAndStatusPairs(UUID facility, UUID program,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,74 @@ public List<Requisition> searchRequisitions(UUID processingPeriod, UUID facility
return entityManager.createQuery(query).getResultList();
}

/**
* Method returns number of all Requisitions with matched parameters.
*
* @param processingPeriod ProcessingPeriod of searched Requisitions.
* @param facility Facility of searched Requisitions.
* @param program Program of searched Requisitions.
* @param status Status of searched Requisitions.
* @param emergency if {@code true}, the method will look only for emergency requisitions,
* if {@code false}, the method will look only for standard requisitions,
* if {@code null} the method will check all requisitions.
* @return Number of Requisitions with matched parameters.
*/
@Override
public Long countRequisitions(UUID processingPeriod, UUID facility,
UUID program, Boolean emergency, RequisitionStatus status) {
CriteriaBuilder builder = getCriteriaBuilder();

CriteriaQuery<Long> query = builder.createQuery(Long.class);
Root<Requisition> root = query.from(Requisition.class);

query = query.select(builder.count(root));

Predicate predicate = builder.conjunction();
predicate = addEqualFilter(predicate, builder, root, EMERGENCY, emergency);
predicate = addEqualFilter(predicate, builder, root, PROCESSING_PERIOD_ID, processingPeriod);
predicate = addEqualFilter(predicate, builder, root, FACILITY_ID, facility);
predicate = addEqualFilter(predicate, builder, root, PROGRAM_ID, program);
predicate = addEqualFilter(predicate, builder, root, STATUS, status);

query.where(predicate);

return countEntities(query);
}

/**
* Method returns number of all Requisitions with matched parameters.
*
* @param facility Facility of searched Requisitions.
* @param programs Program IDs of searched Requisitions.
* @param processingPeriods ProcessingPeriod IDs of searched Requisitions.
* @param statuses Statuses of searched Requisitions.
* @param emergency if {@code true}, the method will look only for emergency requisitions,
* if {@code false}, the method will look only for standard requisitions,
* if {@code null} the method will check all requisitions.
* @return Number of Requisitions with matched parameters.
*/
@Override
public Long countRequisitions(List<UUID> processingPeriods, UUID facility,
List<UUID> programs, Boolean emergency, List<RequisitionStatus> statuses) {
CriteriaBuilder builder = getCriteriaBuilder();

CriteriaQuery<Long> query = builder.createQuery(Long.class);
Root<Requisition> root = query.from(Requisition.class);

query = query.select(builder.count(root));

Predicate predicate = builder.conjunction();
predicate = addEqualFilter(predicate, builder, root, EMERGENCY, emergency);
predicate = addEqualFilter(predicate, builder, root, FACILITY_ID, facility);
predicate = addInFilter(predicate, builder, root, PROGRAM_ID, programs);
predicate = addInFilter(predicate, builder, root, PROCESSING_PERIOD_ID, processingPeriods);
predicate = addInFilter(predicate, builder, root, STATUS, statuses);

query.where(predicate);

return countEntities(query);
}

/**
* Method returns Requisition with matched parameters.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ public Collection<ProcessingPeriodDto> searchByProgramAndFacility(UUID programId
return periodReferenceDataService.searchByProgramAndFacility(programId, facilityId);
}

public Collection<ProcessingPeriodDto> searchByProgramAndFacilityAndDateRange(UUID programId,
UUID facilityId, LocalDate startDate, LocalDate endDate) {
return periodReferenceDataService.search(programId, facilityId, startDate, endDate);
}

public ProcessingPeriodDto getPeriod(UUID periodId) {
return periodReferenceDataService.findOne(periodId);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
Expand All @@ -50,11 +51,11 @@
import java.util.Optional;
import java.util.Set;
import java.util.UUID;

import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.openlmis.requisition.domain.Rejection;
import org.openlmis.requisition.domain.RejectionReason;
import org.openlmis.requisition.domain.RequisitionStatsData;
import org.openlmis.requisition.domain.RequisitionTemplate;
import org.openlmis.requisition.domain.requisition.ApprovedProductReference;
import org.openlmis.requisition.domain.requisition.Requisition;
Expand Down Expand Up @@ -807,6 +808,60 @@ private boolean isRequisitionNewest(Requisition requisition) {
return null == recentRequisition || requisition.getId().equals(recentRequisition.getId());
}


/**
* Returns statistics data regarding requisitions statuses for a specified facility:
* number of requisitions to be created for current periods and number of requisitions
* with each status available in the system.
*
* @param facility FacilityDto object.
* @return RequisitionStatsData object.
*/
public RequisitionStatsData getStatusesStatsData(FacilityDto facility) {
Profiler profiler = new Profiler("GET_STATUSES_STATS_DATA");
profiler.setLogger(LOGGER);

UUID facilityId = facility.getId();
List<RequisitionStatus> postSubmittedStatuses = new ArrayList<>();
Map<String, Long> statusesStats = new HashMap<>();
profiler.start("COUNT_REQUISITIONS_FOR_STATUSES");
for (RequisitionStatus status : RequisitionStatus.values()) {
countRequisitionsForStatus(status, statusesStats, facilityId);
if (status.isPostSubmitted()) {
postSubmittedStatuses.add(status);
}
}
RequisitionStatsData requisitionStatsData = new RequisitionStatsData();
requisitionStatsData.setStatusesStats(statusesStats);
requisitionStatsData.setFacilityId(facilityId);

// The maximum number of combinations of actively supported programs and periods for a given
// facility will be the number of requisitions that can be created for that facility.
Set<ProcessingPeriodDto> currentFacilityPeriods = new HashSet<>();
profiler.start("GET_ACTIVELY_SUPPORTED_PROGRAMS");
List<SupportedProgramDto> activelySupportedPrograms = facility.getSupportedPrograms()
.stream()
.filter(SupportedProgramDto::isSupportActive)
.filter(SupportedProgramDto::isProgramActive)
.collect(toList());
profiler.start("GET_CURRENT_PERIODS_FOR_FACILITY");
getCurrentPeriodsForFacility(activelySupportedPrograms, facilityId, currentFacilityPeriods);

profiler.start("CALCULATE_REQUISITIONS_TO_BE_CREATED");
long maxRequisitionForFacility = currentFacilityPeriods.size();
if (maxRequisitionForFacility == 0) {
requisitionStatsData.setRequisitionsToBeCreated(0L);
} else {
Long createdRequisitions = calculateCurrentlyCreatedRequisitions(activelySupportedPrograms,
currentFacilityPeriods, facilityId, postSubmittedStatuses);
long requisitionsToBeCreated = maxRequisitionForFacility - createdRequisitions;
requisitionStatsData.setRequisitionsToBeCreated(requisitionsToBeCreated);
}

profiler.stop().log();
return requisitionStatsData;
}

/**
* Returns requisition associated with the most recent period for given program and facility.
*
Expand Down Expand Up @@ -931,6 +986,7 @@ private static Set<Pair<UUID, UUID>> getProgramNodePairs(UUID programId,

/**
* Adds approver details to unskipped requisition line items.
*
* @param requisition object
*/
public void processUnSkippedRequisitionLineItems(Requisition requisition,Locale locale) {
Expand All @@ -943,6 +999,7 @@ public void processUnSkippedRequisitionLineItems(Requisition requisition,Locale

/**
* Updates patientsData of requisition.
*
* @param requisitionId - id of requisition
* @param patientsData - stringified JSON string to store patients data
* @return requisition object.
Expand All @@ -954,4 +1011,50 @@ public Requisition updatePatientsData(UUID requisitionId, String patientsData) {
requisition.setPatientsData(patientsData);
return requisition;
}

private void countRequisitionsForStatus(RequisitionStatus status,
Map<String, Long> statusesStats, UUID facilityId) {
statusesStats.put(
status.name(),
requisitionRepository.countRequisitions(null, facilityId,
null, null, status)
);
}

private void getCurrentPeriodsForFacility(List<SupportedProgramDto> activelySupportedPrograms,
UUID facilityId, Set<ProcessingPeriodDto> currentFacilityPeriods) {
activelySupportedPrograms.forEach(program -> {
if (program.isSupportActive()) {
List<ProcessingPeriodDto> foundPeriods =
new ArrayList<>(periodService.searchByProgramAndFacilityAndDateRange(
program.getId(), facilityId,
LocalDate.now(), LocalDate.now()));
currentFacilityPeriods.addAll(foundPeriods);
}
});
}

private Long calculateCurrentlyCreatedRequisitions(
List<SupportedProgramDto> activelySupportedPrograms,
Set<ProcessingPeriodDto> currentFacilityPeriods, UUID facilityId,
List<RequisitionStatus> postSubmittedStatuses) {
Profiler profiler = new Profiler("CALCULATE_CURRENTLY_CREATED_REQUISITIONS");
profiler.setLogger(LOGGER);

profiler.start("GET_ACTIVELY_SUPPORTED_PROGRAMS_IDS");
List<UUID> activelySupportedProgramsIds = activelySupportedPrograms.stream()
.map(SupportedProgramDto::getId)
.collect(toList());
profiler.start("GET_CURRENT_FACILITY_PERIODS_IDS");
List<UUID> currentFacilityPeriodsIds = currentFacilityPeriods.stream()
.map(ProcessingPeriodDto::getId)
.collect(toList());

profiler.stop().log();
return requisitionRepository.countRequisitions(
currentFacilityPeriodsIds, facilityId, activelySupportedProgramsIds, null,
postSubmittedStatuses
);
}

}
Loading

0 comments on commit fcd98f8

Please sign in to comment.