Skip to content

Commit

Permalink
Merge pull request #100 from onaio/lastupdated_filter_90
Browse files Browse the repository at this point in the history
Lastupdated filter 90
  • Loading branch information
pld authored Nov 15, 2024
2 parents b615556 + ada1ddd commit d952d39
Show file tree
Hide file tree
Showing 8 changed files with 203 additions and 104 deletions.
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,29 @@ Example:
[GET] /LocationHierarchy?_id=<some-location-id>&filterInventory=true&_count=<page-size>&_page=<page-number>&_sort=<some-sort>
```

##### LastUpdated Filters

The `LocationHierarchy` endpoint supports filtering by the lastUpdated timestamp
of locations. This filter allows users to retrieve locations based on the last
modification date, making it useful for tracking recent updates or syncing data
changes over time.

Behavior based on the lastUpdated parameter:

- `_lastUpdated` Not Defined: The endpoint will include all locations in the
response, regardless of when they were last modified.
- `_lastUpdated` Defined: The response will include only those locations that
were updated on or after the specified timestamp.

Note: This filter only works when in list mode i.e `mode=list` is set as one of
the parameters

Example:

```
[GET] /LocationHierarchy?_id=<some-location-id>&mode=list&_lastUpdated=2024-09-22T15%3A13%3A53.014%2B00%3A00&_count=<page-size>&_page=<page-number>&_sort=<some-sort>
```

#### Important Note:

Developers, please update your client applications accordingly to accommodate
Expand Down
4 changes: 2 additions & 2 deletions exec/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>org.smartregister</groupId>
<artifactId>opensrp-gateway-plugin</artifactId>
<version>2.2.0</version>
<version>2.2.1</version>
</parent>

<artifactId>exec</artifactId>
Expand Down Expand Up @@ -70,7 +70,7 @@
<dependency>
<groupId>org.smartregister</groupId>
<artifactId>plugins</artifactId>
<version>2.2.0</version>
<version>2.2.1</version>
</dependency>

<dependency>
Expand Down
2 changes: 1 addition & 1 deletion plugins/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>org.smartregister</groupId>
<artifactId>opensrp-gateway-plugin</artifactId>
<version>2.2.0</version>
<version>2.2.1</version>
</parent>

<artifactId>plugins</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public class Constants {
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 String LAST_UPDATED = "_lastUpdated";
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 Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@
import static org.smartregister.utils.Constants.LOCATION_RESOURCE;
import static org.smartregister.utils.Constants.LOCATION_RESOURCE_NOT_FOUND;

import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -56,33 +59,23 @@ public LocationHierarchy getLocationHierarchy(
String locationId,
List<String> preFetchAdminLevels,
List<String> postFetchAdminLevels,
Boolean filterInventory) {
LocationHierarchy locationHierarchy;

if (CacheHelper.INSTANCE.skipCache()) {
locationHierarchy =
getLocationHierarchyCore(
locationId, preFetchAdminLevels, postFetchAdminLevels, filterInventory);
} else {
locationHierarchy =
(LocationHierarchy)
CacheHelper.INSTANCE.resourceCache.get(
locationId,
key ->
getLocationHierarchyCore(
locationId,
preFetchAdminLevels,
postFetchAdminLevels,
filterInventory));
}
return locationHierarchy;
Boolean filterInventory,
String lastUpdated) {
// TODO: implement correct caching
return getLocationHierarchyCore(
locationId,
preFetchAdminLevels,
postFetchAdminLevels,
filterInventory,
lastUpdated);
}

public List<LocationHierarchy> getLocationHierarchies(
List<String> locationIds,
List<String> preFetchAdminLevels,
List<String> postFetchAdminLevels,
Boolean filterInventory) {
Boolean filterInventory,
String lastUpdated) {

return locationIds.parallelStream()
.map(
Expand All @@ -91,15 +84,17 @@ public List<LocationHierarchy> getLocationHierarchies(
locationId,
preFetchAdminLevels,
postFetchAdminLevels,
filterInventory))
filterInventory,
lastUpdated))
.collect(Collectors.toList());
}

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

LocationHierarchyTree locationHierarchyTree = new LocationHierarchyTree();
Expand All @@ -108,11 +103,12 @@ public LocationHierarchy getLocationHierarchyCore(
logger.info("Building Location Hierarchy of Location Id : {}", locationId);

List<Location> descendants = getDescendants(locationId, location, preFetchAdminLevels);
if (filterInventory) {
descendants = filterLocationsByInventory(descendants);
}
locationHierarchyTree.buildTreeFromList(
filterLocationsByAdminLevels(descendants, postFetchAdminLevels));

descendants =
postFetchFilters(
descendants, postFetchAdminLevels, filterInventory, lastUpdated);

locationHierarchyTree.buildTreeFromList(descendants);
StringType locationIdString = new StringType().setId(locationId).getIdElement();
locationHierarchy.setLocationId(locationIdString);
locationHierarchy.setId(LOCATION_RESOURCE + locationId);
Expand All @@ -130,7 +126,8 @@ public List<Location> getLocationHierarchyLocations(
Location parentLocation,
List<String> preFetchAdminLevels,
List<String> postFetchAdminLevels,
Boolean filterInventory) {
Boolean filterInventory,
String lastUpdated) {
List<Location> descendants;

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

public List<Location> getDescendants(
Expand Down Expand Up @@ -235,6 +231,7 @@ public Bundle handleIdentifierRequest(HttpServletRequest request, String identif
String administrativeLevelMax = request.getParameter(Constants.MAX_ADMIN_LEVEL);
String mode = request.getParameter(Constants.MODE);
Boolean filterInventory = Boolean.valueOf(request.getParameter(Constants.FILTER_INVENTORY));
String lastUpdated = "";
List<String> preFetchAdminLevels =
generateAdminLevels(
String.valueOf(Constants.DEFAULT_MIN_ADMIN_LEVEL), administrativeLevelMax);
Expand All @@ -246,7 +243,11 @@ public Bundle handleIdentifierRequest(HttpServletRequest request, String identif
} else {
LocationHierarchy locationHierarchy =
getLocationHierarchy(
identifier, preFetchAdminLevels, postFetchAdminLevels, filterInventory);
identifier,
preFetchAdminLevels,
postFetchAdminLevels,
filterInventory,
lastUpdated);
return Utils.createBundle(Collections.singletonList(locationHierarchy));
}
}
Expand All @@ -270,6 +271,7 @@ public Bundle handleNonIdentifierRequest(
List<String> userRoles = JwtUtils.getUserRolesFromJWT(verifiedJwt);
String applicationId = JwtUtils.getApplicationIdFromJWT(verifiedJwt);
String syncStrategy = getSyncStrategyByAppId(applicationId);
String lastUpdated = "";

if (Constants.LIST.equals(mode)) {
if (Constants.SyncStrategy.RELATED_ENTITY_LOCATION.equalsIgnoreCase(syncStrategy)
Expand All @@ -293,7 +295,8 @@ public Bundle handleNonIdentifierRequest(
selectedSyncLocations,
preFetchAdminLevels,
postFetchAdminLevels,
filterInventory);
filterInventory,
lastUpdated);
List<Resource> resourceList =
locationHierarchies.stream()
.map(locationHierarchy -> (Resource) locationHierarchy)
Expand All @@ -308,7 +311,8 @@ public Bundle handleNonIdentifierRequest(
locationIds,
preFetchAdminLevels,
postFetchAdminLevels,
filterInventory);
filterInventory,
lastUpdated);
List<Resource> resourceList =
locationHierarchies.stream()
.map(locationHierarchy -> (Resource) locationHierarchy)
Expand Down Expand Up @@ -369,6 +373,7 @@ public Bundle getPaginatedLocations(HttpServletRequest request, List<String> loc
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));
String lastUpdated = request.getParameter(Constants.LAST_UPDATED);
List<String> preFetchAdminLevels =
generateAdminLevels(
String.valueOf(Constants.DEFAULT_MIN_ADMIN_LEVEL), administrativeLevelMax);
Expand Down Expand Up @@ -396,7 +401,8 @@ public Bundle getPaginatedLocations(HttpServletRequest request, List<String> loc
getLocationById(identifier),
preFetchAdminLevels,
postFetchAdminLevels,
filterInventory)
filterInventory,
lastUpdated)
.stream())
.collect(Collectors.toList());
int totalEntries = resourceLocations.size();
Expand Down Expand Up @@ -458,56 +464,57 @@ public List<String> generateAdminLevels(
return adminLevels;
}

public List<Location> filterLocationsByAdminLevels(
List<Location> locations, List<String> postFetchAdminLevels) {

if (postFetchAdminLevels == null || postFetchAdminLevels.isEmpty()) {
return locations;
}

public List<Location> postFetchFilters(
List<Location> locations,
List<String> postFetchAdminLevels,
boolean filterByInventory,
String lastUpdated) {
return locations.stream()
.filter(
location ->
location.getType().stream()
.flatMap(
codeableConcept ->
codeableConcept.getCoding().stream())
.anyMatch(
coding ->
Constants.DEFAULT_ADMIN_LEVEL_TYPE_URL
.equals(coding.getSystem())
&& postFetchAdminLevels.contains(
coding.getCode())))
postFetchAdminLevels == null
|| postFetchAdminLevels.isEmpty()
|| adminLevelFilter(location, postFetchAdminLevels))
.filter(
location ->
lastUpdated == null
|| lastUpdated.isBlank()
|| lastUpdatedFilter(location, lastUpdated))
.filter(location -> !filterByInventory || inventoryFilter(location))
.collect(Collectors.toList());
}

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;
public boolean adminLevelFilter(Location location, List<String> postFetchAdminLevels) {
return location.getType().stream()
.flatMap(codeableConcept -> codeableConcept.getCoding().stream())
.anyMatch(
coding ->
Constants.DEFAULT_ADMIN_LEVEL_TYPE_URL.equals(coding.getSystem())
&& postFetchAdminLevels.contains(coding.getCode()));
}

public boolean lastUpdatedFilter(Location location, String lastUpdated) {
Date locationlastUpdated = location.getMeta().getLastUpdated();
OffsetDateTime locationLastUpdated =
locationlastUpdated.toInstant().atOffset(ZoneOffset.UTC);
return locationLastUpdated.isAfter(OffsetDateTime.parse(lastUpdated))
|| locationLastUpdated.isEqual(OffsetDateTime.parse(lastUpdated));
}

public boolean inventoryFilter(Location 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();

return listBundle != null && !listBundle.getEntry().isEmpty();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -657,7 +657,7 @@ public static List<LocationHierarchy> getLocationsHierarchy(List<String> locatio
locationsIdentifier ->
new LocationHierarchyEndpointHelper(r4FHIRClient)
.getLocationHierarchy(
locationsIdentifier, null, null, false))
locationsIdentifier, null, null, false, ""))
.filter(
locationHierarchy ->
!org.smartregister.utils.Constants.LOCATION_RESOURCE_NOT_FOUND
Expand Down
Loading

0 comments on commit d952d39

Please sign in to comment.