Skip to content

Commit

Permalink
Merge pull request #79 from OpenSRP/support-evaluating-string-express…
Browse files Browse the repository at this point in the history
…ion-on-bundle

Add methods to extract data from a resource bundle
  • Loading branch information
vincent-karuri committed Nov 17, 2020
2 parents 13b3986 + 07dc310 commit 9a0d4cf
Show file tree
Hide file tree
Showing 4 changed files with 2,725 additions and 32 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<artifactId>opensrp-plan-evaluator</artifactId>
<packaging>jar</packaging>
<version>0.1.6-SNAPSHOT</version>
<version>0.2.0-SNAPSHOT</version>
<name>OpenSRP Plan Evaluator</name>
<description>OpenSRP Plan Evaluator Library</description>
<url>https://github.com/OpenSRP/opensrp-plan-evaluator</url>
Expand Down
130 changes: 111 additions & 19 deletions src/main/java/org/smartregister/pathevaluator/PathEvaluatorLibrary.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,17 @@
*/
package org.smartregister.pathevaluator;

import java.util.Collection;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.ibm.fhir.model.resource.Bundle;
import com.ibm.fhir.model.resource.DomainResource;
import com.ibm.fhir.model.resource.Resource;
import com.ibm.fhir.model.type.Element;
import com.ibm.fhir.path.FHIRPathBooleanValue;
import com.ibm.fhir.path.FHIRPathElementNode;
import com.ibm.fhir.path.FHIRPathNode;
import com.ibm.fhir.path.FHIRPathStringValue;
import com.ibm.fhir.path.evaluator.FHIRPathEvaluator;
import com.ibm.fhir.path.exception.FHIRPathException;
import lombok.Getter;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.commons.text.StringEscapeUtils;
import org.smartregister.pathevaluator.dao.ClientDao;
Expand All @@ -19,16 +25,12 @@
import org.smartregister.pathevaluator.dao.TaskDao;
import org.smartregister.pathevaluator.dao.TaskProvider;

import com.ibm.fhir.model.resource.DomainResource;
import com.ibm.fhir.model.resource.Resource;
import com.ibm.fhir.path.FHIRPathBooleanValue;
import com.ibm.fhir.path.FHIRPathElementNode;
import com.ibm.fhir.path.FHIRPathNode;
import com.ibm.fhir.path.FHIRPathStringValue;
import com.ibm.fhir.path.evaluator.FHIRPathEvaluator;
import com.ibm.fhir.path.exception.FHIRPathException;

import lombok.Getter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* @author Samuel Githengi created on 06/15/20
Expand Down Expand Up @@ -68,6 +70,9 @@ public static void init(LocationDao locationDao, ClientDao clientDao, TaskDao ta
* @return the instance
*/
public static PathEvaluatorLibrary getInstance() {
if (instance == null) {
PathEvaluatorLibrary.init(null, null, null, null);
}
return instance;
}

Expand Down Expand Up @@ -111,21 +116,108 @@ public FHIRPathElementNode evaluateElementExpression(DomainResource resource, St
return iterator.hasNext() ? iterator.next().asElementNode() : null;
}
catch (FHIRPathException e) {
logger.log(Level.SEVERE, "Error execuring expression " + expression, e);
logger.log(Level.SEVERE, "Error executing expression " + expression, e);
return null;
}
}

