Skip to content

Commit

Permalink
LocationHierarchy Inventory Filter
Browse files Browse the repository at this point in the history
 fixes #81
  • Loading branch information
lincmba committed Sep 13, 2024
1 parent e77b133 commit 601c62f
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ public class Constants {
"https://smartregister.org/care-team-tag-id";
public static final String CODE_URL_VALUE_SEPARATOR = "|";
public static final String EMPTY_STRING = "";
public static final String FORWARD_SLASH = "/";
public static final String LOCATION_TAG_URL_ENV = "LOCATION_TAG_URL";
public static final String DEFAULT_LOCATION_TAG_URL =
"https://smartregister.org/location-tag-id";
Expand All @@ -23,6 +24,7 @@ public class Constants {
public static final String IDENTIFIER = "_id";
public static final String MIN_ADMIN_LEVEL = "administrativeLevelMin";
public static final String MAX_ADMIN_LEVEL = "administrativeLevelMax";
public static final String FILTER_INVENTORY = "filterInventory";
public static final int DEFAULT_MAX_ADMIN_LEVEL = 10;
public static final int DEFAULT_MIN_ADMIN_LEVEL = 0;
public static final String PAGINATION_PAGE_SIZE = "_count";
Expand All @@ -38,6 +40,7 @@ public class Constants {
public static final String ROLE_WEB_CLIENT = "WEB_CLIENT";
public static final String MODE = "mode";
public static final String LIST = "list";
public static final String SUBJECT = "subject";
public static final String CORS_ALLOW_HEADERS_KEY = "Access-Control-Allow-Headers";
public static final String CORS_ALLOW_HEADERS_VALUE = "authorization, cache-control";
public static final String CORS_ALLOW_METHODS_KEY = "Access-Control-Allow-Methods";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.hl7.fhir.r4.model.CodeableConcept;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.Composition;
import org.hl7.fhir.r4.model.ListResource;
import org.hl7.fhir.r4.model.Location;
import org.hl7.fhir.r4.model.Resource;
import org.hl7.fhir.r4.model.StringType;
Expand Down Expand Up @@ -57,12 +58,14 @@ private IGenericClient getFhirClientForR4() {
public LocationHierarchy getLocationHierarchy(
String locationId,
List<String> preFetchAdminLevels,
List<String> postFetchAdminLevels) {
List<String> postFetchAdminLevels,
Boolean filterInventory) {
LocationHierarchy locationHierarchy;

if (CacheHelper.INSTANCE.skipCache()) {
locationHierarchy =
getLocationHierarchyCore(locationId, preFetchAdminLevels, postFetchAdminLevels);
getLocationHierarchyCore(
locationId, preFetchAdminLevels, postFetchAdminLevels, filterInventory);
} else {
locationHierarchy =
(LocationHierarchy)
Expand All @@ -72,38 +75,46 @@ public LocationHierarchy getLocationHierarchy(
getLocationHierarchyCore(
locationId,
preFetchAdminLevels,
postFetchAdminLevels));
postFetchAdminLevels,
filterInventory));
}
return locationHierarchy;
}

public List<LocationHierarchy> getLocationHierarchies(
List<String> locationIds,
List<String> preFetchAdminLevels,
List<String> postFetchAdminLevels) {
List<String> postFetchAdminLevels,
Boolean filterInventory) {
return locationIds.parallelStream()
.map(
locationId ->
getLocationHierarchy(
locationId, preFetchAdminLevels, postFetchAdminLevels))
locationId,
preFetchAdminLevels,
postFetchAdminLevels,
filterInventory))
.collect(Collectors.toList());
}

