Skip to content

Commit

Permalink
#1146: Added support for terminology filtering through a reference (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
brynrhodes authored Oct 3, 2023
1 parent 2321019 commit 3ecf27c
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3474,23 +3474,96 @@ else if (classType.getPrimaryCodePath() != null) {

// Only expand a choice-valued code path if no comparator is specified
// Otherwise, a code comparator will always choose a specific representation
boolean hasFHIRHelpers = libraryInfo.resolveLibraryName("FHIRHelpers") != null;
if (property != null && property.getResultType() instanceof ChoiceType && codeComparator == null) {
for (DataType propertyType : ((ChoiceType)property.getResultType()).getTypes()) {
Retrieve retrieve = buildRetrieve(ctx, useStrictRetrieveTyping, namedType, classType, codePath,
codeComparator, property, propertyType, propertyException, terminology);
retrieves.add(retrieve);
retrieve.setResultType(new ListType((DataType) namedType));
if (hasFHIRHelpers && propertyType instanceof NamedType && ((NamedType)propertyType).getSimpleName().equals("Reference") && namedType.getSimpleName().equals("MedicationRequest")) {
// TODO: This is a model-specific special case to support QICore
// This functionality needs to be generalized to a retrieve mapping in the model info
// But that requires a model info change (to represent references, right now the model info only includes context relationships)
// The reference expands to [MedicationRequest] MR with [Medication] M such that M.id = Last(Split(MR.medication.reference, '/')) and M.code in <valueset>
Retrieve mrRetrieve = buildRetrieve(ctx, useStrictRetrieveTyping, namedType, classType, null, null, null, null, null, null);
retrieves.add(mrRetrieve);
mrRetrieve.setResultType(new ListType((DataType) namedType));
DataType mDataType = libraryBuilder.resolveTypeName(model, "Medication");
ClassType mClassType = (ClassType)mDataType;
NamedType mNamedType = mClassType;
Retrieve mRetrieve = buildRetrieve(ctx, useStrictRetrieveTyping, mNamedType, mClassType, null, null, null, null, null, null);
retrieves.add(mRetrieve);
mRetrieve.setResultType(new ListType((DataType) namedType));
Query q = of.createQuery();
AliasedQuerySource aqs = of.createAliasedQuerySource().withExpression(mrRetrieve).withAlias("MR");
track(aqs, ctx);
aqs.setResultType(aqs.getExpression().getResultType());
q.getSource().add(aqs);
track(q, ctx);
q.setResultType(aqs.getResultType());
With w = of.createWith().withExpression(mRetrieve).withAlias("M");
track(w, ctx);
w.setResultType(w.getExpression().getResultType());
q.getRelationship().add(w);
String idPath = "id";
DataType idType = libraryBuilder.resolvePath(mDataType, idPath);
Property idProperty = libraryBuilder.buildProperty("M", idPath, false, idType);
String refPath = "medication.reference";
DataType refType = libraryBuilder.resolvePath(dataType, refPath);
Property refProperty = libraryBuilder.buildProperty("MR", refPath, false, refType);
Split split = of.createSplit().withStringToSplit(refProperty).withSeparator(libraryBuilder.createLiteral("/"));
libraryBuilder.resolveCall("System", "Split", new SplitInvocation(split));
Last last = of.createLast().withSource(split);
libraryBuilder.resolveCall("System", "Last", new LastInvocation(last));
Equal e = of.createEqual().withOperand(idProperty, last);
libraryBuilder.resolveBinaryCall("System", "Equal", e);

DataType mCodeType = libraryBuilder.resolvePath((DataType)mNamedType, "code");
Property mProperty = of.createProperty().withPath("code");
mProperty.setResultType(mCodeType);
String mCodeComparator = "~";
if (terminology.getResultType() instanceof ListType) {
mCodeComparator = "in";

}
else if (libraryBuilder.isCompatibleWith("1.5")) {
mCodeComparator = terminology.getResultType().isSubTypeOf(libraryBuilder.resolveTypeName("System", "Vocabulary")) ? "in" : "~";
}

if (result == null) {
result = retrieve;
Expression terminologyComparison = null;
if (mCodeComparator.equals("in")) {
terminologyComparison = libraryBuilder.resolveIn(mProperty, terminology);
}
else {
BinaryExpression equivalent = of.createEquivalent().withOperand(mProperty, terminology);
libraryBuilder.resolveBinaryCall("System", "Equivalent", equivalent);
terminologyComparison = equivalent;
}
And a = of.createAnd().withOperand(e, terminologyComparison);
libraryBuilder.resolveBinaryCall("System", "And", a);
w.withSuchThat(a);

if (result == null) {
result = q;
}
else {
track(q, ctx);
result = libraryBuilder.resolveUnion(result, q);
}
}
else {
// Should only include the result if it resolved appropriately with the codeComparator
// Allowing it to go through for now
//if (retrieve.getCodeProperty() != null && retrieve.getCodeComparator() != null && retrieve.getCodes() != null) {
Retrieve retrieve = buildRetrieve(ctx, useStrictRetrieveTyping, namedType, classType, codePath,
codeComparator, property, propertyType, propertyException, terminology);
retrieves.add(retrieve);
retrieve.setResultType(new ListType((DataType) namedType));

if (result == null) {
result = retrieve;
} else {
// Should only include the result if it resolved appropriately with the codeComparator
// Allowing it to go through for now
//if (retrieve.getCodeProperty() != null && retrieve.getCodeComparator() != null && retrieve.getCodes() != null) {
track(retrieve, ctx);
result = libraryBuilder.resolveUnion(result, retrieve);
//}
//}
}
}
}
}
Expand Down Expand Up @@ -3522,7 +3595,7 @@ private Retrieve buildRetrieve(cqlParser.RetrieveContext ctx, boolean useStrictR
retrieve.setContext(contextExpression);
}

if (ctx.terminology() != null) {
if (terminology != null) {
// Resolve the terminology target using an in or ~ operator
try {
if (codeComparator == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,44 @@ public void TestMedicationRequest() throws IOException {
assertThat(r.getCodes(), instanceOf(ValueSetRef.class));
ValueSetRef vsr = (ValueSetRef)r.getCodes();
assertThat(vsr.getName(), is("Antithrombotic Therapy"));

def = defs.get("Antithrombotic Therapy at Discharge (2)");
assertThat(def, notNullValue());
assertThat(def.getExpression(), instanceOf(Union.class));
Union u = (Union)def.getExpression();
assertThat(u.getOperand().size(), is(2));
assertThat(u.getOperand().get(0), instanceOf(Retrieve.class));
r = (Retrieve)u.getOperand().get(0);
assertThat(r.getTemplateId(), is("http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-medicationrequest"));
assertThat(r.getCodeProperty(), is("medication"));
assertThat(r.getCodeComparator(), is("in"));
assertThat(r.getCodes(), instanceOf(ValueSetRef.class));
vsr = (ValueSetRef)r.getCodes();
assertThat(vsr.getName(), is("Antithrombotic Therapy"));

assertThat(u.getOperand().get(1), instanceOf(Query.class));
q = (Query)u.getOperand().get(1);
assertThat(q.getSource().size(), is(1));
assertThat(q.getSource().get(0).getExpression(), instanceOf(Retrieve.class));
r = (Retrieve)q.getSource().get(0).getExpression();
assertThat(r.getTemplateId(), is("http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-medicationrequest"));
assertThat(r.getCodeProperty() == null, is(true));
assertThat(r.getCodes() == null, is(true));
assertThat(q.getRelationship(), notNullValue());
assertThat(q.getRelationship().size(), is(1));
assertThat(q.getRelationship().get(0), instanceOf(With.class));
With w = (With)q.getRelationship().get(0);
assertThat(w.getExpression(), instanceOf(Retrieve.class));
r = (Retrieve)w.getExpression();
assertThat(r.getTemplateId(), is("http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-medication"));
assertThat(r.getCodeProperty() == null, is(true));
assertThat(r.getCodes() == null, is(true));
assertThat(w.getSuchThat(), instanceOf(And.class));
And a = (And)w.getSuchThat();
assertThat(a.getOperand().get(0), instanceOf(Equal.class));
assertThat(a.getOperand().get(1), instanceOf(InValueSet.class));
InValueSet ivs = (InValueSet)a.getOperand().get(1);
assertThat(ivs.getValueset().getName(), is("Antithrombotic Therapy"));
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@ context Patient

define "Antithrombotic Therapy at Discharge":
["MedicationRequest": medication in "Antithrombotic Therapy"] Antithrombotic

define "Antithrombotic Therapy at Discharge (2)":
["MedicationRequest": "Antithrombotic Therapy"]
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,24 @@
"dataRequirement": [ {
"type": "Patient",
"profile": [ "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-patient" ]
}, {
"extension": [ {
"url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-relatedRequirement",
"extension": [ {
"url": "targetId",
"valueString": "G10002"
}, {
"url": "targetProperty",
"valueString": "medication"
} ]
} ],
"type": "Medication",
"profile": [ "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-medication" ],
"mustSupport": [ "id" ]
}, {
"id": "G10002",
"type": "MedicationRequest",
"profile": [ "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-medicationrequest" ],
"mustSupport": [ "medication.reference" ]
} ]
}
Original file line number Diff line number Diff line change
Expand Up @@ -203,3 +203,18 @@ define TestMedicationRequirement:
define TestBoundDataRequirement:
TestMedicationRequirement R
where R.code in "Aspirin"


define TestChoiceMedicationRequest:
[MedicationRequest: "Aspirin"]

// If the terminology target is a choice of a terminology-valued element and a reference

define TestChoiceMedicationRequestExpanded:
[MedicationRequest: medication in "Aspirin"]
union (
[MedicationRequest] MR
with [Medication] M
such that M.id = Last(Split(MR.medication.reference, '/'))
and M.code in "Aspirin"
)

0 comments on commit 3ecf27c

Please sign in to comment.