Skip to content

Commit

Permalink
Handle Empty Lists and Null EvaluationResults in CqlFhirParametersCon…
Browse files Browse the repository at this point in the history
…verter
  • Loading branch information
jreyno77 committed Nov 25, 2024
1 parent 187daa0 commit 35a462e
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 11 deletions.
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
package org.opencds.cqf.fhir.cql.engine.parameters;

import static java.util.Objects.requireNonNull;

import ca.uhn.fhir.context.FhirContext;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static java.util.Objects.requireNonNull;
import java.util.Optional;
import java.util.stream.Collectors;

import org.hl7.fhir.instance.model.api.IBaseBackboneElement;
import org.hl7.fhir.instance.model.api.IBaseDatatype;
import org.hl7.fhir.instance.model.api.IBaseExtension;
import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r4.model.BooleanType;
import org.hl7.fhir.r4.model.CodeType;
import org.hl7.fhir.r4.model.Type;
import org.opencds.cqf.cql.engine.execution.EvaluationResult;
import org.opencds.cqf.cql.engine.execution.ExpressionResult;
import org.opencds.cqf.cql.engine.fhir.converter.FhirTypeConverter;
Expand All @@ -26,6 +28,8 @@
import org.opencds.cqf.fhir.utility.model.FhirModelResolverCache;
import org.slf4j.LoggerFactory;

import ca.uhn.fhir.context.FhirContext;

public class CqlFhirParametersConverter {

org.slf4j.Logger logger = LoggerFactory.getLogger(CqlFhirParametersConverter.class);
Expand All @@ -36,6 +40,9 @@ public class CqlFhirParametersConverter {
// private IFhirPath fhirPath;
private ModelResolver modelResolver;

/*
* Converts both CQL parameters and CQL Evaluation Results into Fhir Parameters Resources
*/
public CqlFhirParametersConverter(
FhirContext fhirContext, IAdapterFactory adapterFactory, FhirTypeConverter fhirTypeConverter) {
this.fhirContext = requireNonNull(fhirContext);
Expand All @@ -47,6 +54,22 @@ public CqlFhirParametersConverter(
// this.fhirPath = FhirPathCache.cachedForContext(fhirContext);
}

// This is basically a copy and paste from R4FhirTypeConverter, but it's not exposed.
static final String EMPTY_LIST_EXT_URL = "http://hl7.org/fhir/StructureDefinition/cqf-isEmptyList";
static final String DATA_ABSENT_REASON_EXT_URL = "http://hl7.org/fhir/StructureDefinition/data-absent-reason";
static final String DATA_ABSENT_REASON_UNKNOWN_CODE = "unknown";
private static Iterable<?> asIterable(Object value) {
if (value instanceof Iterable) {
return (Iterable<?>) value;
} else {
return null;
}
}
private static BooleanType emptyBooleanWithExtension(String url, Type value) {
var result = new BooleanType((String) null);
result.addExtension().setUrl(url).setValue(value);
return result;
}
public IBaseParameters toFhirParameters(EvaluationResult evaluationResult) {
IBaseParameters params = null;
try {
Expand All @@ -67,11 +90,20 @@ public IBaseParameters toFhirParameters(EvaluationResult evaluationResult) {
Object value = entry.getValue().value();

if (value == null) {
this.addPart(pa, name);
// Null value, add a single empty value with an extension indicating the reason
var dataAbsentValue = emptyBooleanWithExtension(
DATA_ABSENT_REASON_EXT_URL, new CodeType(DATA_ABSENT_REASON_UNKNOWN_CODE));
addPart(pa, name, dataAbsentValue);
continue;
}

if (value instanceof Iterable) {
var iterable = asIterable(value);
if (!iterable.iterator().hasNext()) {
// Empty list
var emptyListValue = emptyBooleanWithExtension(EMPTY_LIST_EXT_URL, new BooleanType(true));
addPart(pa, name, emptyListValue);
}
Iterable<?> values = (Iterable<?>) value;
for (Object o : values) {
this.addPart(pa, name, o);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
package org.opencds.cqf.fhir.cql.engine.parameters;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.hl7.fhir.r4.model.BooleanType;
import org.hl7.fhir.r4.model.CodeType;
import org.hl7.fhir.r4.model.DateTimeType;
import org.hl7.fhir.r4.model.Encounter;
import org.hl7.fhir.r4.model.ParameterDefinition;
Expand All @@ -19,6 +15,10 @@
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.Period;
import org.hl7.fhir.r4.model.StringType;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.opencds.cqf.cql.engine.execution.EvaluationResult;
Expand All @@ -29,6 +29,9 @@
import org.opencds.cqf.cql.engine.runtime.Interval;
import org.opencds.cqf.fhir.utility.adapter.IAdapterFactory;

import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;

class CqlFhirParametersConverterTests {

protected static CqlFhirParametersConverter cqlFhirParametersConverter;
Expand Down Expand Up @@ -59,6 +62,40 @@ void TestEvaluationResultToParameters() {
assertTrue(expected.equalsDeep(actual));
}

@Test
void TestEvaluationResultToEmptyListParameters() {
Parameters expected = new Parameters();
expected.addParameter().setName("Patient").setResource(new Patient());
BooleanType nullBooleanValue = new BooleanType((String) null);
nullBooleanValue.addExtension("http://hl7.org/fhir/StructureDefinition/cqf-isEmptyList", new BooleanType(true));
expected.addParameter().setName("Encounters").setValue(nullBooleanValue);

EvaluationResult testData = new EvaluationResult();
testData.expressionResults.put("Patient", new ExpressionResult(new Patient(), null));
testData.expressionResults.put("Encounters", new ExpressionResult(Collections.emptyList(), null));

Parameters actual = (Parameters) cqlFhirParametersConverter.toFhirParameters(testData);

assertTrue(expected.equalsDeep(actual));
}

@Test
void TestEvaluationResultNullParameters() {
Parameters expected = new Parameters();
expected.addParameter().setName("Patient").setResource(new Patient());
BooleanType nullBooleanValue = new BooleanType((String) null);
nullBooleanValue.addExtension("http://hl7.org/fhir/StructureDefinition/data-absent-reason", new CodeType("unknown"));
expected.addParameter().setName("Null").setValue(nullBooleanValue);

EvaluationResult testData = new EvaluationResult();
testData.expressionResults.put("Patient", new ExpressionResult(new Patient(), null));
testData.expressionResults.put("Null", new ExpressionResult(null, null));

Parameters actual = (Parameters) cqlFhirParametersConverter.toFhirParameters(testData);

assertTrue(expected.equalsDeep(actual));
}

@Test
void TestFhirParametersToCqlParameters() {
Map<String, Object> expected = new HashMap<>();
Expand Down

0 comments on commit 35a462e

Please sign in to comment.