diff --git a/exec/pom.xml b/exec/pom.xml
index ee38ce5d..ad60b973 100755
--- a/exec/pom.xml
+++ b/exec/pom.xml
@@ -21,7 +21,7 @@
com.google.fhir.gateway
fhir-gateway
- 0.1.26
+ 0.1.27
exec
diff --git a/plugins/pom.xml b/plugins/pom.xml
index ac118be3..d4288199 100755
--- a/plugins/pom.xml
+++ b/plugins/pom.xml
@@ -23,7 +23,7 @@
implementations do not have to do this; they can redeclare those deps. -->
com.google.fhir.gateway
fhir-gateway
- 0.1.26
+ 0.1.27
plugins
diff --git a/plugins/src/main/java/com/google/fhir/gateway/plugin/OpenSRPHelper.java b/plugins/src/main/java/com/google/fhir/gateway/plugin/OpenSRPHelper.java
new file mode 100644
index 00000000..f3841772
--- /dev/null
+++ b/plugins/src/main/java/com/google/fhir/gateway/plugin/OpenSRPHelper.java
@@ -0,0 +1,477 @@
+package com.google.fhir.gateway.plugin;
+
+import static org.smartregister.utils.Constants.EMPTY_STRING;
+
+import ca.uhn.fhir.rest.client.api.IGenericClient;
+import ca.uhn.fhir.rest.gclient.ReferenceClientParam;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.apache.commons.lang3.StringUtils;
+import org.hl7.fhir.instance.model.api.IBaseResource;
+import org.hl7.fhir.r4.model.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.smartregister.model.location.LocationHierarchy;
+import org.smartregister.model.location.ParentChildrenMap;
+import org.smartregister.model.practitioner.FhirPractitionerDetails;
+import org.smartregister.model.practitioner.PractitionerDetails;
+import org.smartregister.utils.Constants;
+import org.springframework.lang.Nullable;
+
+public class OpenSRPHelper {
+ private static final Logger logger = LoggerFactory.getLogger(OpenSRPHelper.class);
+ public static final String PRACTITIONER_GROUP_CODE = "405623001";
+ public static final String HTTP_SNOMED_INFO_SCT = "http://snomed.info/sct";
+ public static final Bundle EMPTY_BUNDLE = new Bundle();
+ private IGenericClient r4FHIRClient;
+
+ public OpenSRPHelper(IGenericClient fhirClient) {
+ this.r4FHIRClient = fhirClient;
+ }
+
+ private IGenericClient getFhirClientForR4() {
+ return r4FHIRClient;
+ }
+
+ public PractitionerDetails getPractitionerDetailsByKeycloakId(String keycloakUUID) {
+ PractitionerDetails practitionerDetails = new PractitionerDetails();
+
+ logger.info("Searching for practitioner with identifier: " + keycloakUUID);
+ IBaseResource practitioner = getPractitionerByIdentifier(keycloakUUID);
+ String practitionerId = EMPTY_STRING;
+
+ if (practitioner.getIdElement() != null && practitioner.getIdElement().getIdPart() != null) {
+ practitionerId = practitioner.getIdElement().getIdPart();
+ }
+
+ if (StringUtils.isNotBlank(practitionerId)) {
+
+ practitionerDetails = getPractitionerDetailsByPractitionerId(practitionerId);
+
+ } else {
+ logger.error("Practitioner with KC identifier: " + keycloakUUID + " not found");
+ practitionerDetails.setId(Constants.PRACTITIONER_NOT_FOUND);
+ }
+
+ return practitionerDetails;
+ }
+
+ public Bundle getSupervisorPractitionerDetailsByKeycloakId(String keycloakUUID) {
+ Bundle bundle = new Bundle();
+
+ logger.info("Searching for practitioner with identifier: " + keycloakUUID);
+ IBaseResource practitioner = getPractitionerByIdentifier(keycloakUUID);
+ String practitionerId = EMPTY_STRING;
+
+ if (practitioner.getIdElement() != null && practitioner.getIdElement().getIdPart() != null) {
+ practitionerId = practitioner.getIdElement().getIdPart();
+ }
+
+ if (StringUtils.isNotBlank(practitionerId)) {
+
+ bundle = getAttributedPractitionerDetailsByPractitionerId(practitionerId);
+
+ } else {
+ logger.error("Practitioner with KC identifier: " + keycloakUUID + " not found");
+ }
+
+ return bundle;
+ }
+
+ private Bundle getAttributedPractitionerDetailsByPractitionerId(String practitionerId) {
+ Bundle responseBundle = new Bundle();
+ List attributedPractitionerIds = new ArrayList<>();
+ PractitionerDetails practitionerDetails =
+ getPractitionerDetailsByPractitionerId(practitionerId);
+
+ List careTeamList = practitionerDetails.getFhirPractitionerDetails().getCareTeams();
+ // Get other guys.
+
+ List careTeamManagingOrganizationIds =
+ getManagingOrganizationsOfCareTeamIds(careTeamList);
+ List supervisorCareTeamOrganizationLocationIds =
+ getLocationIdentifiersByOrganizationIds(careTeamManagingOrganizationIds);
+ List officialLocationIds =
+ getOfficialLocationIdentifiersByLocationIds(supervisorCareTeamOrganizationLocationIds);
+ List locationHierarchies =
+ getLocationsHierarchyByOfficialLocationIdentifiers(officialLocationIds);
+ List parentChildrenList =
+ locationHierarchies.stream()
+ .flatMap(
+ locationHierarchy ->
+ locationHierarchy
+ .getLocationHierarchyTree()
+ .getLocationsHierarchy()
+ .getParentChildren()
+ .stream())
+ .collect(Collectors.toList());
+ List attributedLocationsList =
+ parentChildrenList.stream()
+ .flatMap(parentChildren -> parentChildren.children().stream())
+ .map(it -> it.getName())
+ .collect(Collectors.toList());
+ List attributedOrganizationIds =
+ getOrganizationIdsByLocationIds(attributedLocationsList);
+
+ // Get care teams by organization Ids
+ List attributedCareTeams = getCareTeamsByOrganizationIds(attributedOrganizationIds);
+ careTeamList.addAll(attributedCareTeams);
+
+ for (CareTeam careTeam : careTeamList) {
+ // Add current supervisor practitioners
+ attributedPractitionerIds.addAll(
+ careTeam.getParticipant().stream()
+ .filter(
+ it ->
+ it.hasMember()
+ && it.getMember()
+ .getReference()
+ .startsWith(Enumerations.ResourceType.PRACTITIONER.toCode()))
+ .map(it -> getReferenceIDPart(it.getMember().getReference()))
+ .collect(Collectors.toList()));
+ }
+
+ List bundleEntryComponentList = new ArrayList<>();
+
+ for (String attributedPractitionerId : attributedPractitionerIds) {
+ bundleEntryComponentList.add(
+ new Bundle.BundleEntryComponent()
+ .setResource(getPractitionerDetailsByPractitionerId(attributedPractitionerId)));
+ }
+
+ responseBundle.setEntry(bundleEntryComponentList);
+ responseBundle.setTotal(bundleEntryComponentList.size());
+ return responseBundle;
+ }
+
+ private List getOrganizationIdsByLocationIds(List attributedLocationsList) {
+ if (attributedLocationsList == null || attributedLocationsList.isEmpty()) {
+ return new ArrayList<>();
+ }
+
+ Bundle organizationAffiliationsBundle =
+ getFhirClientForR4()
+ .search()
+ .forResource(OrganizationAffiliation.class)
+ .where(OrganizationAffiliation.LOCATION.hasAnyOfIds(attributedLocationsList))
+ .returnBundle(Bundle.class)
+ .execute();
+
+ return organizationAffiliationsBundle.getEntry().stream()
+ .map(
+ bundleEntryComponent ->
+ getReferenceIDPart(
+ ((OrganizationAffiliation) bundleEntryComponent.getResource())
+ .getOrganization()
+ .getReference()))
+ .collect(Collectors.toList());
+ }
+
+ private PractitionerDetails getPractitionerDetailsByPractitionerId(String practitionerId) {
+
+ PractitionerDetails practitionerDetails = new PractitionerDetails();
+ FhirPractitionerDetails fhirPractitionerDetails = new FhirPractitionerDetails();
+
+ logger.info("Searching for care teams for practitioner with id: " + practitionerId);
+ Bundle careTeams = getCareTeams(practitionerId);
+ List careTeamsList = mapBundleToCareTeams(careTeams);
+ fhirPractitionerDetails.setCareTeams(careTeamsList);
+
+ StringType practitionerIdString = new StringType(practitionerId);
+ fhirPractitionerDetails.setPractitionerId(practitionerIdString);
+
+ logger.info("Searching for Organizations tied with CareTeams: ");
+ List careTeamManagingOrganizationIds =
+ getManagingOrganizationsOfCareTeamIds(careTeamsList);
+
+ Bundle careTeamManagingOrganizations = getOrganizationsById(careTeamManagingOrganizationIds);
+ logger.info("Managing Organization are fetched");
+
+ List managingOrganizationTeams =
+ mapBundleToOrganizations(careTeamManagingOrganizations);
+
+ logger.info("Searching for organizations of practitioner with id: " + practitionerId);
+
+ List practitionerRoleList =
+ getPractitionerRolesByPractitionerId(practitionerId);
+ logger.info("Practitioner Roles are fetched");
+
+ List practitionerOrganizationIds =
+ getOrganizationIdsByPractitionerRoles(practitionerRoleList);
+
+ Bundle practitionerOrganizations = getOrganizationsById(practitionerOrganizationIds);
+
+ List teams = mapBundleToOrganizations(practitionerOrganizations);
+ // TODO Fix Distinct
+ List bothOrganizations =
+ Stream.concat(managingOrganizationTeams.stream(), teams.stream())
+ .distinct()
+ .collect(Collectors.toList());
+
+ fhirPractitionerDetails.setOrganizations(bothOrganizations);
+
+ fhirPractitionerDetails.setPractitionerRoles(practitionerRoleList);
+
+ Bundle groupsBundle = getGroupsAssignedToPractitioner(practitionerId);
+ logger.info("Groups are fetched");
+
+ List groupsList = mapBundleToGroups(groupsBundle);
+ fhirPractitionerDetails.setGroups(groupsList);
+ fhirPractitionerDetails.setId(practitionerIdString.getValue());
+
+ logger.info("Searching for locations by organizations");
+
+ List locationIds =
+ getLocationIdentifiersByOrganizationIds(
+ Stream.concat(
+ careTeamManagingOrganizationIds.stream(), practitionerOrganizationIds.stream())
+ .distinct()
+ .collect(Collectors.toList()));
+
+ List locationsIdentifiers =
+ getOfficialLocationIdentifiersByLocationIds(
+ locationIds); // TODO Investigate why the Location ID and official identifiers are
+ // different
+
+ logger.info("Searching for location hierarchy list by locations identifiers");
+ List locationHierarchyList =
+ getLocationsHierarchyByOfficialLocationIdentifiers(locationsIdentifiers);
+ fhirPractitionerDetails.setLocationHierarchyList(locationHierarchyList);
+
+ logger.info("Searching for locations by ids");
+ List locationsList = getLocationsByIds(locationIds);
+ fhirPractitionerDetails.setLocations(locationsList);
+
+ practitionerDetails.setId(practitionerId);
+ practitionerDetails.setFhirPractitionerDetails(fhirPractitionerDetails);
+
+ return practitionerDetails;
+ }
+
+ private List mapBundleToOrganizations(Bundle organizationBundle) {
+ return organizationBundle.getEntry().stream()
+ .map(bundleEntryComponent -> (Organization) bundleEntryComponent.getResource())
+ .collect(Collectors.toList());
+ }
+
+ private Bundle getGroupsAssignedToPractitioner(String practitionerId) {
+ return getFhirClientForR4()
+ .search()
+ .forResource(Group.class)
+ .where(Group.MEMBER.hasId(practitionerId))
+ .where(Group.CODE.exactly().systemAndCode(HTTP_SNOMED_INFO_SCT, PRACTITIONER_GROUP_CODE))
+ .returnBundle(Bundle.class)
+ .execute();
+ }
+
+ public static Predicate distinctByKey(Function super T, ?> keyExtractor) {
+ Set