Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add support for converting CQL Tuples to FHIR Parameters #1422

Merged
merged 10 commits into from
Oct 10, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,15 @@ public boolean isFhirType(Object value) {
}

@Override
public Iterable<Object> toFhirTypes(Iterable<?> values) {
public List<Object> toFhirTypes(Iterable<?> values) {
List<Object> converted = new ArrayList<>();
for (Object value : values) {
if (value == null) {
converted.add(null);
} else if (value instanceof Iterable<?>) {
converted.add(toFhirTypes((Iterable<?>) value));
} else if (isFhirType(value)) {
converted.add(value);
converted.add((IBase) value);
} else if (isCqlType(value)) {
converted.add(toFhirType(value));
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,13 +221,74 @@ public ICompositeType toFhirRange(Interval value) {
return range;
}

private static BooleanType emptyBooleanWithExtension(String url, Type value) {
var result = new BooleanType((String) null);
result.addExtension().setUrl(url).setValue(value);
return result;
}

private static void addPartWithNameAndValue(
Parameters.ParametersParameterComponent param, String key, Object value) {
var part = param.addPart().setName(key);
if (value instanceof Resource) {
part.setResource((Resource) value);
} else if (value instanceof Type) {
part.setValue((Type) value);
} else {
throw new IllegalArgumentException(
"Unsupported FHIR type: " + value.getClass().getName());
}
}

private static Iterable<?> asIterable(Object value) {
if (value instanceof Iterable) {
return (Iterable<?>) value;
} else {
return null;
}
}

@Override
public IBase toFhirTuple(Tuple value) {
if (value == null) {
return null;
}
var parameters = new Parameters();
if (value.getElements().isEmpty()) {
return parameters;
}

var param = parameters.addParameter();
for (String key : value.getElements().keySet()) {
var element = value.getElements().get(key);
if (element == null) {
// 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));
addPartWithNameAndValue(param, key, dataAbsentValue);
continue;
}

var iterable = asIterable(element);
if (iterable == null) {
// Single value
addPartWithNameAndValue(param, key, toFhirType(element));
} else {
if (!iterable.iterator().hasNext()) {
// Empty list
var emptyListValue = emptyBooleanWithExtension(EMPTY_LIST_EXT_URL, new BooleanType(true));
addPartWithNameAndValue(param, key, emptyListValue);
} else {
// Non-empty list, one part per value
var fhirTypes = this.toFhirTypes(iterable);
for (var fhirType : fhirTypes) {
addPartWithNameAndValue(param, key, fhirType);
}
}
}
}

throw new NotImplementedException("can't convert Tuples");
return parameters;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,13 +220,74 @@ public ICompositeType toFhirRange(Interval value) {
return range;
}

private static BooleanType emptyBooleanWithExtension(String url, Type value) {
var result = new BooleanType((String) null);
result.addExtension().setUrl(url).setValue(value);
return result;
}

private static void addPartWithNameAndValue(
Parameters.ParametersParameterComponent param, String key, Object value) {
var part = param.addPart().setName(key);
if (value instanceof Resource) {
part.setResource((Resource) value);
} else if (value instanceof Type) {
part.setValue((Type) value);
} else {
throw new IllegalArgumentException(
"Unsupported FHIR type: " + value.getClass().getName());
}
}

private static Iterable<?> asIterable(Object value) {
if (value instanceof Iterable) {
return (Iterable<?>) value;
} else {
return null;
}
}

@Override
public IBase toFhirTuple(Tuple value) {
if (value == null) {
return null;
}
var parameters = new Parameters();
if (value.getElements().isEmpty()) {
return parameters;
}

var param = parameters.addParameter();
for (String key : value.getElements().keySet()) {
var element = value.getElements().get(key);
if (element == null) {
// 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));
addPartWithNameAndValue(param, key, dataAbsentValue);
continue;
}

var iterable = asIterable(element);
if (iterable == null) {
// Single value
addPartWithNameAndValue(param, key, toFhirType(element));
} else {
if (!iterable.iterator().hasNext()) {
// Empty list
var emptyListValue = emptyBooleanWithExtension(EMPTY_LIST_EXT_URL, new BooleanType(true));
addPartWithNameAndValue(param, key, emptyListValue);
} else {
// Non-empty list, one part per value
var fhirTypes = this.toFhirTypes(iterable);
for (var fhirType : fhirTypes) {
addPartWithNameAndValue(param, key, fhirType);
}
}
}
}

throw new NotImplementedException("can't convert Tuples");
return parameters;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.opencds.cqf.cql.engine.fhir.converter;

import java.math.BigDecimal;
import java.util.List;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseCoding;
import org.hl7.fhir.instance.model.api.ICompositeType;
Expand Down Expand Up @@ -28,6 +29,11 @@
*/
public interface FhirTypeConverter {

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";
static final String CQL_TYPE_EXT_URL = "http://hl7.org/fhir/StructureDefinition/cqf-cqlType";

// CQL-to-FHIR conversions

/**
Expand All @@ -53,9 +59,9 @@ public interface FhirTypeConverter {
* nulls, and sublist hierarchy
*
* @param values an Iterable containing CQL structures, nulls, or sublists
* @return an Iterable containing FHIR types, nulls, and sublists
* @return an List containing FHIR types, nulls, and sublists
*/
public Iterable<Object> toFhirTypes(Iterable<?> values);
public List<Object> toFhirTypes(Iterable<?> values);

/**
* Converts a String to a FHIR Id
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,13 +212,74 @@ public ICompositeType toFhirRange(Interval value) {
return range;
}

private static BooleanType emptyBooleanWithExtension(String url, Type value) {
var result = new BooleanType((String) null);
result.addExtension().setUrl(url).setValue(value);
return result;
}

private static void addPartWithNameAndValue(
Parameters.ParametersParameterComponent param, String key, Object value) {
var part = param.addPart().setName(key);
if (value instanceof Resource) {
part.setResource((Resource) value);
} else if (value instanceof Type) {
part.setValue((Type) value);
} else {
throw new IllegalArgumentException(
"Unsupported FHIR type: " + value.getClass().getName());
}
}

private static Iterable<?> asIterable(Object value) {
if (value instanceof Iterable) {
return (Iterable<?>) value;
} else {
return null;
}
}

@Override
public IBase toFhirTuple(Tuple value) {
if (value == null) {
return null;
}
var parameters = new Parameters();
if (value.getElements().isEmpty()) {
return parameters;
}

var param = parameters.addParameter();
for (String key : value.getElements().keySet()) {
var element = value.getElements().get(key);
if (element == null) {
// 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));
addPartWithNameAndValue(param, key, dataAbsentValue);
continue;
}

var iterable = asIterable(element);
if (iterable == null) {
// Single value
addPartWithNameAndValue(param, key, toFhirType(element));
} else {
if (!iterable.iterator().hasNext()) {
// Empty list
var emptyListValue = emptyBooleanWithExtension(EMPTY_LIST_EXT_URL, new BooleanType(true));
addPartWithNameAndValue(param, key, emptyListValue);
} else {
// Non-empty list, one part per value
var fhirTypes = this.toFhirTypes(iterable);
for (var fhirType : fhirTypes) {
addPartWithNameAndValue(param, key, fhirType);
}
}
}
}

throw new NotImplementedException("can't convert Tuples");
return parameters;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,13 +212,74 @@ public ICompositeType toFhirRange(Interval value) {
return range;
}

private static BooleanType emptyBooleanWithExtension(String url, DataType value) {
var result = new BooleanType((String) null);
result.addExtension().setUrl(url).setValue(value);
return result;
}

private static void addPartWithNameAndValue(
Parameters.ParametersParameterComponent param, String key, Object value) {
var part = param.addPart().setName(key);
if (value instanceof Resource) {
part.setResource((Resource) value);
} else if (value instanceof DataType) {
part.setValue((DataType) value);
} else {
throw new IllegalArgumentException(
"Unsupported FHIR type: " + value.getClass().getName());
}
}

private static Iterable<?> asIterable(Object value) {
if (value instanceof Iterable) {
return (Iterable<?>) value;
} else {
return null;
}
}

@Override
public IBase toFhirTuple(Tuple value) {
if (value == null) {
return null;
}
var parameters = new Parameters();
if (value.getElements().isEmpty()) {
return parameters;
}

var param = parameters.addParameter();
for (String key : value.getElements().keySet()) {
var element = value.getElements().get(key);
if (element == null) {
// 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));
addPartWithNameAndValue(param, key, dataAbsentValue);
continue;
}

var iterable = asIterable(element);
if (iterable == null) {
// Single value
addPartWithNameAndValue(param, key, toFhirType(element));
} else {
if (!iterable.iterator().hasNext()) {
// Empty list
var emptyListValue = emptyBooleanWithExtension(EMPTY_LIST_EXT_URL, new BooleanType(true));
addPartWithNameAndValue(param, key, emptyListValue);
} else {
// Non-empty list, one part per value
var fhirTypes = this.toFhirTypes(iterable);
for (var fhirType : fhirTypes) {
addPartWithNameAndValue(param, key, fhirType);
}
}
}
}

throw new NotImplementedException("can't convert Tuples");
return parameters;
}

@Override
Expand Down
Loading
Loading