diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..f109e63 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, +and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by +the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all +other entities that control, are controlled by, or are under common +control with that entity. For the purposes of this definition, +"control" means (i) the power, direct or indirect, to cause the +direction or management of such entity, whether by contract or +otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity +exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, +including but not limited to software source code, documentation +source, and configuration files. + +"Object" form shall mean any form resulting from mechanical +transformation or translation of a Source form, including but +not limited to compiled object code, generated documentation, +and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or +Object form, made available under the License, as indicated by a +copyright notice that is included in or attached to the work +(an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object +form, that is based on (or derived from) the Work and for which the +editorial revisions, annotations, elaborations, or other modifications +represent, as a whole, an original work of authorship. For the purposes +of this License, Derivative Works shall not include works that remain +separable from, or merely link (or bind by name) to the interfaces of, +the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including +the original version of the Work and any modifications or additions +to that Work or Derivative Works thereof, that is intentionally +submitted to Licensor for inclusion in the Work by the copyright owner +or by an individual or Legal Entity authorized to submit on behalf of +the copyright owner. For the purposes of this definition, "submitted" +means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, +and issue tracking systems that are managed by, or on behalf of, the +Licensor for the purpose of discussing and improving the Work, but +excluding communication that is conspicuously marked or otherwise +designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity +on behalf of whom a Contribution has been received by Licensor and +subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the +Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +(except as stated in this section) patent license to make, have made, +use, offer to sell, sell, import, and otherwise transfer the Work, +where such license applies only to those patent claims licensable +by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) +with the Work to which such Contribution(s) was submitted. If You +institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work +or a Contribution incorporated within the Work constitutes direct +or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate +as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the +Work or Derivative Works thereof in any medium, with or without +modifications, and in Source or Object form, provided that You +meet the following conditions: + +(a) You must give any other recipients of the Work or +Derivative Works a copy of this License; and + +(b) You must cause any modified files to carry prominent notices +stating that You changed the files; and + +(c) You must retain, in the Source form of any Derivative Works +that You distribute, all copyright, patent, trademark, and +attribution notices from the Source form of the Work, +excluding those notices that do not pertain to any part of +the Derivative Works; and + +(d) If the Work includes a "NOTICE" text file as part of its +distribution, then any Derivative Works that You distribute must +include a readable copy of the attribution notices contained +within such NOTICE file, excluding those notices that do not +pertain to any part of the Derivative Works, in at least one +of the following places: within a NOTICE text file distributed +as part of the Derivative Works; within the Source form or +documentation, if provided along with the Derivative Works; or, +within a display generated by the Derivative Works, if and +wherever such third-party notices normally appear. The contents +of the NOTICE file are for informational purposes only and +do not modify the License. You may add Your own attribution +notices within Derivative Works that You distribute, alongside +or as an addendum to the NOTICE text from the Work, provided +that such additional attribution notices cannot be construed +as modifying the License. + +You may add Your own copyright statement to Your modifications and +may provide additional or different license terms and conditions +for use, reproduction, or distribution of Your modifications, or +for any such Derivative Works as a whole, provided Your use, +reproduction, and distribution of the Work otherwise complies with +the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, +any Contribution intentionally submitted for inclusion in the Work +by You to the Licensor shall be under the terms and conditions of +this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify +the terms of any separate license agreement you may have executed +with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade +names, trademarks, service marks, or product names of the Licensor, +except as required for reasonable and customary use in describing the +origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or +agreed to in writing, Licensor provides the Work (and each +Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied, including, without limitation, any warranties or conditions +of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any +risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, +whether in tort (including negligence), contract, or otherwise, +unless required by applicable law (such as deliberate and grossly +negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, +incidental, or consequential damages of any character arising as a +result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all +other commercial damages or losses), even if such Contributor +has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing +the Work or Derivative Works thereof, You may choose to offer, +and charge a fee for, acceptance of support, warranty, indemnity, +or other liability obligations and/or rights consistent with this +License. However, in accepting such obligations, You may act only +on Your own behalf and on Your sole responsibility, not on behalf +of any other Contributor, and only if You agree to indemnify, +defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason +of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/README.md b/README.md index 2538f06..f8727d2 100755 --- a/README.md +++ b/README.md @@ -1,2 +1,2 @@ # hapi-fhir-opensrp-extensions -This repository holds all the code extensions OpenSRP will build on top of the HAPI FHIR server. +This repository holds all the code extensions OpenSRP will build on top of the HAPI FHIR server. diff --git a/location/.gitignore b/location/.gitignore new file mode 100755 index 0000000..128621a --- /dev/null +++ b/location/.gitignore @@ -0,0 +1,40 @@ +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +# Default ignored files +/shelf/ +/workspace.xml +target/ +.project +.classpath +.settings/ +.DS_Store +.idea/ +*.iml +*.ipr +*.iws +logfile +*.log* +.springBeans +.gradle/ diff --git a/location/README.md b/location/README.md new file mode 100755 index 0000000..f8727d2 --- /dev/null +++ b/location/README.md @@ -0,0 +1,2 @@ +# hapi-fhir-opensrp-extensions +This repository holds all the code extensions OpenSRP will build on top of the HAPI FHIR server. diff --git a/location/pom.xml b/location/pom.xml new file mode 100755 index 0000000..2015b63 --- /dev/null +++ b/location/pom.xml @@ -0,0 +1,19 @@ + + 4.0.0 + + hapi-fhir-opensrp-extensions + org.smartregister + 0.0.1-SNAPSHOT + + + The repository holds the location extensions on the HAPI server + org.smartregister.hapi-fhir-opensrp-extensions.location + hapi-fhir-opensrp-extensions.location + jar + https://github.com/opensrp/hapi-fhir-opensrp-extensions + 0.0.1-SNAPSHOT + + location + + diff --git a/location/src/license-header.txt b/location/src/license-header.txt new file mode 100644 index 0000000..d937133 --- /dev/null +++ b/location/src/license-header.txt @@ -0,0 +1,15 @@ +/* + * Copyright $YEAR Ona Systems, Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ diff --git a/location/src/main/java/org/smartregister/extension/model/ChildTreeNode.java b/location/src/main/java/org/smartregister/extension/model/ChildTreeNode.java new file mode 100755 index 0000000..c561a74 --- /dev/null +++ b/location/src/main/java/org/smartregister/extension/model/ChildTreeNode.java @@ -0,0 +1,84 @@ +/* + * Copyright 2021 Ona Systems, Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.smartregister.extension.model; + +import ca.uhn.fhir.model.api.annotation.Child; +import ca.uhn.fhir.model.api.annotation.DatatypeDef; +import ca.uhn.fhir.util.ElementUtil; +import org.hl7.fhir.instance.model.api.ICompositeType; +import org.hl7.fhir.r4.model.StringType; +import org.hl7.fhir.r4.model.Type; + +@DatatypeDef(name = "ChildTreeNode") +public class ChildTreeNode extends Type implements ICompositeType { + + @Child( + name = "childId", + type = {StringType.class}, + order = 0, + min = 1, + max = 1, + modifier = false, + summary = false) + private StringType childId; + + @Child( + name = "treeNode", + type = {TreeNode.class}) + private TreeNode treeNode; + + public ChildTreeNode() { + treeNode = new TreeNode(); + } + + public StringType getChildId() { + return childId; + } + + public ChildTreeNode setChildId(StringType childId) { + this.childId = childId; + return this; + } + + public TreeNode getChildren() { + if (treeNode == null) { + treeNode = new TreeNode(); + } + return treeNode; + } + + public ChildTreeNode setChildren(TreeNode children) { + this.treeNode = children; + return this; + } + + @Override + public Type copy() { + ChildTreeNode childTreeNode = new ChildTreeNode(); + copyValues(childTreeNode); + return childTreeNode; + } + + @Override + public boolean isEmpty() { + return ElementUtil.isEmpty(childId); + } + + @Override + protected Type typedCopy() { + return copy(); + } +} diff --git a/location/src/main/java/org/smartregister/extension/model/LocationHierarchy.java b/location/src/main/java/org/smartregister/extension/model/LocationHierarchy.java new file mode 100755 index 0000000..6a4528c --- /dev/null +++ b/location/src/main/java/org/smartregister/extension/model/LocationHierarchy.java @@ -0,0 +1,83 @@ +/* + * Copyright 2021 Ona Systems, Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.smartregister.extension.model; + +import ca.uhn.fhir.model.api.annotation.Child; +import ca.uhn.fhir.model.api.annotation.Description; +import ca.uhn.fhir.model.api.annotation.ResourceDef; +import java.util.ArrayList; +import java.util.List; +import org.hl7.fhir.r4.model.*; + +@ResourceDef(name = "LocationHierarchy", profile = "http://hl7.org/fhir/profiles/custom-resource") +public class LocationHierarchy extends Location { + + @Child( + name = "locationId", + type = {StringType.class}, + order = 5, + min = 0, + max = 1, + modifier = false, + summary = true) + @Description( + shortDefinition = "Unique id to the location", + formalDefinition = "Id of the location whose location hierarchy will be displayed.") + protected StringType locationId; + + @Child( + name = "LocationHierarchyTree", + type = {LocationHierarchyTree.class}) + @Description( + shortDefinition = "Complete Location Hierarchy Tree", + formalDefinition = + "Consists of Location Hierarchy Tree and Parent Child Identifiers List") + private LocationHierarchyTree locationHierarchyTree; + + @Override + public Location copy() { + Location location = new Location(); + Bundle bundle = new Bundle(); + List theEntry = new ArrayList<>(); + Bundle.BundleEntryComponent entryComponent = new Bundle.BundleEntryComponent(); + entryComponent.setResource(new Bundle()); + theEntry.add(entryComponent); + bundle.setEntry(theEntry); + this.copyValues(location); + return location; + } + + @Override + public ResourceType getResourceType() { + return ResourceType.Bundle; + } + + public StringType getLocationId() { + return locationId; + } + + public void setLocationId(StringType locationId) { + this.locationId = locationId; + } + + public LocationHierarchyTree getLocationHierarchyTree() { + return locationHierarchyTree; + } + + public void setLocationHierarchyTree(LocationHierarchyTree locationHierarchyTree) { + this.locationHierarchyTree = locationHierarchyTree; + } +} diff --git a/location/src/main/java/org/smartregister/extension/model/LocationHierarchyTree.java b/location/src/main/java/org/smartregister/extension/model/LocationHierarchyTree.java new file mode 100755 index 0000000..bb7a007 --- /dev/null +++ b/location/src/main/java/org/smartregister/extension/model/LocationHierarchyTree.java @@ -0,0 +1,90 @@ +/* + * Copyright 2021 Ona Systems, Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.smartregister.extension.model; + +import ca.uhn.fhir.model.api.annotation.Child; +import ca.uhn.fhir.model.api.annotation.DatatypeDef; +import ca.uhn.fhir.util.ElementUtil; +import java.util.List; +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.thymeleaf.util.StringUtils; + +@DatatypeDef(name = "LocationHierarchyTree") +public class LocationHierarchyTree extends Type implements ICompositeType { + + @Child(name = "locationsHierarchy") + private Tree locationsHierarchy; + + public LocationHierarchyTree() { + this.locationsHierarchy = new Tree(); + } + + public void addLocation(Location l) { + 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()); + } + } + } + + /** + * WARNING: Overrides existing locations + * + * @param locations + */ + public void buildTreeFromList(List locations) { + for (Location location : locations) { + addLocation(location); + } + } + + public Tree getLocationsHierarchy() { + return locationsHierarchy; + } + + public LocationHierarchyTree setLocationsHierarchy(Tree locationsHierarchy) { + this.locationsHierarchy = locationsHierarchy; + return this; + } + + @Override + public Type copy() { + LocationHierarchyTree locationHierarchyTree = new LocationHierarchyTree(); + copyValues(locationHierarchyTree); + return locationHierarchyTree; + } + + @Override + public boolean isEmpty() { + return ElementUtil.isEmpty(locationsHierarchy); + } + + @Override + protected Type typedCopy() { + return copy(); + } +} diff --git a/location/src/main/java/org/smartregister/extension/model/ParentChildrenMap.java b/location/src/main/java/org/smartregister/extension/model/ParentChildrenMap.java new file mode 100755 index 0000000..07667a1 --- /dev/null +++ b/location/src/main/java/org/smartregister/extension/model/ParentChildrenMap.java @@ -0,0 +1,83 @@ +/* + * Copyright 2021 Ona Systems, Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.smartregister.extension.model; + +import ca.uhn.fhir.model.api.annotation.Child; +import ca.uhn.fhir.model.api.annotation.DatatypeDef; +import ca.uhn.fhir.util.ElementUtil; +import java.util.List; +import org.hl7.fhir.instance.model.api.ICompositeType; +import org.hl7.fhir.r4.model.StringType; +import org.hl7.fhir.r4.model.Type; + +@DatatypeDef(name = "ParentChildrenMap") +public class ParentChildrenMap extends Type implements ICompositeType { + + @Child( + name = "identifier", + type = {StringType.class}, + order = 0, + min = 1, + max = 1, + modifier = false, + summary = false) + private StringType identifier; + + @Child( + name = "childIdentifiers", + type = {StringType.class}, + order = 1, + min = 0, + max = -1, + modifier = false, + summary = false) + private List childIdentifiers; + + public StringType getIdentifier() { + return identifier; + } + + public ParentChildrenMap setIdentifier(StringType identifier) { + this.identifier = identifier; + return this; + } + + public List getChildIdentifiers() { + return childIdentifiers; + } + + public ParentChildrenMap setChildIdentifiers(List childIdentifiers) { + this.childIdentifiers = childIdentifiers; + return this; + } + + @Override + public Type copy() { + ParentChildrenMap parentChildrenMap = new ParentChildrenMap(); + copyValues(parentChildrenMap); + return parentChildrenMap; + } + + @Override + public boolean isEmpty() { + return ElementUtil.isEmpty(identifier); + } + + @Override + protected Type typedCopy() { + return copy(); + } +} diff --git a/location/src/main/java/org/smartregister/extension/model/SingleTreeNode.java b/location/src/main/java/org/smartregister/extension/model/SingleTreeNode.java new file mode 100755 index 0000000..f9fd9e7 --- /dev/null +++ b/location/src/main/java/org/smartregister/extension/model/SingleTreeNode.java @@ -0,0 +1,78 @@ +/* + * Copyright 2021 Ona Systems, Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.smartregister.extension.model; + +import ca.uhn.fhir.model.api.annotation.Child; +import ca.uhn.fhir.model.api.annotation.DatatypeDef; +import ca.uhn.fhir.util.ElementUtil; +import org.hl7.fhir.instance.model.api.ICompositeType; +import org.hl7.fhir.r4.model.StringType; +import org.hl7.fhir.r4.model.Type; + +@DatatypeDef(name = "SingleTreeNode") +public class SingleTreeNode extends Type implements ICompositeType { + + @Child( + name = "treeNodeId", + type = {StringType.class}, + order = 0) + private StringType treeNodeId; + + @Child( + name = "treeNode", + type = {TreeNode.class}, + order = 1, + min = 0, + max = -1, + modifier = false, + summary = false) + private TreeNode treeNode; + + @Override + public Type copy() { + SingleTreeNode singleTreeNode = new SingleTreeNode(); + copyValues(singleTreeNode); + return singleTreeNode; + } + + @Override + public boolean isEmpty() { + return ElementUtil.isEmpty(treeNodeId, treeNode); + } + + @Override + protected Type typedCopy() { + return copy(); + } + + public StringType getTreeNodeId() { + return treeNodeId; + } + + public SingleTreeNode setTreeNodeId(StringType treeNodeId) { + this.treeNodeId = treeNodeId; + return this; + } + + public TreeNode getTreeNode() { + return treeNode; + } + + public SingleTreeNode setTreeNode(TreeNode treeNode) { + this.treeNode = treeNode; + return this; + } +} diff --git a/location/src/main/java/org/smartregister/extension/model/Tree.java b/location/src/main/java/org/smartregister/extension/model/Tree.java new file mode 100755 index 0000000..de4896c --- /dev/null +++ b/location/src/main/java/org/smartregister/extension/model/Tree.java @@ -0,0 +1,274 @@ +/* + * Copyright 2021 Ona Systems, Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.smartregister.extension.model; + +import static org.smartregister.extension.utils.Constants.SLASH_UNDERSCORE; + +import ca.uhn.fhir.model.api.annotation.Child; +import ca.uhn.fhir.model.api.annotation.DatatypeDef; +import ca.uhn.fhir.util.ElementUtil; +import java.util.*; +import org.apache.commons.lang3.StringUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +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; + +@DatatypeDef(name = "Tree") +public class Tree extends Type implements ICompositeType { + + @Child( + name = "listOfNodes", + type = {SingleTreeNode.class}) + private SingleTreeNode listOfNodes; + + @Child( + name = "parentChildren", + type = {ParentChildrenMap.class}, + order = 1, + min = 0, + max = -1, + modifier = false, + summary = false) + private List parentChildren; + + private static Logger logger = LogManager.getLogger(Tree.class.toString()); + + public SingleTreeNode getTree() { + return listOfNodes; + } + + public Tree() { + listOfNodes = new SingleTreeNode(); + parentChildren = new ArrayList<>(); + } + + private void addToParentChildRelation(String parent, String id) { + if (parentChildren == null) { + parentChildren = new ArrayList<>(); + } + List 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; + } + } + } + + 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)); + } + idStringType.setValue(idString); + + StringType parentStringType = new StringType(); + parentStringType.setValue(parent); + 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; + } + } + + if (!setParentChildMap) { + ParentChildrenMap parentChildrenMap = new ParentChildrenMap(); + parentChildrenMap.setIdentifier(parentStringType); + parentChildrenMap.setChildIdentifiers(kids); + parentChildren.add(parentChildrenMap); + } + } + + 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"); + } + + TreeNode treeNode = makeNode(id, label, node, parentId); + + if (parentId != null) { + 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)); + } + SingleTreeNode singleTreeNode = new SingleTreeNode(); + StringType treeNodeId = new StringType(); + treeNodeId.setValue(idString); + singleTreeNode.setTreeNodeId(treeNodeId); + singleTreeNode.setTreeNode(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; + } + } + + private TreeNode makeNode(String id, String label, Location node, String parentId) { + TreeNode treenode = getNode(id); + 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); + 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)); + } + parentIdString.setValue(parentIdStringVar); + treenode.setParent(parentIdString); + } + return treenode; + } + + 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)); + } + + if (listOfNodes.getTreeNodeId() != null + && StringUtils.isNotBlank(listOfNodes.getTreeNodeId().getValue()) + && listOfNodes.getTreeNodeId().getValue().equals(idString)) { + return listOfNodes.getTreeNode(); + + } else { + if (listOfNodes != null + && listOfNodes.getTreeNode() != null + && listOfNodes.getTreeNode().getChildren() != null) { + return recursivelyFindNode(idString, listOfNodes.getTreeNode().getChildren()); + } + } + return null; + } + + public boolean hasNode(String id) { + return getNode(id) != null; + } + + public SingleTreeNode getListOfNodes() { + return listOfNodes; + } + + public void setListOfNodes(SingleTreeNode listOfNodes) { + this.listOfNodes = listOfNodes; + } + + public List getParentChildren() { + return parentChildren; + } + + public void setParentChildren(List parentChildren) { + this.parentChildren = parentChildren; + } + + @Override + public Type copy() { + Tree tree = new Tree(); + copyValues(tree); + return tree; + } + + @Override + public boolean isEmpty() { + return ElementUtil.isEmpty(listOfNodes); + } + + @Override + protected Type typedCopy() { + return copy(); + } + + private TreeNode recursivelyFindNode(String idString, List childTreeNodeList) { + for (ChildTreeNode childTreeNode : childTreeNodeList) { + TreeNode treeNode = childTreeNode.getChildren(); + if (treeNode != null + && treeNode.getNodeId() != null + && StringUtils.isNotBlank(treeNode.getNodeId().getValue()) + && treeNode.getNodeId().getValue().equals(idString)) { + return treeNode; + } else { + if (treeNode != null + && treeNode.getChildren() != null + && treeNode.getChildren().size() > 0) { + return recursivelyFindNode(idString, treeNode.getChildren()); + } + } + } + return null; + } +} diff --git a/location/src/main/java/org/smartregister/extension/model/TreeNode.java b/location/src/main/java/org/smartregister/extension/model/TreeNode.java new file mode 100755 index 0000000..2107464 --- /dev/null +++ b/location/src/main/java/org/smartregister/extension/model/TreeNode.java @@ -0,0 +1,213 @@ +/* + * Copyright 2021 Ona Systems, Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.smartregister.extension.model; + +import static org.smartregister.extension.utils.Constants.SLASH_UNDERSCORE; + +import ca.uhn.fhir.model.api.annotation.Child; +import ca.uhn.fhir.model.api.annotation.DatatypeDef; +import ca.uhn.fhir.util.ElementUtil; +import java.util.ArrayList; +import java.util.List; +import org.apache.commons.lang3.StringUtils; +import org.hl7.fhir.instance.model.api.ICompositeType; +import org.hl7.fhir.r4.model.*; + +@DatatypeDef(name = "TreeNode") +public class TreeNode extends Type implements ICompositeType { + + @Child( + name = "name", + type = {StringType.class}, + order = 0, + min = 1, + max = 1, + modifier = false, + summary = false) + protected StringType name; + + @Child( + name = "nodeId", + type = {StringType.class}, + order = 2) + private StringType nodeId; + + @Child( + name = "label", + type = {StringType.class}, + order = 3) + private StringType label; + + @Child( + name = "node", + type = {Location.class}, + order = 4) + private Location node; + + @Child( + name = "parent", + type = {StringType.class}, + order = 5) + private StringType parent; + + @Child( + name = "children", + type = {ChildTreeNode.class}, + order = 6, + min = 0, + max = -1, + modifier = false, + summary = false) + private List children; + + public TreeNode() { + children = new ArrayList<>(); + } + + public TreeNode( + StringType name, + StringType nodeId, + StringType label, + Location node, + StringType parent) { + this.name = name; + this.nodeId = nodeId; + this.label = label; + this.node = node; + this.parent = parent; + } + + public StringType getName() { + if (name == null) { + name = new StringType(); + } + return name; + } + + public TreeNode setName(StringType name) { + this.name = name; + return this; + } + + public StringType getLabel() { + return label; + } + + public TreeNode setLabel(StringType label) { + this.label = label; + return this; + } + + @Override + public Type copy() { + TreeNode treeNode = new TreeNode(); + copyValues(treeNode); + return treeNode; + } + + @Override + public boolean isEmpty() { + return ElementUtil.isEmpty(node); + } + + @Override + protected Type typedCopy() { + return copy(); + } + + public StringType getNodeId() { + return nodeId; + } + + public TreeNode setNodeId(StringType nodeId) { + this.nodeId = nodeId; + return this; + } + + public Location getNode() { + return node; + } + + public TreeNode setNode(Location node) { + this.node = node; + return this; + } + + public StringType getParent() { + return parent; + } + + public TreeNode setParent(StringType parent) { + this.parent = parent; + return this; + } + + public List getChildren() { + if (children == null) { + children = new ArrayList<>(); + } + return children; + } + + public TreeNode setChildren(List children) { + this.children = children; + return this; + } + + public void addChild(TreeNode node) { + if (children == null) { + children = new ArrayList<>(); + } + ChildTreeNode childTreeNode = new ChildTreeNode(); + childTreeNode.setChildId(node.getNodeId()); + List treeNodeList = new ArrayList<>(); + TreeNode treeNode = new TreeNode(); + treeNode.setNode(node.getNode()); + treeNode.setNodeId(node.getNodeId()); + treeNode.setLabel(node.getLabel()); + treeNode.setParent(node.getParent()); + treeNodeList.add(treeNode); + childTreeNode.setChildren(treeNode); + 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; + } + } + } + } + } + return null; + } +} diff --git a/location/src/main/java/org/smartregister/extension/rest/LocationHierarchyResourceProvider.java b/location/src/main/java/org/smartregister/extension/rest/LocationHierarchyResourceProvider.java new file mode 100755 index 0000000..781f187 --- /dev/null +++ b/location/src/main/java/org/smartregister/extension/rest/LocationHierarchyResourceProvider.java @@ -0,0 +1,115 @@ +/* + * Copyright 2021 Ona Systems, Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.smartregister.extension.rest; + +import static org.smartregister.extension.utils.Constants.*; + +import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; +import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; +import ca.uhn.fhir.rest.annotation.*; +import ca.uhn.fhir.rest.api.server.IBundleProvider; +import ca.uhn.fhir.rest.param.*; +import ca.uhn.fhir.rest.server.IResourceProvider; +import java.util.*; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r4.model.*; +import org.smartregister.extension.model.LocationHierarchy; +import org.smartregister.extension.model.LocationHierarchyTree; +import org.springframework.beans.factory.annotation.Autowired; + +public class LocationHierarchyResourceProvider implements IResourceProvider { + + @Autowired IFhirResourceDao locationIFhirResourceDao; + + private static Logger logger = + LogManager.getLogger(LocationHierarchyResourceProvider.class.toString()); + + @Override + public Class getResourceType() { + return LocationHierarchy.class; + } + + @Search + public LocationHierarchy getLocationHierarchy( + @RequiredParam(name = IDENTIFIER) TokenParam identifier) { + + SearchParameterMap paramMap = new SearchParameterMap(); + paramMap.add(IDENTIFIER, identifier); + + IBundleProvider locationBundle = locationIFhirResourceDao.search(paramMap); + List locations = + locationBundle != null + ? locationBundle.getResources(0, locationBundle.size()) + : new ArrayList<>(); + Long id = null; + if (locations.size() > 0) { + id = + locations.get(0) != null && locations.get(0).getIdElement() != null + ? locations.get(0).getIdElement().getIdPartAsLong() + : null; + } + + LocationHierarchyTree locationHierarchyTree = new LocationHierarchyTree(); + LocationHierarchy locationHierarchy = new LocationHierarchy(); + if (id != null && locations.size() > 0) { + logger.info("Building Location Hierarchy of Location Id : " + id); + locationHierarchyTree.buildTreeFromList(getLocationHierarchy(id, locations.get(0))); + StringType locationIdString = new StringType().setId(id.toString()).getIdElement(); + locationHierarchy.setLocationId(locationIdString); + locationHierarchy.setId(LOCATION_RESOURCE + id); + + locationHierarchy.setLocationHierarchyTree(locationHierarchyTree); + } else { + locationHierarchy.setId(LOCATION_RESOURCE_NOT_FOUND); + } + return locationHierarchy; + } + + private List getLocationHierarchy(Long id, IBaseResource parentLocation) { + return descendants(id, parentLocation); + } + + public List descendants(Long id, IBaseResource parentLocation) { + + SearchParameterMap paramMap = new SearchParameterMap(); + ReferenceAndListParam thePartOf = new ReferenceAndListParam(); + ReferenceParam partOf = new ReferenceParam(); + partOf.setValue(LOCATION + FORWARD_SLASH + id); + ReferenceOrListParam referenceOrListParam = new ReferenceOrListParam(); + referenceOrListParam.add(partOf); + thePartOf.addValue(referenceOrListParam); + paramMap.add(PART_OF, thePartOf); + + IBundleProvider childLocationBundle = locationIFhirResourceDao.search(paramMap); + List allLocations = new ArrayList<>(); + if (parentLocation != null) { + allLocations.add((Location) parentLocation); + } + for (IBaseResource childLocation : + childLocationBundle.getResources(0, childLocationBundle.size())) { + Location childLocationEntity = (Location) childLocation; + allLocations.add(childLocationEntity); + allLocations.addAll(descendants(childLocation.getIdElement().getIdPartAsLong(), null)); + } + return allLocations; + } + + public void setLocationIFhirResourceDao(IFhirResourceDao locationIFhirResourceDao) { + this.locationIFhirResourceDao = locationIFhirResourceDao; + } +} diff --git a/location/src/main/java/org/smartregister/extension/utils/Constants.java b/location/src/main/java/org/smartregister/extension/utils/Constants.java new file mode 100644 index 0000000..dc8deda --- /dev/null +++ b/location/src/main/java/org/smartregister/extension/utils/Constants.java @@ -0,0 +1,27 @@ +/* + * Copyright 2021 Ona Systems, Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.smartregister.extension.utils; + +public interface Constants { + + String SLASH_UNDERSCORE = "/_"; + String LOCATION = "Location"; + String FORWARD_SLASH = "/"; + String IDENTIFIER = "identifier"; + String LOCATION_RESOURCE_NOT_FOUND = "Location Resource : Not Found"; + String LOCATION_RESOURCE = "Location Resource : "; + String PART_OF = "partof"; +} diff --git a/location/src/test/java/org/smartregister/extension/model/LocationHierarchyTreeTest.java b/location/src/test/java/org/smartregister/extension/model/LocationHierarchyTreeTest.java new file mode 100644 index 0000000..4e129a1 --- /dev/null +++ b/location/src/test/java/org/smartregister/extension/model/LocationHierarchyTreeTest.java @@ -0,0 +1,250 @@ +/* + * Copyright 2021 Ona Systems, Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.smartregister.extension.model; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNull; +import static org.junit.Assert.assertNotNull; + +import java.util.ArrayList; +import java.util.List; +import org.hl7.fhir.r4.model.Location; +import org.hl7.fhir.r4.model.Reference; +import org.junit.Test; + +public class LocationHierarchyTreeTest { + + @Test + public void testAddLocationWithoutChildLocations() { + Location location = new Location(); + location.setId("Location/1"); + location.setName("Test Location"); + Reference partOfReference = new Reference(); + partOfReference.setReference(""); + location.setPartOf(partOfReference); + LocationHierarchyTree locationHierarchyTree = new LocationHierarchyTree(); + locationHierarchyTree.addLocation(location); + + Tree tree = locationHierarchyTree.getLocationsHierarchy(); + assertNotNull(tree); + assertNotNull(tree.getTree()); + assertEquals("Location/1", tree.getTree().getTreeNodeId().getValue()); + assertEquals("Location/1", tree.getTree().getTreeNode().getNodeId().getValue()); + assertEquals("Test Location", tree.getTree().getTreeNode().getLabel().getValue()); + assertNull(tree.getTree().getTreeNode().getParent().getValue()); + assertEquals(0, tree.getTree().getTreeNode().getChildren().size()); + } + + @Test + public void testBuildTreeFromList() { + Location location1 = new Location(); + location1.setId("Location/1"); + location1.setName("Test Location"); + Reference partOfReference = new Reference(); + partOfReference.setReference(""); + location1.setPartOf(partOfReference); + + Location location2 = new Location(); + location2.setId("Location/2"); + location2.setName("Test Location 2"); + partOfReference = new Reference(); + partOfReference.setReference("Location/1"); + location2.setPartOf(partOfReference); + + Location location3 = new Location(); + location3.setId("Location/3"); + location3.setName("Test Location 3"); + partOfReference = new Reference(); + partOfReference.setReference("Location/2"); + location3.setPartOf(partOfReference); + + LocationHierarchyTree locationHierarchyTree = new LocationHierarchyTree(); + + List locationList = new ArrayList<>(); + locationList.add(location1); + locationList.add(location2); + locationList.add(location3); + + locationHierarchyTree.buildTreeFromList(locationList); + Tree tree = locationHierarchyTree.getLocationsHierarchy(); + assertNotNull(tree); + assertNotNull(tree.getTree()); + assertEquals("Location/1", tree.getTree().getTreeNodeId().getValue()); + assertEquals("Location/1", tree.getTree().getTreeNode().getNodeId().getValue()); + assertEquals("Test Location", tree.getTree().getTreeNode().getLabel().getValue()); + assertNull(tree.getTree().getTreeNode().getParent().getValue()); + assertEquals(1, tree.getTree().getTreeNode().getChildren().size()); + + assertEquals( + "Location/2", + tree.getTree() + .getTreeNode() + .getChildren() + .get(0) + .getChildren() + .getNodeId() + .getValue()); + assertEquals( + "Test Location 2", + tree.getTree() + .getTreeNode() + .getChildren() + .get(0) + .getChildren() + .getLabel() + .getValue()); + assertNotNull( + tree.getTree() + .getTreeNode() + .getChildren() + .get(0) + .getChildren() + .getParent() + .getValue()); + assertEquals( + "Location/1", + tree.getTree() + .getTreeNode() + .getChildren() + .get(0) + .getChildren() + .getParent() + .getValue()); + assertEquals( + 1, + tree.getTree() + .getTreeNode() + .getChildren() + .get(0) + .getChildren() + .getChildren() + .size()); + + assertEquals( + "Location/3", + tree.getTree() + .getTreeNode() + .getChildren() + .get(0) + .getChildren() + .getChildren() + .get(0) + .getChildren() + .getNodeId() + .getValue()); + assertEquals( + "Test Location 3", + tree.getTree() + .getTreeNode() + .getChildren() + .get(0) + .getChildren() + .getChildren() + .get(0) + .getChildren() + .getLabel() + .getValue()); + assertNotNull( + tree.getTree() + .getTreeNode() + .getChildren() + .get(0) + .getChildren() + .getChildren() + .get(0) + .getChildren() + .getParent() + .getValue()); + assertEquals( + "Location/2", + tree.getTree() + .getTreeNode() + .getChildren() + .get(0) + .getChildren() + .getChildren() + .get(0) + .getChildren() + .getParent() + .getValue()); + assertEquals( + 0, + tree.getTree() + .getTreeNode() + .getChildren() + .get(0) + .getChildren() + .getChildren() + .get(0) + .getChildren() + .getChildren() + .size()); + + assertNotNull(locationHierarchyTree.getLocationsHierarchy().getParentChildren()); + assertEquals(2, locationHierarchyTree.getLocationsHierarchy().getParentChildren().size()); + assertEquals( + "Location/1", + locationHierarchyTree + .getLocationsHierarchy() + .getParentChildren() + .get(0) + .getIdentifier() + .getValue()); + assertEquals( + 1, + locationHierarchyTree + .getLocationsHierarchy() + .getParentChildren() + .get(0) + .getChildIdentifiers() + .size()); + assertEquals( + "Location/2", + locationHierarchyTree + .getLocationsHierarchy() + .getParentChildren() + .get(0) + .getChildIdentifiers() + .get(0) + .getValue()); + + assertEquals( + "Location/2", + locationHierarchyTree + .getLocationsHierarchy() + .getParentChildren() + .get(1) + .getIdentifier() + .getValue()); + assertEquals( + 1, + locationHierarchyTree + .getLocationsHierarchy() + .getParentChildren() + .get(1) + .getChildIdentifiers() + .size()); + assertEquals( + "Location/3", + locationHierarchyTree + .getLocationsHierarchy() + .getParentChildren() + .get(1) + .getChildIdentifiers() + .get(0) + .getValue()); + } +} diff --git a/src/test/java/org/smartregister/extension/model/TreeNodeTest.java b/location/src/test/java/org/smartregister/extension/model/TreeNodeTest.java similarity index 51% rename from src/test/java/org/smartregister/extension/model/TreeNodeTest.java rename to location/src/test/java/org/smartregister/extension/model/TreeNodeTest.java index f4df891..06c7b37 100644 --- a/src/test/java/org/smartregister/extension/model/TreeNodeTest.java +++ b/location/src/test/java/org/smartregister/extension/model/TreeNodeTest.java @@ -1,19 +1,31 @@ +/* + * Copyright 2021 Ona Systems, Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.smartregister.extension.model; +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNull; + import org.hl7.fhir.r4.model.Location; import org.hl7.fhir.r4.model.StringType; import org.junit.Test; -import java.util.HashMap; -import java.util.Map; - -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertNull; - public class TreeNodeTest { - @Test - public void testAddingChild() { + @Test + public void testAddingChild() { StringType rootNodeName = new StringType(); rootNodeName.setValue("Root Node"); @@ -36,16 +48,21 @@ public void testAddingChild() { StringType childNodeLabel = new StringType(); childNodeLabel.setValue("Child Location Node"); - TreeNode rootNode = new TreeNode(rootNodeName, rootNodeId, rootNodeLabel, location, null); - TreeNode childNode = new TreeNode(childNodeName, childNodeId, childNodeLabel, location, rootNodeId); - rootNode.addChild(childNode); - - assertEquals(childNodeId.getValue(), rootNode.findChild(childNodeId.getValue()).getNodeId().getValue()); - assertEquals(rootNodeId.getValue(), rootNode.findChild(childNodeId.getValue()).getParent().getValue()); - } - - @Test - public void findInvalidChildren() { + TreeNode rootNode = new TreeNode(rootNodeName, rootNodeId, rootNodeLabel, location, null); + TreeNode childNode = + new TreeNode(childNodeName, childNodeId, childNodeLabel, location, rootNodeId); + rootNode.addChild(childNode); + + assertEquals( + childNodeId.getValue(), + rootNode.findChild(childNodeId.getValue()).getNodeId().getValue()); + assertEquals( + rootNodeId.getValue(), + rootNode.findChild(childNodeId.getValue()).getParent().getValue()); + } + + @Test + public void findInvalidChildren() { StringType rootNodeName = new StringType(); rootNodeName.setValue("Root Node"); @@ -59,7 +76,7 @@ public void findInvalidChildren() { location.setId("Location/1"); TreeNode rootNode = new TreeNode(rootNodeName, rootNodeId, rootNodeLabel, location, null); - assertEquals(0, rootNode.getChildren().size()); - assertNull(rootNode.findChild("Location/2")); - } + assertEquals(0, rootNode.getChildren().size()); + assertNull(rootNode.findChild("Location/2")); + } } diff --git a/location/src/test/java/org/smartregister/extension/model/TreeTest.java b/location/src/test/java/org/smartregister/extension/model/TreeTest.java new file mode 100644 index 0000000..2f59d3d --- /dev/null +++ b/location/src/test/java/org/smartregister/extension/model/TreeTest.java @@ -0,0 +1,70 @@ +/* + * Copyright 2021 Ona Systems, Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.smartregister.extension.model; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNull; +import static org.junit.Assert.assertNotNull; + +import org.hl7.fhir.r4.model.Location; +import org.junit.Test; + +public class TreeTest { + + @Test + public void testAddingNodeWithOutParent() { + Tree tree = new Tree(); + Location location = new Location(); + location.setId("testId"); + tree.addNode("Location/1", "test", location, null); + + TreeNode treeNode = tree.getNode("Location/1"); + + assertEquals("Location/1", treeNode.getNodeId().getValue()); + assertEquals("test", treeNode.getLabel().getValue()); + assertEquals(location, treeNode.getNode()); + assertNull(treeNode.getParent().getValue()); + } + + @Test(expected = IllegalArgumentException.class) + public void testCannotReAddExistingNode() { + Tree tree = new Tree(); + Location location = new Location(); + location.setId("testId"); + tree.addNode("Location/1", "test", location, null); + tree.addNode("Location/1", "test", location, null); + } + + @Test + public void testAddingNodeWithValidParent() { + Tree tree = new Tree(); + Location location = new Location(); + location.setId("testId"); + tree.addNode("Location/1", "test", location, null); + tree.addNode("Location/2", "test2", location, "Location/1"); + + TreeNode childNode = tree.getNode("Location/2"); + + assertEquals("Location/2", childNode.getNodeId().getValue()); + assertEquals("test2", childNode.getLabel().getValue()); + assertEquals(location, childNode.getNode()); + assertNotNull(childNode.getParent().getValue()); + + String parentNodeId = childNode.getParent().getValue(); + + assertEquals("Location/1", parentNodeId); + } +} diff --git a/pom.xml b/pom.xml index d1bce25..d85aceb 100755 --- a/pom.xml +++ b/pom.xml @@ -1,89 +1,160 @@ - - - 4.0.0 + + 4.0.0 - org.smartregister - hapi-fhir-opensrp-extensions - jar - 0.0.1-SNAPSHOT - hapi-fhir-opensrp-extensions - This repository holds all the code extensions on top of Hapi-FHIR - https://github.com/opensrp/hapi-fhir-opensrp-extensions + org.smartregister + hapi-fhir-opensrp-extensions + pom + 0.0.1-SNAPSHOT + hapi-fhir-opensrp-extensions + This repository holds all the code extensions on top of Hapi-FHIR + https://github.com/opensrp/hapi-fhir-opensrp-extensions - - 8 - 8 - 5.2.4.RELEASE - + + GitHub Issues + https://github.com/opensrp/hapi-fhir-opensrp-extensions/issues + + 2021 - - - nexus-snapshots - Nexus Snapshots Repository - https://oss.sonatype.org/content/repositories/snapshots - false - - - nexus-releases - https://oss.sonatype.org/service/local/staging/deploy/maven2 - - + + + nexus-releases + https://oss.sonatype.org/service/local/staging/deploy/maven2 + + + nexus-snapshots + Nexus Snapshots Repository + false + https://oss.sonatype.org/content/repositories/snapshots + + - - - org.springframework - spring-web - ${spring.version} - + + 8 + 8 + 5.2.4.RELEASE + 2.13.0 + 2.5 + 5.4.0 + 4.13.1 + - - org.springframework - spring-jdbc - ${spring.version} - - - - javax.servlet - servlet-api - 2.5 - provided - + + location + - - - ca.uhn.hapi.fhir - hapi-fhir-base - 5.4.0 - + + + spring-web + org.springframework + ${spring.version} + - - - ca.uhn.hapi.fhir - hapi-fhir-jpaserver-base - 5.4.0 - - - org.springframework - spring-jcl - - - commons-logging - commons-logging - - - + + spring-jdbc + org.springframework + ${spring.version} + + + + servlet-api + javax.servlet + provided + ${servlet.version} + - - - junit - junit - 4.13.1 - test - + + + hapi-fhir-base + ca.uhn.hapi.fhir + ${hapi.fhir.base.version} + - + + + com.diffplug.spotless + spotless-maven-plugin + ${spotless.version} + + + + hapi-fhir-jpaserver-base + + + spring-jcl + org.springframework + + + commons-logging + commons-logging + + + ca.uhn.hapi.fhir + 5.4.0 + + + + junit + junit + test + ${junit.version} + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 1.8 + 1.8 + true + UTF-8 + + + + com.diffplug.spotless + spotless-maven-plugin + ${spotless.version} + + + + check + + compile + + + + + + + *.factories + *.xml + .gitignore + + + + + true + 4 + + + + + + 1.8 + + + + ${basedir}/src/license-header.txt + + + + + + diff --git a/src/main/java/org/smartregister/extension/model/ChildTreeNode.java b/src/main/java/org/smartregister/extension/model/ChildTreeNode.java deleted file mode 100755 index e6d3f4f..0000000 --- a/src/main/java/org/smartregister/extension/model/ChildTreeNode.java +++ /dev/null @@ -1,60 +0,0 @@ -package org.smartregister.extension.model; - -import ca.uhn.fhir.model.api.annotation.Child; -import ca.uhn.fhir.model.api.annotation.DatatypeDef; -import ca.uhn.fhir.util.ElementUtil; -import org.hl7.fhir.instance.model.api.ICompositeType; -import org.hl7.fhir.r4.model.StringType; -import org.hl7.fhir.r4.model.Type; - -@DatatypeDef(name = "ChildTreeNode") -public class ChildTreeNode extends Type implements ICompositeType { - - @Child(name = "childId", type = { StringType.class }, order = 0, min = 1, max = 1, modifier = false, summary = false) - private StringType childId; - - @Child(name = "treeNode", type = { TreeNode.class }) - private TreeNode treeNode; - - public ChildTreeNode() { - treeNode = new TreeNode(); - } - - public StringType getChildId() { - return childId; - } - - public ChildTreeNode setChildId(StringType childId) { - this.childId = childId; - return this; - } - - public TreeNode getChildren() { - if (treeNode == null) { - treeNode = new TreeNode(); - } - return treeNode; - } - - public ChildTreeNode setChildren(TreeNode children) { - this.treeNode = children; - return this; - } - - @Override - public Type copy() { - ChildTreeNode childTreeNode = new ChildTreeNode(); - copyValues(childTreeNode); - return childTreeNode; - } - - @Override - public boolean isEmpty() { - return ElementUtil.isEmpty(childId); - } - - @Override - protected Type typedCopy() { - return copy(); - } -} diff --git a/src/main/java/org/smartregister/extension/model/LocationHierarchy.java b/src/main/java/org/smartregister/extension/model/LocationHierarchy.java deleted file mode 100755 index e4a5c59..0000000 --- a/src/main/java/org/smartregister/extension/model/LocationHierarchy.java +++ /dev/null @@ -1,71 +0,0 @@ -package org.smartregister.extension.model; - -import ca.uhn.fhir.model.api.annotation.Child; -import ca.uhn.fhir.model.api.annotation.Description; -import ca.uhn.fhir.model.api.annotation.ResourceDef; -import org.hl7.fhir.r4.model.*; - -import java.util.ArrayList; -import java.util.List; - -@ResourceDef(name = "LocationHierarchy", profile = "http://hl7.org/fhir/profiles/custom-resource") -public class LocationHierarchy extends Location { - - @Child( - name = "locationId", - type = { StringType.class }, - order = 5, - min = 0, - max = 1, - modifier = false, - summary = true - ) - @Description( - shortDefinition = "Unique id to the location", - formalDefinition = "Id of the location whose location hierarchy will be displayed." - ) - protected StringType locationId; - - @Child(name = "LocationHierarchyTree", type = { LocationHierarchyTree.class }) - @Description( - shortDefinition = "Complete Location Hierarchy Tree", - formalDefinition = "Consists of Location Hierarchy Tree and Parent Child Identifiers List" - ) - private LocationHierarchyTree locationHierarchyTree; - - @Override - public Location copy() { - Location location = new Location(); - Bundle bundle = new Bundle(); - List theEntry = new ArrayList<>(); - Bundle.BundleEntryComponent entryComponent = new Bundle.BundleEntryComponent(); - entryComponent.setResource(new Bundle()); - theEntry.add(entryComponent); - bundle.setEntry(theEntry); - this.copyValues(location); - return location; - } - - @Override - public ResourceType getResourceType() { - return ResourceType.Bundle; - } - - public StringType getLocationId() { - return locationId; - } - - public void setLocationId(StringType locationId) { - this.locationId = locationId; - - } - - public LocationHierarchyTree getLocationHierarchyTree() { - return locationHierarchyTree; - } - - public void setLocationHierarchyTree(LocationHierarchyTree locationHierarchyTree) { - this.locationHierarchyTree = locationHierarchyTree; - } - -} diff --git a/src/main/java/org/smartregister/extension/model/LocationHierarchyTree.java b/src/main/java/org/smartregister/extension/model/LocationHierarchyTree.java deleted file mode 100755 index 5a73778..0000000 --- a/src/main/java/org/smartregister/extension/model/LocationHierarchyTree.java +++ /dev/null @@ -1,76 +0,0 @@ -package org.smartregister.extension.model; - -import ca.uhn.fhir.model.api.annotation.Child; -import ca.uhn.fhir.model.api.annotation.DatatypeDef; -import ca.uhn.fhir.util.ElementUtil; -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.thymeleaf.util.StringUtils; - -import java.util.List; - -@DatatypeDef(name = "LocationHierarchyTree") -public class LocationHierarchyTree extends Type implements ICompositeType { - - @Child(name = "locationsHierarchy") - private Tree locationsHierarchy; - - public LocationHierarchyTree() { - this.locationsHierarchy = new Tree(); - } - - public void addLocation(Location l) { - 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()); - } - } - } - - /** - * WARNING: Overrides existing locations - * - * @param locations - */ - public void buildTreeFromList(List locations) { - for (Location location : locations) { - addLocation(location); - } - } - - public Tree getLocationsHierarchy() { - return locationsHierarchy; - } - - public LocationHierarchyTree setLocationsHierarchy(Tree locationsHierarchy) { - this.locationsHierarchy = locationsHierarchy; - return this; - } - - @Override - public Type copy() { - LocationHierarchyTree locationHierarchyTree = new LocationHierarchyTree(); - copyValues(locationHierarchyTree); - return locationHierarchyTree; - } - - @Override - public boolean isEmpty() { - return ElementUtil.isEmpty(locationsHierarchy); - } - - @Override - protected Type typedCopy() { - return copy(); - } -} - diff --git a/src/main/java/org/smartregister/extension/model/ParentChildrenMap.java b/src/main/java/org/smartregister/extension/model/ParentChildrenMap.java deleted file mode 100755 index fc40c69..0000000 --- a/src/main/java/org/smartregister/extension/model/ParentChildrenMap.java +++ /dev/null @@ -1,61 +0,0 @@ -package org.smartregister.extension.model; - -import ca.uhn.fhir.model.api.annotation.Child; -import ca.uhn.fhir.model.api.annotation.DatatypeDef; -import ca.uhn.fhir.util.ElementUtil; -import org.hl7.fhir.instance.model.api.ICompositeType; -import org.hl7.fhir.r4.model.StringType; -import org.hl7.fhir.r4.model.Type; - -import java.util.List; - -@DatatypeDef(name = "ParentChildrenMap") -public class ParentChildrenMap extends Type implements ICompositeType { - - @Child(name = "identifier", type = { StringType.class }, order = 0, min = 1, max = 1, modifier = false, summary = false) - private StringType identifier; - - @Child(name = "childIdentifiers", type = { StringType.class }, - order = 1, - min = 0, - max = -1, - modifier = false, - summary = false) - private List childIdentifiers; - - public StringType getIdentifier() { - return identifier; - } - - public ParentChildrenMap setIdentifier(StringType identifier) { - this.identifier = identifier; - return this; - } - - public List getChildIdentifiers() { - return childIdentifiers; - } - - public ParentChildrenMap setChildIdentifiers(List childIdentifiers) { - this.childIdentifiers = childIdentifiers; - return this; - } - - @Override - public Type copy() { - ParentChildrenMap parentChildrenMap = new ParentChildrenMap(); - copyValues(parentChildrenMap); - return parentChildrenMap; - } - - @Override - public boolean isEmpty() { - return ElementUtil.isEmpty(identifier); - } - - @Override - protected Type typedCopy() { - return copy(); - } - -} diff --git a/src/main/java/org/smartregister/extension/model/SingleTreeNode.java b/src/main/java/org/smartregister/extension/model/SingleTreeNode.java deleted file mode 100755 index b0f6392..0000000 --- a/src/main/java/org/smartregister/extension/model/SingleTreeNode.java +++ /dev/null @@ -1,58 +0,0 @@ -package org.smartregister.extension.model; - -import ca.uhn.fhir.model.api.annotation.Child; -import ca.uhn.fhir.model.api.annotation.DatatypeDef; -import ca.uhn.fhir.util.ElementUtil; -import org.hl7.fhir.instance.model.api.ICompositeType; -import org.hl7.fhir.r4.model.StringType; -import org.hl7.fhir.r4.model.Type; - -@DatatypeDef(name = "SingleTreeNode") -public class SingleTreeNode extends Type implements ICompositeType { - - @Child(name = "treeNodeId", type = { StringType.class }, order = 0) - private StringType treeNodeId; - - @Child(name = "treeNode", type = { TreeNode.class }, - order = 1, - min = 0, - max = -1, - modifier = false, - summary = false) - private TreeNode treeNode; - - @Override - public Type copy() { - SingleTreeNode singleTreeNode = new SingleTreeNode(); - copyValues(singleTreeNode); - return singleTreeNode; - } - - @Override - public boolean isEmpty() { - return ElementUtil.isEmpty(treeNodeId, treeNode); - } - - @Override - protected Type typedCopy() { - return copy(); - } - - public StringType getTreeNodeId() { - return treeNodeId; - } - - public SingleTreeNode setTreeNodeId(StringType treeNodeId) { - this.treeNodeId = treeNodeId; - return this; - } - - public TreeNode getTreeNode() { - return treeNode; - } - - public SingleTreeNode setTreeNode(TreeNode treeNode) { - this.treeNode = treeNode; - return this; - } -} diff --git a/src/main/java/org/smartregister/extension/model/Tree.java b/src/main/java/org/smartregister/extension/model/Tree.java deleted file mode 100755 index 9dde450..0000000 --- a/src/main/java/org/smartregister/extension/model/Tree.java +++ /dev/null @@ -1,243 +0,0 @@ -package org.smartregister.extension.model; - -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.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -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 java.util.*; - -import static org.smartregister.extension.utils.Constants.SLASH_UNDERSCORE; - -@DatatypeDef(name = "Tree") -public class Tree extends Type implements ICompositeType { - - @Child(name = "listOfNodes", type = { SingleTreeNode.class }) - private SingleTreeNode listOfNodes; - - @Child(name = "parentChildren", - type = { ParentChildrenMap.class }, - order = 1, - min = 0, - max = -1, - modifier = false, - summary = false) - private List parentChildren; - - private static Logger logger = LogManager.getLogger(Tree.class.toString()); - - public SingleTreeNode getTree() { - return listOfNodes; - } - - public Tree() { - listOfNodes = new SingleTreeNode(); - parentChildren = new ArrayList<>(); - } - - private void addToParentChildRelation(String parent, String id) { - if (parentChildren == null) { - parentChildren = new ArrayList<>(); - } - List 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; - } - } - } - - 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)); - } - idStringType.setValue(idString); - - StringType parentStringType = new StringType(); - parentStringType.setValue(parent); - 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; - } - } - - if (!setParentChildMap) { - ParentChildrenMap parentChildrenMap = new ParentChildrenMap(); - parentChildrenMap.setIdentifier(parentStringType); - parentChildrenMap.setChildIdentifiers(kids); - parentChildren.add(parentChildrenMap); - } - - } - - 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"); - } - - TreeNode treeNode = makeNode(id, label, node, parentId); - - if (parentId != null) { - 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)); - } - SingleTreeNode singleTreeNode = new SingleTreeNode(); - StringType treeNodeId = new StringType(); - treeNodeId.setValue(idString); - singleTreeNode.setTreeNodeId(treeNodeId); - singleTreeNode.setTreeNode(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; - } - } - - private TreeNode makeNode(String id, String label, Location node, String parentId) { - TreeNode treenode = getNode(id); - 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); - 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)); - } - parentIdString.setValue(parentIdStringVar); - treenode.setParent(parentIdString); - } - return treenode; - } - - 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)); - } - - if (listOfNodes.getTreeNodeId() != null && StringUtils.isNotBlank(listOfNodes.getTreeNodeId().getValue()) && - listOfNodes.getTreeNodeId().getValue().equals(idString)) { - return listOfNodes.getTreeNode(); - - } else { - if (listOfNodes != null && listOfNodes.getTreeNode() != null - && listOfNodes.getTreeNode().getChildren() != null) { - return recursivelyFindNode(idString, listOfNodes.getTreeNode().getChildren()); - } - } - return null; - } - - public boolean hasNode(String id) { - return getNode(id) != null; - } - - public SingleTreeNode getListOfNodes() { - return listOfNodes; - } - - public void setListOfNodes(SingleTreeNode listOfNodes) { - this.listOfNodes = listOfNodes; - } - - public List getParentChildren() { - return parentChildren; - } - - public void setParentChildren(List parentChildren) { - this.parentChildren = parentChildren; - } - - @Override - public Type copy() { - Tree tree = new Tree(); - copyValues(tree); - return tree; - } - - @Override - public boolean isEmpty() { - return ElementUtil.isEmpty(listOfNodes); - } - - @Override - protected Type typedCopy() { - return copy(); - } - - private TreeNode recursivelyFindNode(String idString, List childTreeNodeList) { - for (ChildTreeNode childTreeNode : childTreeNodeList) { - TreeNode treeNode = childTreeNode.getChildren(); - if (treeNode != null && treeNode.getNodeId() != null && StringUtils.isNotBlank(treeNode.getNodeId().getValue()) && - treeNode.getNodeId().getValue().equals(idString)) { - return treeNode; - } else { - if (treeNode != null && treeNode.getChildren() != null && treeNode.getChildren().size() > 0) { - return recursivelyFindNode(idString, treeNode.getChildren()); - } - } - } - return null; - } - -} diff --git a/src/main/java/org/smartregister/extension/model/TreeNode.java b/src/main/java/org/smartregister/extension/model/TreeNode.java deleted file mode 100755 index 95fc4c3..0000000 --- a/src/main/java/org/smartregister/extension/model/TreeNode.java +++ /dev/null @@ -1,175 +0,0 @@ -package org.smartregister.extension.model; - -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.*; - -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; - -import static org.smartregister.extension.utils.Constants.SLASH_UNDERSCORE; - -@DatatypeDef(name = "TreeNode") -public class TreeNode extends Type implements ICompositeType { - - @Child(name = "name", type = { StringType.class }, order = 0, min = 1, max = 1, modifier = false, summary = false) - protected StringType name; - - @Child(name = "nodeId", type = { StringType.class }, order = 2) - private StringType nodeId; - - @Child(name = "label", type = { StringType.class }, order = 3) - private StringType label; - - @Child(name = "node", type = { Location.class }, order = 4) - private Location node; - - @Child(name = "parent", type = { StringType.class }, order = 5) - private StringType parent; - - @Child(name = "children", type = { ChildTreeNode.class }, - order = 6, - min = 0, - max = -1, - modifier = false, - summary = false) - private List children; - - public TreeNode() { - children = new ArrayList<>(); - } - - public TreeNode(StringType name, StringType nodeId, StringType label, Location node, StringType parent) { - this.name = name; - this.nodeId = nodeId; - this.label = label; - this.node = node; - this.parent = parent; - } - - public StringType getName() { - if (name == null) { - name = new StringType(); - } - return name; - } - - public TreeNode setName(StringType name) { - this.name = name; - return this; - } - - public StringType getLabel() { - return label; - } - - public TreeNode setLabel(StringType label) { - this.label = label; - return this; - } - - @Override - public Type copy() { - TreeNode treeNode = new TreeNode(); - copyValues(treeNode); - return treeNode; - } - - @Override - public boolean isEmpty() { - return ElementUtil.isEmpty(node); - } - - @Override - protected Type typedCopy() { - return copy(); - } - - public StringType getNodeId() { - return nodeId; - } - - public TreeNode setNodeId(StringType nodeId) { - this.nodeId = nodeId; - return this; - } - - public Location getNode() { - return node; - } - - public TreeNode setNode(Location node) { - this.node = node; - return this; - } - - public StringType getParent() { - return parent; - } - - public TreeNode setParent(StringType parent) { - this.parent = parent; - return this; - } - - public List getChildren() { - if (children == null) { - children = new ArrayList<>(); - } - return children; - } - - public TreeNode setChildren(List children) { - this.children = children; - return this; - } - - public void addChild(TreeNode node) { - if (children == null) { - children = new ArrayList<>(); - } - ChildTreeNode childTreeNode = new ChildTreeNode(); - childTreeNode.setChildId(node.getNodeId()); - List treeNodeList = new ArrayList<>(); - TreeNode treeNode = new TreeNode(); - treeNode.setNode(node.getNode()); - treeNode.setNodeId(node.getNodeId()); - treeNode.setLabel(node.getLabel()); - treeNode.setParent(node.getParent()); - treeNodeList.add(treeNode); - childTreeNode.setChildren(treeNode); - 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; - } - } - } - } - } - return null; - } - -} diff --git a/src/main/java/org/smartregister/extension/rest/LocationHierarchyResourceProvider.java b/src/main/java/org/smartregister/extension/rest/LocationHierarchyResourceProvider.java deleted file mode 100755 index 4a9ed37..0000000 --- a/src/main/java/org/smartregister/extension/rest/LocationHierarchyResourceProvider.java +++ /dev/null @@ -1,95 +0,0 @@ -package org.smartregister.extension.rest; - -import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; -import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; -import ca.uhn.fhir.rest.annotation.*; -import ca.uhn.fhir.rest.api.server.IBundleProvider; -import ca.uhn.fhir.rest.param.*; -import ca.uhn.fhir.rest.server.IResourceProvider; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.smartregister.extension.model.LocationHierarchy; -import org.smartregister.extension.model.LocationHierarchyTree; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.hl7.fhir.r4.model.*; -import org.springframework.beans.factory.annotation.Autowired; - -import java.util.*; - -import static org.smartregister.extension.utils.Constants.*; - -public class LocationHierarchyResourceProvider implements IResourceProvider { - - @Autowired - IFhirResourceDao locationIFhirResourceDao; - - private static Logger logger = LogManager.getLogger(LocationHierarchyResourceProvider.class.toString()); - - @Override - public Class getResourceType() { - return LocationHierarchy.class; - } - - @Search - public LocationHierarchy getLocationHierarchy(@RequiredParam(name = IDENTIFIER) TokenParam identifier) { - - SearchParameterMap paramMap = new SearchParameterMap(); - paramMap.add(IDENTIFIER, identifier); - - IBundleProvider locationBundle = locationIFhirResourceDao.search(paramMap); - List locations = locationBundle != null ? - locationBundle.getResources(0, locationBundle.size()) : new ArrayList<>(); - Long id = null; - if (locations.size() > 0) { - id = locations.get(0) != null && locations.get(0).getIdElement() != null ? locations.get(0).getIdElement().getIdPartAsLong() : null; - } - - LocationHierarchyTree locationHierarchyTree = new LocationHierarchyTree(); - LocationHierarchy locationHierarchy = new LocationHierarchy(); - if (id != null && locations.size() > 0) { - logger.info("Building Location Hierarchy of Location Id : " + id); - locationHierarchyTree.buildTreeFromList(getLocationHierarchy(id, locations.get(0))); - StringType locationIdString = new StringType().setId(id.toString()).getIdElement(); - locationHierarchy.setLocationId(locationIdString); - locationHierarchy.setId(LOCATION_RESOURCE + id); - - locationHierarchy.setLocationHierarchyTree(locationHierarchyTree); - } else { - locationHierarchy.setId(LOCATION_RESOURCE_NOT_FOUND); - } - return locationHierarchy; - - } - - private List getLocationHierarchy(Long id, IBaseResource parentLocation) { - return descendants(id, parentLocation); - } - - public List descendants(Long id, IBaseResource parentLocation) { - - SearchParameterMap paramMap = new SearchParameterMap(); - ReferenceAndListParam thePartOf = new ReferenceAndListParam(); - ReferenceParam partOf = new ReferenceParam(); - partOf.setValue(LOCATION + FORWARD_SLASH + id); - ReferenceOrListParam referenceOrListParam = new ReferenceOrListParam(); - referenceOrListParam.add(partOf); - thePartOf.addValue(referenceOrListParam); - paramMap.add(PART_OF, thePartOf); - - IBundleProvider childLocationBundle = locationIFhirResourceDao.search(paramMap); - List allLocations = new ArrayList<>(); - if (parentLocation != null) { - allLocations.add((Location) parentLocation); - } - for (IBaseResource childLocation : childLocationBundle.getResources(0, childLocationBundle.size())) { - Location childLocationEntity = (Location) childLocation; - allLocations.add(childLocationEntity); - allLocations.addAll(descendants(childLocation.getIdElement().getIdPartAsLong(), null)); - } - return allLocations; - } - - public void setLocationIFhirResourceDao(IFhirResourceDao locationIFhirResourceDao) { - this.locationIFhirResourceDao = locationIFhirResourceDao; - } -} diff --git a/src/main/java/org/smartregister/extension/utils/Constants.java b/src/main/java/org/smartregister/extension/utils/Constants.java deleted file mode 100644 index eaf35a6..0000000 --- a/src/main/java/org/smartregister/extension/utils/Constants.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.smartregister.extension.utils; - -public interface Constants { - - String SLASH_UNDERSCORE = "/_"; - String LOCATION = "Location"; - String FORWARD_SLASH = "/"; - String IDENTIFIER = "identifier"; - String LOCATION_RESOURCE_NOT_FOUND = "Location Resource : Not Found"; - String LOCATION_RESOURCE = "Location Resource : "; - String PART_OF = "partof"; -} diff --git a/src/test/java/org/smartregister/extension/model/LocationHierarchyTreeTest.java b/src/test/java/org/smartregister/extension/model/LocationHierarchyTreeTest.java deleted file mode 100644 index fdb4bee..0000000 --- a/src/test/java/org/smartregister/extension/model/LocationHierarchyTreeTest.java +++ /dev/null @@ -1,104 +0,0 @@ -package org.smartregister.extension.model; - -import ca.uhn.fhir.rest.param.ReferenceParam; -import org.hl7.fhir.r4.model.Location; -import org.hl7.fhir.r4.model.Reference; -import org.junit.Test; - - -import java.util.ArrayList; -import java.util.List; - -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertNull; -import static org.junit.Assert.assertNotNull; - -public class LocationHierarchyTreeTest { - - @Test - public void testAddLocationWithoutChildLocations() { - Location location = new Location(); - location.setId("Location/1"); - location.setName("Test Location"); - Reference partOfReference = new Reference(); - partOfReference.setReference(""); - location.setPartOf(partOfReference); - LocationHierarchyTree locationHierarchyTree = new LocationHierarchyTree(); - locationHierarchyTree.addLocation(location); - - Tree tree = locationHierarchyTree.getLocationsHierarchy(); - assertNotNull(tree); - assertNotNull(tree.getTree()); - assertEquals("Location/1", tree.getTree().getTreeNodeId().getValue()); - assertEquals("Location/1", tree.getTree().getTreeNode().getNodeId().getValue()); - assertEquals("Test Location", tree.getTree().getTreeNode().getLabel().getValue()); - assertNull(tree.getTree().getTreeNode().getParent().getValue()); - assertEquals(0, tree.getTree().getTreeNode().getChildren().size()); - } - - @Test - public void testBuildTreeFromList() { - Location location1 = new Location(); - location1.setId("Location/1"); - location1.setName("Test Location"); - Reference partOfReference = new Reference(); - partOfReference.setReference(""); - location1.setPartOf(partOfReference); - - Location location2 = new Location(); - location2.setId("Location/2"); - location2.setName("Test Location 2"); - partOfReference = new Reference(); - partOfReference.setReference("Location/1"); - location2.setPartOf(partOfReference); - - Location location3 = new Location(); - location3.setId("Location/3"); - location3.setName("Test Location 3"); - partOfReference = new Reference(); - partOfReference.setReference("Location/2"); - location3.setPartOf(partOfReference); - - LocationHierarchyTree locationHierarchyTree = new LocationHierarchyTree(); - - List locationList = new ArrayList<>(); - locationList.add(location1); - locationList.add(location2); - locationList.add(location3); - - locationHierarchyTree.buildTreeFromList(locationList); - Tree tree = locationHierarchyTree.getLocationsHierarchy(); - assertNotNull(tree); - assertNotNull(tree.getTree()); - assertEquals("Location/1", tree.getTree().getTreeNodeId().getValue()); - assertEquals("Location/1", tree.getTree().getTreeNode().getNodeId().getValue()); - assertEquals("Test Location", tree.getTree().getTreeNode().getLabel().getValue()); - assertNull(tree.getTree().getTreeNode().getParent().getValue()); - assertEquals(1, tree.getTree().getTreeNode().getChildren().size()); - - assertEquals("Location/2", tree.getTree().getTreeNode().getChildren().get(0).getChildren().getNodeId().getValue()); - assertEquals("Test Location 2", tree.getTree().getTreeNode().getChildren().get(0).getChildren().getLabel().getValue()); - assertNotNull(tree.getTree().getTreeNode().getChildren().get(0).getChildren().getParent().getValue()); - assertEquals("Location/1", tree.getTree().getTreeNode().getChildren().get(0).getChildren().getParent().getValue()); - assertEquals(1, tree.getTree().getTreeNode().getChildren().get(0).getChildren().getChildren().size()); - - assertEquals("Location/3", tree.getTree().getTreeNode().getChildren().get(0).getChildren().getChildren().get(0).getChildren().getNodeId().getValue()); - assertEquals("Test Location 3", tree.getTree().getTreeNode().getChildren().get(0).getChildren().getChildren().get(0).getChildren().getLabel().getValue()); - assertNotNull(tree.getTree().getTreeNode().getChildren().get(0).getChildren().getChildren().get(0).getChildren().getParent().getValue()); - assertEquals("Location/2", tree.getTree().getTreeNode().getChildren().get(0).getChildren().getChildren().get(0).getChildren().getParent().getValue()); - assertEquals(0, tree.getTree().getTreeNode().getChildren().get(0).getChildren().getChildren().get(0).getChildren().getChildren().size()); - - assertNotNull(locationHierarchyTree.getLocationsHierarchy().getParentChildren()); - assertEquals(2, locationHierarchyTree.getLocationsHierarchy().getParentChildren().size()); - assertEquals("Location/1", locationHierarchyTree.getLocationsHierarchy().getParentChildren().get(0).getIdentifier().getValue()); - assertEquals(1, locationHierarchyTree.getLocationsHierarchy().getParentChildren().get(0).getChildIdentifiers().size()); - assertEquals("Location/2", locationHierarchyTree.getLocationsHierarchy().getParentChildren().get(0).getChildIdentifiers().get(0).getValue()); - - assertEquals("Location/2", locationHierarchyTree.getLocationsHierarchy().getParentChildren().get(1).getIdentifier().getValue()); - assertEquals(1, locationHierarchyTree.getLocationsHierarchy().getParentChildren().get(1).getChildIdentifiers().size()); - assertEquals("Location/3", locationHierarchyTree.getLocationsHierarchy().getParentChildren().get(1).getChildIdentifiers().get(0).getValue()); - - - } - -} diff --git a/src/test/java/org/smartregister/extension/model/TreeTest.java b/src/test/java/org/smartregister/extension/model/TreeTest.java deleted file mode 100644 index 00cf9cb..0000000 --- a/src/test/java/org/smartregister/extension/model/TreeTest.java +++ /dev/null @@ -1,57 +0,0 @@ -package org.smartregister.extension.model; - - -import org.hl7.fhir.r4.model.Location; -import org.junit.Test; - - -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertNull; -import static org.junit.Assert.assertNotNull; - -public class TreeTest { - - @Test - public void testAddingNodeWithOutParent() { - Tree tree = new Tree(); - Location location = new Location(); - location.setId("testId"); - tree.addNode("Location/1", "test", location, null); - - TreeNode treeNode = tree.getNode("Location/1"); - - assertEquals("Location/1", treeNode.getNodeId().getValue()); - assertEquals("test", treeNode.getLabel().getValue()); - assertEquals(location, treeNode.getNode()); - assertNull(treeNode.getParent().getValue()); - } - - @Test(expected = IllegalArgumentException.class) - public void testCannotReAddExistingNode() { - Tree tree = new Tree(); - Location location = new Location(); - location.setId("testId"); - tree.addNode("Location/1", "test", location, null); - tree.addNode("Location/1", "test", location, null); - } - - @Test - public void testAddingNodeWithValidParent() { - Tree tree = new Tree(); - Location location = new Location(); - location.setId("testId"); - tree.addNode("Location/1", "test", location, null); - tree.addNode("Location/2", "test2", location, "Location/1"); - - TreeNode childNode = tree.getNode("Location/2"); - - assertEquals("Location/2", childNode.getNodeId().getValue()); - assertEquals("test2", childNode.getLabel().getValue()); - assertEquals(location, childNode.getNode()); - assertNotNull(childNode.getParent().getValue()); - - String parentNodeId = childNode.getParent().getValue(); - - assertEquals("Location/1", parentNodeId); - } -}