public LocationHierarchy getLocationHierarchyCore(
String locationId,
List<String> preFetchAdminLevels,
List<String> postFetchAdminLevels) {
List<String> postFetchAdminLevels,
Boolean filterInventory) {
Location location = getLocationById(locationId);

LocationHierarchyTree locationHierarchyTree = new LocationHierarchyTree();
LocationHierarchy locationHierarchy = new LocationHierarchy();
if (location != null) {
logger.info("Building Location Hierarchy of Location Id : {}", locationId);

List<Location> descendants = getDescendants(locationId, location, preFetchAdminLevels);
if (filterInventory) {
descendants = filterLocationsByInventory(descendants);
}
locationHierarchyTree.buildTreeFromList(
filterLocationsByAdminLevels(
getDescendants(locationId, location, preFetchAdminLevels),
postFetchAdminLevels));
filterLocationsByAdminLevels(descendants, postFetchAdminLevels));
StringType locationIdString = new StringType().setId(locationId).getIdElement();
locationHierarchy.setLocationId(locationIdString);
locationHierarchy.setId(LOCATION_RESOURCE + locationId);
Expand All @@ -120,7 +131,8 @@ public List<Location> getLocationHierarchyLocations(
String locationId,
Location parentLocation,
List<String> preFetchAdminLevels,
List<String> postFetchAdminLevels) {
List<String> postFetchAdminLevels,
Boolean filterInventory) {
List<Location> descendants;

if (CacheHelper.INSTANCE.skipCache()) {
Expand All @@ -131,6 +143,9 @@ public List<Location> getLocationHierarchyLocations(
locationId,
key -> getDescendants(locationId, parentLocation, preFetchAdminLevels));
}
if (filterInventory) {
descendants = filterLocationsByInventory(descendants);
}
return filterLocationsByAdminLevels(descendants, postFetchAdminLevels);
}

Expand Down Expand Up @@ -199,18 +214,20 @@ public List<Location> getDescendants(
public Bundle handleIdentifierRequest(HttpServletRequest request, String identifier) {
String administrativeLevelMin = request.getParameter(Constants.MIN_ADMIN_LEVEL);
String administrativeLevelMax = request.getParameter(Constants.MAX_ADMIN_LEVEL);
String mode = request.getParameter(Constants.MODE);
Boolean filterInventory = Boolean.valueOf(request.getParameter(Constants.FILTER_INVENTORY));
List<String> preFetchAdminLevels =
generateAdminLevels(
String.valueOf(Constants.DEFAULT_MIN_ADMIN_LEVEL), administrativeLevelMax);
List<String> postFetchAdminLevels =
generateAdminLevels(administrativeLevelMin, administrativeLevelMax);
String mode = request.getParameter(Constants.MODE);
if (Constants.LIST.equals(mode)) {
List<String> locationIds = Collections.singletonList(identifier);
return getPaginatedLocations(request, locationIds);
} else {
LocationHierarchy locationHierarchy =
getLocationHierarchy(identifier, preFetchAdminLevels, postFetchAdminLevels);
getLocationHierarchy(
identifier, preFetchAdminLevels, postFetchAdminLevels, filterInventory);
return Utils.createBundle(Collections.singletonList(locationHierarchy));
}
}
Expand All @@ -223,6 +240,7 @@ public Bundle handleNonIdentifierRequest(
String syncLocationsParam = request.getParameter(Constants.SYNC_LOCATIONS_SEARCH_PARAM);
String administrativeLevelMin = request.getParameter(Constants.MIN_ADMIN_LEVEL);
String administrativeLevelMax = request.getParameter(Constants.MAX_ADMIN_LEVEL);
Boolean filterInventory = Boolean.valueOf(request.getParameter(Constants.FILTER_INVENTORY));
List<String> preFetchAdminLevels =
generateAdminLevels(
String.valueOf(Constants.DEFAULT_MIN_ADMIN_LEVEL), administrativeLevelMax);
Expand Down Expand Up @@ -259,7 +277,10 @@ public Bundle handleNonIdentifierRequest(
&& !selectedSyncLocations.isEmpty()) {
List<LocationHierarchy> locationHierarchies =
getLocationHierarchies(
selectedSyncLocations, preFetchAdminLevels, postFetchAdminLevels);
selectedSyncLocations,
preFetchAdminLevels,
postFetchAdminLevels,
filterInventory);
List<Resource> resourceList =
locationHierarchies.stream()
.map(locationHierarchy -> (Resource) locationHierarchy)
Expand Down Expand Up @@ -330,6 +351,7 @@ public Bundle getPaginatedLocations(HttpServletRequest request, List<String> loc
String pageNumber = request.getParameter(Constants.PAGINATION_PAGE_NUMBER);
String administrativeLevelMin = request.getParameter(Constants.MIN_ADMIN_LEVEL);
String administrativeLevelMax = request.getParameter(Constants.MAX_ADMIN_LEVEL);
Boolean filterInventory = Boolean.valueOf(request.getParameter(Constants.FILTER_INVENTORY));
List<String> preFetchAdminLevels =
generateAdminLevels(
String.valueOf(Constants.DEFAULT_MIN_ADMIN_LEVEL), administrativeLevelMax);
Expand All @@ -353,7 +375,11 @@ public Bundle getPaginatedLocations(HttpServletRequest request, List<String> loc
Location parentLocation = getLocationById(identifier);
List<Location> locations =
getLocationHierarchyLocations(
identifier, parentLocation, preFetchAdminLevels, postFetchAdminLevels);
identifier,
parentLocation,
preFetchAdminLevels,
postFetchAdminLevels,
filterInventory);
resourceLocations.addAll(locations);
}
int totalEntries = resourceLocations.size();
Expand Down Expand Up @@ -435,4 +461,34 @@ public List<Location> filterLocationsByAdminLevels(
}
return allLocations;
}

public List<Location> filterLocationsByInventory(List<Location> locations) {
List<Location> filteredLocations = Collections.synchronizedList(new ArrayList<>());

locations.parallelStream()
.forEach(
location -> {
String locationId = location.getIdElement().getIdPart();
String locationReference =
Constants.SyncStrategy.LOCATION
+ Constants.FORWARD_SLASH
+ locationId;

Bundle listBundle =
getFhirClientForR4()
.search()
.forResource(ListResource.class)
.where(
new ReferenceClientParam(Constants.SUBJECT)
.hasId(locationReference))
.usingStyle(SearchStyleEnum.POST)
.returnBundle(Bundle.class)
.execute();

if (listBundle != null && !listBundle.getEntry().isEmpty()) {
filteredLocations.add(location);
}
});
return filteredLocations;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -573,7 +573,8 @@ public static List<LocationHierarchy> getLocationsHierarchy(List<String> locatio
.map(
locationsIdentifier ->
new LocationHierarchyEndpointHelper(r4FHIRClient)
.getLocationHierarchy(locationsIdentifier, null, null))
.getLocationHierarchy(
locationsIdentifier, null, null, false))
.filter(
locationHierarchy ->
!org.smartregister.utils.Constants.LOCATION_RESOURCE_NOT_FOUND
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.hl7.fhir.r4.model.CodeableConcept;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.Identifier;
import org.hl7.fhir.r4.model.ListResource;
import org.hl7.fhir.r4.model.Location;
import org.junit.Assert;
import org.junit.Before;
Expand Down Expand Up @@ -54,7 +55,8 @@ public void testGetLocationHierarchyNotFound() {
.when(client)
.fetchResourceFromUrl(any(), any());
LocationHierarchy locationHierarchy =
locationHierarchyEndpointHelper.getLocationHierarchy("non-existent", null, null);
locationHierarchyEndpointHelper.getLocationHierarchy(
"non-existent", null, null, false);
assertEquals(
org.smartregister.utils.Constants.LOCATION_RESOURCE_NOT_FOUND,
locationHierarchy.getId());
Expand All @@ -69,7 +71,7 @@ public void testGetLocationHierarchyFound() {
.when(client)
.fetchResourceFromUrl(Location.class, "Location/12345");
LocationHierarchy locationHierarchy =
locationHierarchyEndpointHelper.getLocationHierarchy("12345", null, null);
locationHierarchyEndpointHelper.getLocationHierarchy("12345", null, null, false);
assertEquals("Location Resource : 12345", locationHierarchy.getId());
}

Expand Down Expand Up @@ -105,7 +107,7 @@ public void testGetPaginatedLocationsPaginatesLocations() {
.filterLocationsByAdminLevels(locations, adminLevels);
Mockito.doReturn(locations)
.when(mockLocationHierarchyEndpointHelper)
.getLocationHierarchyLocations("12345", null, adminLevels, adminLevels);
.getLocationHierarchyLocations("12345", null, adminLevels, adminLevels, false);

Bundle resultBundle =
mockLocationHierarchyEndpointHelper.getPaginatedLocations(request, locationIds);
Expand Down Expand Up @@ -181,7 +183,11 @@ public void testHandleNonIdentifierRequestListModePaginatesLocations() {
Mockito.doReturn(locations)
.when(mockLocationHierarchyEndpointHelper)
.getLocationHierarchyLocations(
Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any());
Mockito.anyString(),
Mockito.any(),
Mockito.any(),
Mockito.any(),
Mockito.any());
Mockito.doReturn(Constants.SyncStrategy.RELATED_ENTITY_LOCATION)
.when(mockLocationHierarchyEndpointHelper)
.getSyncStrategyByAppId(Mockito.any());
Expand Down Expand Up @@ -316,6 +322,56 @@ public void testFilterLocationsByAdminLevelsWithNullAdminLevelsDoesNotFilter() {
Assert.assertEquals("4", filteredLocations.get(4).getId());
}

@Test
public void testFilterLocationsByInventoryWithInventory() {
IUntypedQuery<IBaseBundle> untypedQueryMock = mock(IUntypedQuery.class);
IQuery<IBaseBundle> queryMock = mock(IQuery.class);

Bundle bundleWithInventory = new Bundle();
List<Bundle.BundleEntryComponent> entriesWithInventory = new ArrayList<>();

ListResource resource1 = new ListResource();
resource1.setId("1");
entriesWithInventory.add(new Bundle.BundleEntryComponent().setResource(resource1));
bundleWithInventory.setEntry(entriesWithInventory);

Mockito.doReturn(untypedQueryMock).when(client).search();
Mockito.doReturn(queryMock).when(untypedQueryMock).forResource(ListResource.class);
Mockito.doReturn(queryMock).when(queryMock).where(any(ICriterion.class));
Mockito.doReturn(queryMock).when(queryMock).usingStyle(SearchStyleEnum.POST);
Mockito.doReturn(queryMock).when(queryMock).returnBundle(Bundle.class);
Mockito.doReturn(bundleWithInventory).when(queryMock).execute();

List<Location> locations = createLocationList(5, true);
List<Location> filteredLocations =
locationHierarchyEndpointHelper.filterLocationsByInventory(locations);

Assert.assertNotNull(filteredLocations);
Assert.assertEquals(5, filteredLocations.size());
}

@Test
public void testFilterLocationsByInventoryNoInventory() {
IUntypedQuery<IBaseBundle> untypedQueryMock = mock(IUntypedQuery.class);
IQuery<IBaseBundle> queryMock = mock(IQuery.class);

Bundle bundleWithInventory = new Bundle();

Mockito.doReturn(untypedQueryMock).when(client).search();
Mockito.doReturn(queryMock).when(untypedQueryMock).forResource(ListResource.class);
Mockito.doReturn(queryMock).when(queryMock).where(any(ICriterion.class));
Mockito.doReturn(queryMock).when(queryMock).usingStyle(SearchStyleEnum.POST);
Mockito.doReturn(queryMock).when(queryMock).returnBundle(Bundle.class);
Mockito.doReturn(bundleWithInventory).when(queryMock).execute();

List<Location> locations = createLocationList(5, true);
List<Location> filteredLocations =
locationHierarchyEndpointHelper.filterLocationsByInventory(locations);

Assert.assertNotNull(filteredLocations);
Assert.assertEquals(0, filteredLocations.size());
}

private Bundle getLocationBundle() {
Bundle bundleLocation = new Bundle();
bundleLocation.setId("Location/1234");
Expand Down

0 comments on commit 601c62f

Please sign in to comment.