Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor to Optimize processing of the Location Hierarchy #14

Merged
merged 5 commits into from
Sep 28, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 All @@ -58,9 +58,12 @@ public void addLocation(Location l) {
* @param locations
*/
public void buildTreeFromList(List<Location> locations) {
long start = System.currentTimeMillis();
for (Location location : locations) {
addLocation(location);
}
logger.info(String.format("LocationHierarchyTree.buildTreeFromList took : %d ms", System.currentTimeMillis() - start));

}

public Tree getLocationsHierarchy() {
Expand Down
170 changes: 73 additions & 97 deletions src/main/java/org/smartregister/model/location/Tree.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,17 @@
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.Objects;
import java.util.Optional;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Logger;

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

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

Expand All @@ -48,7 +52,7 @@ public class Tree extends Type implements ICompositeType {
summary = false)
private List<ParentChildrenMap> parentChildren;

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

public SingleTreeNode getTree() {
return listOfNodes;
Expand All @@ -59,148 +63,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);

if (!setParentChildMap) {
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.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 {
logger.severe("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 +195,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);
}
}
Loading
Loading