Skip to content

Commit

Permalink
Refactor to Optimize processing of the Location Hierarchy (#14)
Browse files Browse the repository at this point in the history
* Perform parallel Processing of Location Hierarchy

* Refactor location hieranchy processing for optimization

* Optimization PR Clean up

* Clean up
  • Loading branch information
ndegwamartin authored Sep 28, 2023
1 parent b6ce179 commit 6e60aba
Show file tree
Hide file tree
Showing 7 changed files with 197 additions and 165 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>org.smartregister</groupId>
<artifactId>fhir-common-utils</artifactId>
<version>0.0.10-SNAPSHOT</version>
<version>0.0.11-SNAPSHOT</version>

<distributionManagement>
<repository>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,37 +18,37 @@
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
import ca.uhn.fhir.util.ElementUtil;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.ICompositeType;
import org.hl7.fhir.r4.model.Location;
import org.hl7.fhir.r4.model.StringType;
import org.hl7.fhir.r4.model.Type;
import org.apache.commons.lang3.StringUtils;

import java.util.List;
import java.util.logging.Logger;

@DatatypeDef(name = "LocationHierarchyTree")
public class LocationHierarchyTree extends Type implements ICompositeType {

private static final Logger logger = Logger.getLogger(LocationHierarchyTree.class.getSimpleName());
@Child(name = "locationsHierarchy")
private Tree locationsHierarchy;

public LocationHierarchyTree() {
this.locationsHierarchy = new Tree();
}

public void addLocation(Location l) {
public void addLocation(Location location) {
StringType idString = new StringType();
idString.setValue(l.getId());
if (!locationsHierarchy.hasNode(idString.getValue())) {
if (l.getPartOf() == null || StringUtils.isEmpty(l.getPartOf().getReference())) {
locationsHierarchy.addNode(idString.getValue(), l.getName(), l, null);
} else {
// get Parent Location
StringType parentId = new StringType();
parentId.setValue(l.getPartOf().getReference());
locationsHierarchy.addNode(
idString.getValue(), l.getName(), l, parentId.getValue());
}
idString.setValue(location.getId());
if (location.getPartOf() == null || StringUtils.isEmpty(location.getPartOf().getReference())) {
locationsHierarchy.addNode(idString.getValue(), location.getName(), location, null);
} else {
// get Parent Location
StringType parentId = new StringType();
parentId.setValue(location.getPartOf().getReference());
locationsHierarchy.addNode(
idString.getValue(), location.getName(), location, parentId.getValue());
}
}

Expand Down
171 changes: 72 additions & 99 deletions src/main/java/org/smartregister/model/location/Tree.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,15 @@
import org.hl7.fhir.r4.model.Location;
import org.hl7.fhir.r4.model.StringType;
import org.hl7.fhir.r4.model.Type;
import org.smartregister.utils.Utils;

import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

import static org.smartregister.utils.Constants.SLASH_UNDERSCORE;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicReference;

@DatatypeDef(name = "Tree")
public class Tree extends Type implements ICompositeType {
Expand All @@ -48,8 +51,6 @@ public class Tree extends Type implements ICompositeType {
summary = false)
private List<ParentChildrenMap> parentChildren;

private static Logger logger = Logger.getLogger(Tree.class.getSimpleName());

public SingleTreeNode getTree() {
return listOfNodes;
}
Expand All @@ -59,148 +60,124 @@ public Tree() {
parentChildren = new ArrayList<>();
}

private void addToParentChildRelation(String parent, String id) {
private void addToParentChildRelation(String parentId, String id) {
if (parentChildren == null) {
parentChildren = new ArrayList<>();
}
List<StringType> kids = null;
if (parentChildren != null) {
for (int i = 0; i < parentChildren.size(); i++) {
kids =
parentChildren.get(i) != null
&& parentChildren.get(i).getIdentifier() != null
&& StringUtils.isNotBlank(
parentChildren.get(i).getIdentifier().getValue())
&& parentChildren
.get(i)
.getIdentifier()
.getValue()
.equals(parent)
? parentChildren.get(i).getChildIdentifiers()
: null;
logger.info("Kids are : " + kids);
if (kids != null) {
break;
}
}
parentChildren = new CopyOnWriteArrayList<>();
}

List<StringType> kids = getChildrenIdsByParentId(parentId);

if (kids == null) {
kids = new ArrayList<>();
}
StringType idStringType = new StringType();
String idString = id;
if (idString.contains(SLASH_UNDERSCORE)) {
idString = idString.substring(0, idString.indexOf(SLASH_UNDERSCORE));
}
String idString = Utils.cleanIdString(id);
idStringType.setValue(idString);

StringType parentStringType = new StringType();
parentStringType.setValue(parent);
parentStringType.setValue(parentId);
kids.add(idStringType);
Boolean setParentChildMap = false;
for (int i = 0; i < parentChildren.size(); i++) {
if (parentChildren.get(i) != null
&& parentChildren.get(i).getIdentifier() != null
&& StringUtils.isNotBlank(parentChildren.get(i).getIdentifier().getValue())
&& parentChildren.get(i).getIdentifier().getValue().equals(parent)) {
parentChildren.get(i).setChildIdentifiers(kids);
setParentChildMap = true;
}
}
AtomicReference<Boolean> setParentChildMap = new AtomicReference<>(false);

List<StringType> finalKids = kids;
parentChildren.parallelStream().filter(parentChildrenMap -> parentChildrenMap != null
&& parentChildrenMap.getIdentifier() != null
&& StringUtils.isNotBlank(parentChildrenMap.getIdentifier().getValue())
&& parentChildrenMap.getIdentifier().getValue().equals(parentId)).forEach(innerParentChildrenMap -> {

innerParentChildrenMap.setChildIdentifiers(finalKids);
setParentChildMap.set(true);

if (!setParentChildMap) {
});

if (!setParentChildMap.get()) {
ParentChildrenMap parentChildrenMap = new ParentChildrenMap();
parentChildrenMap.setIdentifier(parentStringType);
parentChildrenMap.setChildIdentifiers(kids);
parentChildren.add(parentChildrenMap);
}
}

private List<StringType> getChildrenIdsByParentId(String parentId) {
Optional<List<StringType>> kidsOptional = parentChildren.parallelStream().filter(parentChildrenMap -> parentChildrenMap != null
&& parentChildrenMap.getIdentifier() != null
&& StringUtils.isNotBlank(parentChildrenMap.getIdentifier().getValue())
&& parentChildrenMap
.getIdentifier()
.getValue()
.equals(parentId)).map(ParentChildrenMap::getChildIdentifiers).filter(Objects::nonNull).findFirst();
return kidsOptional.orElse(null);
}

public void addNode(String id, String label, Location node, String parentId) {
if (listOfNodes == null) {
listOfNodes = new SingleTreeNode();
}

// if node exists we should break since user should write optimized code and also tree can
// not have duplicates
if (hasNode(id)) {
throw new IllegalArgumentException("Node with ID " + id + " already exists in tree");
}
// We only add node if it doesn't already exist, else log as an exception
TreeNode treenode = getNode(id);

TreeNode treeNode = makeNode(id, label, node, parentId);
if (treenode == null) {
TreeNode treeNode = makeNode(id, null, label, node, parentId);

if (parentId != null) {
addToParentChildRelation(parentId, id);
if (parentId != null) {

TreeNode parentNode = getNode(parentId);
addToParentChildRelation(parentId, id);
TreeNode parentNode = getNode(parentId);

// if parent exists add to it otherwise add as root for now
if (parentNode != null) {
parentNode.addChild(treeNode);
} else {
// if no parent exists add it as root node
String idString = (String) id;
if (idString.contains(SLASH_UNDERSCORE)) {
idString = idString.substring(0, idString.indexOf(SLASH_UNDERSCORE));
// if parent exists add to it otherwise add as root for now
if (parentNode != null) {
parentNode.addChild(treeNode);
} else {
// if no parent exists add it as root node
SingleTreeNode singleTreeNode = getSingleTreeNode(id, treeNode);
listOfNodes = singleTreeNode;
}
SingleTreeNode singleTreeNode = new SingleTreeNode();
StringType treeNodeId = new StringType();
treeNodeId.setValue(idString);
singleTreeNode.setTreeNodeId(treeNodeId);
singleTreeNode.setTreeNode(treeNode);
} else {
// if no parent add it as root node
SingleTreeNode singleTreeNode = getSingleTreeNode(id, treeNode);
listOfNodes = singleTreeNode;
}
} else {
// if no parent add it as root node
String idString = id;
if (idString.contains(SLASH_UNDERSCORE)) {
idString = idString.substring(0, idString.indexOf(SLASH_UNDERSCORE));
}

SingleTreeNode singleTreeNode = new SingleTreeNode();
StringType treeNodeId = new StringType();
treeNodeId.setValue(idString);
singleTreeNode.setTreeNodeId(treeNodeId);
singleTreeNode.setTreeNode(treeNode);
listOfNodes = singleTreeNode;
} else {
throw new IllegalArgumentException("Node with ID " + id + " already exists in tree");
}
}

private TreeNode makeNode(String id, String label, Location node, String parentId) {
TreeNode treenode = getNode(id);
private static SingleTreeNode getSingleTreeNode(String id, TreeNode treeNode) {
String idString = id;
idString = Utils.cleanIdString(idString);
SingleTreeNode singleTreeNode = new SingleTreeNode();
StringType treeNodeId = new StringType();
treeNodeId.setValue(idString);
singleTreeNode.setTreeNodeId(treeNodeId);
singleTreeNode.setTreeNode(treeNode);
return singleTreeNode;
}

private TreeNode makeNode(String currentNodeId, TreeNode treenode, String label, Location node, String parentId) {
if (treenode == null) {
treenode = new TreeNode();
StringType nodeId = new StringType();
String idString = (String) id;
if (idString.contains(SLASH_UNDERSCORE)) {
idString = idString.substring(0, idString.indexOf(SLASH_UNDERSCORE));
}
nodeId.setValue((String) idString);
String idString = Utils.cleanIdString(currentNodeId);
nodeId.setValue(idString);
treenode.setNodeId(nodeId);
StringType labelString = new StringType();
labelString.setValue(label);
treenode.setLabel(labelString);
treenode.setNode(node);
StringType parentIdString = new StringType();
String parentIdStringVar = parentId;

if (parentIdStringVar != null && parentIdStringVar.contains(SLASH_UNDERSCORE)) {
parentIdStringVar =
parentIdStringVar.substring(0, parentIdStringVar.indexOf(SLASH_UNDERSCORE));
}
String parentIdStringVar = Utils.cleanIdString(parentId);
parentIdString.setValue(parentIdStringVar);
treenode.setParent(parentIdString);
}
return treenode;
}

@Nullable
public TreeNode getNode(String id) {
// Check if id is any root node
String idString = id;
if (idString != null && idString.contains(SLASH_UNDERSCORE)) {
idString = idString.substring(0, idString.indexOf(SLASH_UNDERSCORE));
}
String idString = Utils.cleanIdString(id);

if (listOfNodes.getTreeNodeId() != null
&& StringUtils.isNotBlank(listOfNodes.getTreeNodeId().getValue())
Expand All @@ -215,10 +192,6 @@ public TreeNode getNode(String id) {
return null;
}

public boolean hasNode(String id) {
return getNode(id) != null;
}

public SingleTreeNode getListOfNodes() {
return listOfNodes;
}
Expand Down
47 changes: 23 additions & 24 deletions src/main/java/org/smartregister/model/location/TreeNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,14 @@
import ca.uhn.fhir.util.ElementUtil;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.ICompositeType;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.Location;
import org.hl7.fhir.r4.model.StringType;
import org.hl7.fhir.r4.model.Type;
import org.smartregister.utils.Utils;

import java.util.ArrayList;
import java.util.List;

import static org.smartregister.utils.Constants.SLASH_UNDERSCORE;

@DatatypeDef(name = "TreeNode")
public class TreeNode extends Type implements ICompositeType {

Expand Down Expand Up @@ -185,30 +186,28 @@ public void addChild(TreeNode node) {
children.add(childTreeNode);
}

public TreeNode findChild(String id) {
String idString = (String) id;
if (idString.contains(SLASH_UNDERSCORE)) {
idString = idString.substring(0, idString.indexOf(SLASH_UNDERSCORE));
}
if (children != null && children.size() > 0) {
for (int i = 0; i < children.size(); i++) {
if (children.get(i) != null) {
for (ChildTreeNode child : children) {
if (child != null
&& child.getChildren() != null
&& child.getChildren().getNodeId() != null
&& StringUtils.isNotBlank(
child.getChildren().getNodeId().getValue())
&& child.getChildren().getNodeId().getValue().equals(idString)) {
return child.getChildren();
} else if (child != null && child != null) {
TreeNode node = child.getChildren().findChild(idString);
if (node != null) return node;
}
}
public TreeNode findChild(String childId) {

String idString = Utils.cleanIdString(childId);
if (children != null && !children.isEmpty()) {
for (ChildTreeNode child : children) {
if (isChildFound(child, idString)) {
return child.getChildren();
} else if (child != null && child.getChildren() != null) {
TreeNode node = child.getChildren().findChild(idString);
if (node != null) return node;
}
}
}
return null;
}

private static boolean isChildFound(ChildTreeNode child, String idString) {
return child != null
&& child.getChildren() != null
&& child.getChildren().getNodeId() != null
&& StringUtils.isNotBlank(
child.getChildren().getNodeId().getValue())
&& child.getChildren().getNodeId().getValue().equals(idString);
}
}
10 changes: 10 additions & 0 deletions src/main/java/org/smartregister/utils/Utils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.smartregister.utils;

import org.apache.commons.lang3.StringUtils;

public class Utils {
public static String cleanIdString(String idString) {
return StringUtils.isNotBlank(idString) && idString.contains(Constants.SLASH_UNDERSCORE) ?
idString.substring(0, idString.indexOf(Constants.SLASH_UNDERSCORE)) : idString;
}
}
Loading

0 comments on commit 6e60aba

Please sign in to comment.