public FHIRPathStringValue evaluateStringExpression(DomainResource resource, String expression) {

try {
Iterator<FHIRPathNode> iterator = fhirPathEvaluator.evaluate(resource, expression).iterator();
return iterator.hasNext() ? iterator.next().as(FHIRPathStringValue.class) : null;
} catch (FHIRPathException e) {
logger.log(Level.SEVERE, "Error executing expression " + expression, e);
return null;
}
catch (FHIRPathException e) {
logger.log(Level.SEVERE, "Error execuring expression " + expression, e);
}

/**
* Evaluates a FHIR Path {@param expression} on a {@param bundle} and returns a String result
* or null if the query fails or doesn't have results
*
* @param bundle
* @param expression
* @return
*/

public String extractStringFromBundle(Bundle bundle, String expression) {
try {
Iterator<FHIRPathNode> iterator = fhirPathEvaluator.evaluate(bundle, expression).iterator();
return iterator.hasNext() ? convertElementNodeValToStr(iterator.next().asElementNode()) : null;
} catch (FHIRPathException e) {
logger.log(Level.SEVERE, "Error executing expression " + expression, e);
return null;
}
}


/**
* Evaluates a FHIR Path {@param expression} on a {@param bundle} and returns a List of String results
* or null if the query fails or doesn't have results
*
* @param bundle
* @param expression
* @return
*/
public List<String> extractStringsFromBundle(Bundle bundle, String expression) {
List<String> strs = new ArrayList<>();
try {
Iterator<FHIRPathNode> iterator = fhirPathEvaluator.evaluate(bundle, expression).iterator();
while (iterator.hasNext()) {
strs.add(convertElementNodeValToStr(iterator.next().asElementNode()));
}
} catch (FHIRPathException e) {
logger.log(Level.SEVERE, "Error executing expression " + expression, e);
}
return strs;
}

/**
* Returns a {@link FHIRPathElementNode}'s value as a String
* @param fhirPathElementNode
* @return
*/
private String convertElementNodeValToStr(FHIRPathElementNode fhirPathElementNode) {
return fhirPathElementNode.getValue().asStringValue().string();
}

/**
* Evaluates a FHIR Path {@param expression} on a {@param bundle} and returns a List of {@link Element}s
* or null if the query fails or doesn't have results
*
* @param bundle
* @param expression
* @return
*/
public List<Element> extractElementsFromBundle(Bundle bundle, String expression) {
List<Element> elements = new ArrayList<>();
try {
Iterator<FHIRPathNode> iterator = fhirPathEvaluator.evaluate(bundle, expression).iterator();
while (iterator.hasNext()) {
elements.add(iterator.next().asElementNode().element());
}
} catch (FHIRPathException e) {
logger.log(Level.SEVERE, "Error executing expression " + expression, e);
return elements;
}
return elements;
}

/**
* Evaluates a FHIR Path {@param expression} on a {@param bundle} and returns a {@link Resource} result
* or null if the query fails or doesn't have results
*
* @param bundle
* @param expression
* @return
*/
public Resource extractResourceFromBundle(Bundle bundle, String expression) {
try {
Iterator<FHIRPathNode> iterator = fhirPathEvaluator.evaluate(bundle, expression).iterator();
return iterator.hasNext() ? iterator.next().asResourceNode().resource() : null;
} catch (FHIRPathException e) {
logger.log(Level.SEVERE, "Error executing expression " + expression, e);
return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,35 @@
*/
package org.smartregister.pathevaluator;

import static com.ibm.fhir.model.type.String.of;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import java.util.Calendar;

import org.junit.Before;
import org.junit.Test;

import com.ibm.fhir.model.format.Format;
import com.ibm.fhir.model.parser.FHIRParser;
import com.ibm.fhir.model.resource.Bundle;
import com.ibm.fhir.model.resource.DeviceDefinition;
import com.ibm.fhir.model.resource.Location;
import com.ibm.fhir.model.resource.Patient;
import com.ibm.fhir.model.type.Date;
import com.ibm.fhir.model.type.Element;
import com.ibm.fhir.model.type.HumanName;
import com.ibm.fhir.model.type.Identifier;
import com.ibm.fhir.model.type.Reference;
import com.ibm.fhir.path.FHIRPathStringValue;
import com.ibm.fhir.path.exception.FHIRPathException;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.powermock.reflect.Whitebox;

import java.io.InputStream;
import java.util.Calendar;
import java.util.List;

import static com.ibm.fhir.model.type.String.of;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;

/**
* @author Samuel Githengi created on 06/10/20
Expand All @@ -30,10 +41,12 @@ public class PathEvaluatorLibraryTest {
private PathEvaluatorLibrary pathEvaluatorLibrary;

private Patient patient;

private Bundle deviceDefinitionBundle;

@Before
public void startUp() {
PathEvaluatorLibrary.init(null, null, null, null);
Whitebox.setInternalState(PathEvaluatorLibrary.class, "instance", (Object) null);
pathEvaluatorLibrary = PathEvaluatorLibrary.getInstance();
patient = Patient.builder().id("12345").birthDate(Date.of("1990-12-19"))
.identifier(Identifier.builder().id("1234").value(of("1212313")).build())
Expand All @@ -43,6 +56,11 @@ public void startUp() {
builder.id("12345");
builder.reference(of(patient.getId()));
}

@After
public void tearDown() {
Whitebox.setInternalState(PathEvaluatorLibrary.class, "instance", (Object) null);
}

@Test
public void testWhere() {
Expand Down Expand Up @@ -109,5 +127,70 @@ public void testEvaluateActionExpressions() {
node = pathEvaluatorLibrary.evaluateStringExpression(patient, "'Cancelled'");
assertEquals("Cancelled", node.string());
}


@Test
public void testExtractStringFromBundleShouldReturnCorrectString() throws Exception {
verifyCorrectStringIsExtracted("d3fdac0e-061e-b068-2bed-5a95e803636f", "Collect blood sample");
verifyCorrectStringIsExtracted("620d3142-0a70-de75-88bb-8ad688195663", "Collect 2 blood samples");
}

@Test
public void testExtractStringsFromBundleShouldReturnAllRelevantStrings() throws Exception {
List<String> strs = pathEvaluatorLibrary.extractStringsFromBundle(getDeviceDefinitionBundle(), "$this.entry.resource.identifier.value");
assertTrue(strs.contains("620d3142-0a70-de75-88bb-8ad688195663"));
assertTrue(strs.contains("8020a21e-671e-0c31-717a-bf6735800677"));
assertTrue(strs.contains("d8d7a874-2760-a382-de82-59c07fee0db6"));
assertTrue(strs.contains("47540dbf-ec9a-4c06-c684-eb626795b332"));
}

@Test
public void testExtractElementsFromBundleShouldGetAllRelevantElements() throws Exception {
List<Element> elements = pathEvaluatorLibrary.extractElementsFromBundle(getDeviceDefinitionBundle(), "$this.entry.resource.where(identifier.where(value='d3fdac0e-061e-b068-2bed-5a95e803636f')).property.where(type.where(text='RDTScan Configuration')).valueCode");
assertEquals(9, elements.size());
}

@Test
public void testExtractResourceFromBundleShouldExtractCorrectResource() throws Exception {
verifyResourceIsExtracted("d3fdac0e-061e-b068-2bed-5a95e803636f");
verifyResourceIsExtracted("cf4443a1-f582-74ea-be89-ae53b5fd7bfe");
}

@Test
public void testExtractStringFromBundleShouldReturnNullWhenFHIRExceptionOccurs() {
Assert.assertNull(pathEvaluatorLibrary.extractStringFromBundle(mock(Bundle.class), "null=null"));
}

@Test
public void testExtractStringsFromBundleShouldReturnEmptyListWhenFHIRExceptionOccurs() {
Assert.assertEquals(0, pathEvaluatorLibrary.extractStringsFromBundle(mock(Bundle.class), "null=null").size());
}

@Test
public void testExtractElementsFromBundleShouldReturnEmptyListWhenFHIRExceptionOccurs() {
Assert.assertEquals(0, pathEvaluatorLibrary.extractElementsFromBundle(mock(Bundle.class), "null=null").size());
}

@Test
public void testExtractResourceFromBundleShouldReturnNullWhenFHIRExceptionOccurs() {
Assert.assertNull(pathEvaluatorLibrary.extractResourceFromBundle(mock(Bundle.class), "null=null"));
}

private void verifyCorrectStringIsExtracted(String resourceId, String expectedString) throws Exception {
String str = pathEvaluatorLibrary.extractStringFromBundle(getDeviceDefinitionBundle(), String.format("$this.entry.resource.where(identifier.where(value='%s')).capability.where(type.where(text='instructions')).description.text", resourceId));
assertEquals(expectedString, str);
}

private void verifyResourceIsExtracted(String resourceId) throws Exception {
DeviceDefinition resource = (DeviceDefinition) pathEvaluatorLibrary.extractResourceFromBundle(getDeviceDefinitionBundle(), String.format("$this.entry.resource.where(identifier.where(value='%s')))", resourceId));
assertNotNull(resource);
assertEquals(resourceId, resource.getIdentifier().get(0).getValue().getValue());
}

private Bundle getDeviceDefinitionBundle() throws Exception {
if (deviceDefinitionBundle == null) {
InputStream stream = getClass().getClassLoader().getResourceAsStream("DeviceDefinition.json");
deviceDefinitionBundle = FHIRParser.parser(Format.JSON).parse(stream);
}
return deviceDefinitionBundle;
}
}
Loading

0 comments on commit 9a0d4cf

Please sign in to comment.