Skip to content

Commit 16a89aa

Browse files
#1146: Additional fixes for Medication-related resource retrieves (#1478)
Co-authored-by: JP <[email protected]>
1 parent 63b3bc4 commit 16a89aa

File tree

6 files changed

+199
-24
lines changed

6 files changed

+199
-24
lines changed

Src/java/cql-to-elm/src/main/java/org/cqframework/cql/cql2elm/Cql2ElmVisitor.java

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2919,16 +2919,19 @@ public Expression visitRetrieve(cqlParser.RetrieveContext ctx) {
29192919
if (hasFHIRHelpers
29202920
&& propertyType instanceof NamedType
29212921
&& ((NamedType) propertyType).getSimpleName().equals("Reference")
2922-
&& namedType.getSimpleName().equals("MedicationRequest")) {
2922+
&& (namedType.getSimpleName().equals("MedicationRequest")
2923+
|| namedType.getSimpleName().equals("MedicationAdministration")
2924+
|| namedType.getSimpleName().equals("MedicationDispense")
2925+
|| namedType.getSimpleName().equals("MedicationStatement"))) {
29232926
// TODO: This is a model-specific special case to support QICore
29242927
// This functionality needs to be generalized to a retrieve mapping in the model
2925-
// info
2926-
// But that requires a model info change (to represent references, right now the
2927-
// model info only
2928-
// includes context relationships)
2929-
// The reference expands to [MedicationRequest] MR with [Medication] M such that
2930-
// M.id =
2931-
// Last(Split(MR.medication.reference, '/')) and M.code in <valueset>
2928+
// info. But that requires a model info change (to represent references, right
2929+
// now the model info only includes context relationships)
2930+
// The reference expands to
2931+
// [MedicationRequest] MR
2932+
// with [Medication] M
2933+
// such that M.id = Last(Split(MR.medication.reference, '/'))
2934+
// and M.code in <valueset>
29322935
Retrieve mrRetrieve = buildRetrieve(
29332936
ctx, useStrictRetrieveTyping, namedType, classType, null, null, null, null, null, null);
29342937
retrieves.add(mrRetrieve);
@@ -2939,7 +2942,7 @@ public Expression visitRetrieve(cqlParser.RetrieveContext ctx) {
29392942
Retrieve mRetrieve = buildRetrieve(
29402943
ctx, useStrictRetrieveTyping, mNamedType, mClassType, null, null, null, null, null, null);
29412944
retrieves.add(mRetrieve);
2942-
mRetrieve.setResultType(new ListType((DataType) namedType));
2945+
mRetrieve.setResultType(new ListType(mDataType));
29432946
Query q = of.createQuery();
29442947
AliasedQuerySource aqs = of.createAliasedQuerySource()
29452948
.withExpression(mrRetrieve)
@@ -2968,9 +2971,27 @@ public Expression visitRetrieve(cqlParser.RetrieveContext ctx) {
29682971
Equal e = of.createEqual().withOperand(idProperty, last);
29692972
libraryBuilder.resolveBinaryCall("System", "Equal", e);
29702973

2974+
// Apply target mapping if this is a profile-informed model info
2975+
if (DataTypes.equal(idType, libraryBuilder.resolveTypeName("System", "String"))) {
2976+
idProperty.setPath("id.value");
2977+
}
2978+
if (DataTypes.equal(refType, libraryBuilder.resolveTypeName("System", "String"))) {
2979+
refProperty.setPath("medication.reference.value");
2980+
}
2981+
29712982
DataType mCodeType = libraryBuilder.resolvePath((DataType) mNamedType, "code");
2972-
Property mProperty = of.createProperty().withPath("code");
2973-
mProperty.setResultType(mCodeType);
2983+
Property mProperty = libraryBuilder.buildProperty("M", "code", false, mCodeType);
2984+
Expression mCodeProperty = mProperty;
2985+
2986+
// Apply target mapping if this is a profile-informed model info
2987+
if (DataTypes.equal(mCodeType, libraryBuilder.resolveTypeName("System", "Concept"))) {
2988+
FunctionRef toConcept = of.createFunctionRef()
2989+
.withLibraryName("FHIRHelpers")
2990+
.withName("ToConcept")
2991+
.withOperand(mCodeProperty);
2992+
toConcept.setResultType(mCodeType);
2993+
mCodeProperty = toConcept;
2994+
}
29742995
String mCodeComparator = "~";
29752996
if (terminology.getResultType() instanceof ListType) {
29762997
mCodeComparator = "in";
@@ -2985,9 +3006,9 @@ public Expression visitRetrieve(cqlParser.RetrieveContext ctx) {
29853006

29863007
Expression terminologyComparison = null;
29873008
if (mCodeComparator.equals("in")) {
2988-
terminologyComparison = libraryBuilder.resolveIn(mProperty, terminology);
3009+
terminologyComparison = libraryBuilder.resolveIn(mCodeProperty, terminology);
29893010
} else {
2990-
BinaryExpression equivalent = of.createEquivalent().withOperand(mProperty, terminology);
3011+
BinaryExpression equivalent = of.createEquivalent().withOperand(mCodeProperty, terminology);
29913012
libraryBuilder.resolveBinaryCall("System", "Equivalent", equivalent);
29923013
terminologyComparison = equivalent;
29933014
}

Src/java/cql-to-elm/src/test/java/org/cqframework/cql/cql2elm/fhir/r401/BaseTest.java

Lines changed: 103 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import static org.cqframework.cql.cql2elm.TestUtils.visitFile;
44
import static org.cqframework.cql.cql2elm.TestUtils.visitFileLibrary;
55
import static org.cqframework.cql.cql2elm.matchers.QuickDataType.quickDataType;
6+
import static org.hamcrest.CoreMatchers.notNullValue;
67
import static org.hamcrest.MatcherAssert.assertThat;
78
import static org.hamcrest.Matchers.*;
89

@@ -16,10 +17,7 @@
1617
import org.cqframework.cql.cql2elm.LibraryBuilder;
1718
import org.cqframework.cql.cql2elm.TestUtils;
1819
import org.cqframework.cql.cql2elm.model.CompiledLibrary;
19-
import org.hl7.cql.model.ChoiceType;
20-
import org.hl7.cql.model.ClassType;
21-
import org.hl7.cql.model.DataType;
22-
import org.hl7.cql.model.NamespaceInfo;
20+
import org.hl7.cql.model.*;
2321
import org.hl7.elm.r1.*;
2422
import org.junit.jupiter.api.Test;
2523

@@ -907,4 +905,105 @@ void overloadForwardOutput() throws IOException {
907905
String.format(
908906
"You used a string literal: [Encounter] here that matches an identifier in scope: [Encounter]. Did you mean to use the identifier instead?")));
909907
}
908+
909+
@Test
910+
void medicationRequest() throws IOException {
911+
CqlTranslator translator = TestUtils.runSemanticTest("fhir/r401/TestMedicationRequest.cql", 0);
912+
Library library = translator.toELM();
913+
Map<String, ExpressionDef> defs = new HashMap<>();
914+
915+
if (library.getStatements() != null) {
916+
for (ExpressionDef def : library.getStatements().getDef()) {
917+
defs.put(def.getName(), def);
918+
}
919+
}
920+
921+
ExpressionDef def = defs.get("Antithrombotic Therapy at Discharge");
922+
assertThat(def, notNullValue());
923+
assertThat(def.getExpression(), instanceOf(Query.class));
924+
Query q = (Query) def.getExpression();
925+
assertThat(q.getSource().size(), is(1));
926+
assertThat(q.getSource().get(0).getExpression(), instanceOf(Retrieve.class));
927+
Retrieve r = (Retrieve) q.getSource().get(0).getExpression();
928+
assertThat(r.getTemplateId(), is("http://hl7.org/fhir/StructureDefinition/MedicationRequest"));
929+
assertThat(r.getCodeProperty(), is("medication"));
930+
assertThat(r.getCodeComparator(), is("in"));
931+
assertThat(r.getCodes(), instanceOf(ValueSetRef.class));
932+
ValueSetRef vsr = (ValueSetRef) r.getCodes();
933+
assertThat(vsr.getName(), is("Antithrombotic Therapy"));
934+
935+
def = defs.get("Antithrombotic Therapy at Discharge (2)");
936+
assertThat(def, notNullValue());
937+
assertThat(def.getExpression(), instanceOf(Union.class));
938+
Union u = (Union) def.getExpression();
939+
assertThat(u.getOperand().size(), is(2));
940+
assertThat(u.getOperand().get(0), instanceOf(Retrieve.class));
941+
r = (Retrieve) u.getOperand().get(0);
942+
assertThat(r.getTemplateId(), is("http://hl7.org/fhir/StructureDefinition/MedicationRequest"));
943+
assertThat(r.getCodeProperty(), is("medication"));
944+
assertThat(r.getCodeComparator(), is("in"));
945+
assertThat(r.getCodes(), instanceOf(ValueSetRef.class));
946+
vsr = (ValueSetRef) r.getCodes();
947+
assertThat(vsr.getName(), is("Antithrombotic Therapy"));
948+
949+
assertThat(u.getOperand().get(1), instanceOf(Query.class));
950+
q = (Query) u.getOperand().get(1);
951+
assertThat(q.getSource().size(), is(1));
952+
assertThat(q.getSource().get(0).getExpression(), instanceOf(Retrieve.class));
953+
r = (Retrieve) q.getSource().get(0).getExpression();
954+
assertThat(r.getTemplateId(), is("http://hl7.org/fhir/StructureDefinition/MedicationRequest"));
955+
assertThat(r.getCodeProperty() == null, is(true));
956+
assertThat(r.getCodes() == null, is(true));
957+
assertThat(q.getRelationship(), notNullValue());
958+
assertThat(q.getRelationship().size(), is(1));
959+
assertThat(q.getRelationship().get(0), instanceOf(With.class));
960+
With w = (With) q.getRelationship().get(0);
961+
assertThat(w.getExpression(), instanceOf(Retrieve.class));
962+
r = (Retrieve) w.getExpression();
963+
assertThat(r.getTemplateId(), is("http://hl7.org/fhir/StructureDefinition/Medication"));
964+
assertThat(r.getCodeProperty() == null, is(true));
965+
assertThat(r.getCodes() == null, is(true));
966+
assertThat(r.getResultType(), instanceOf(ListType.class));
967+
assertThat(((ListType) r.getResultType()).getElementType(), instanceOf(ClassType.class));
968+
assertThat(((ClassType) ((ListType) r.getResultType()).getElementType()).getName(), is("FHIR.Medication"));
969+
assertThat(w.getSuchThat(), instanceOf(And.class));
970+
And a = (And) w.getSuchThat();
971+
assertThat(a.getOperand().get(0), instanceOf(Equal.class));
972+
Equal eq = (Equal) a.getOperand().get(0);
973+
assertThat(eq.getOperand().get(0), instanceOf(FunctionRef.class));
974+
FunctionRef fr = (FunctionRef) eq.getOperand().get(0);
975+
assertThat(fr.getLibraryName(), is("FHIRHelpers"));
976+
assertThat(fr.getName(), is("ToString"));
977+
assertThat(fr.getOperand().size(), is(1));
978+
assertThat(fr.getOperand().get(0), instanceOf(Property.class));
979+
Property p = (Property) fr.getOperand().get(0);
980+
assertThat(p.getScope(), is("M"));
981+
assertThat(p.getPath(), is("id"));
982+
assertThat(eq.getOperand().get(1), instanceOf(Last.class));
983+
Last l = (Last) eq.getOperand().get(1);
984+
assertThat(l.getSource(), instanceOf(Split.class));
985+
Split s = (Split) l.getSource();
986+
assertThat(s.getStringToSplit(), instanceOf(FunctionRef.class));
987+
fr = (FunctionRef) s.getStringToSplit();
988+
assertThat(fr.getLibraryName(), is("FHIRHelpers"));
989+
assertThat(fr.getName(), is("ToString"));
990+
assertThat(fr.getOperand().size(), is(1));
991+
assertThat(fr.getOperand().get(0), instanceOf(Property.class));
992+
p = (Property) fr.getOperand().get(0);
993+
assertThat(p.getScope(), is("MR"));
994+
assertThat(p.getPath(), is("medication.reference"));
995+
// assertThat(s.getSeparator(), is("/"));
996+
assertThat(a.getOperand().get(1), instanceOf(InValueSet.class));
997+
InValueSet ivs = (InValueSet) a.getOperand().get(1);
998+
assertThat(ivs.getValueset().getName(), is("Antithrombotic Therapy"));
999+
assertThat(ivs.getCode(), instanceOf(FunctionRef.class));
1000+
fr = (FunctionRef) ivs.getCode();
1001+
assertThat(fr.getLibraryName(), is("FHIRHelpers"));
1002+
assertThat(fr.getName(), is("ToConcept"));
1003+
assertThat(fr.getOperand().size(), is(1));
1004+
assertThat(fr.getOperand().get(0), instanceOf(Property.class));
1005+
p = (Property) fr.getOperand().get(0);
1006+
assertThat(p.getScope(), is("M"));
1007+
assertThat(p.getPath(), is("code"));
1008+
}
9101009
}

Src/java/cql-to-elm/src/test/java/org/cqframework/cql/cql2elm/qicore/v411/BaseTest.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
import org.cqframework.cql.cql2elm.CqlTranslator;
1414
import org.cqframework.cql.cql2elm.LibraryBuilder;
1515
import org.cqframework.cql.cql2elm.TestUtils;
16+
import org.hl7.cql.model.ClassType;
17+
import org.hl7.cql.model.ListType;
1618
import org.hl7.elm.r1.*;
1719
import org.junit.jupiter.api.Test;
1820

@@ -370,12 +372,38 @@ void medicationRequest() throws IOException {
370372
assertThat(r.getTemplateId(), is("http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-medication"));
371373
assertThat(r.getCodeProperty() == null, is(true));
372374
assertThat(r.getCodes() == null, is(true));
375+
assertThat(r.getResultType(), instanceOf(ListType.class));
376+
assertThat(((ListType) r.getResultType()).getElementType(), instanceOf(ClassType.class));
377+
assertThat(((ClassType) ((ListType) r.getResultType()).getElementType()).getName(), is("QICore.Medication"));
373378
assertThat(w.getSuchThat(), instanceOf(And.class));
374379
And a = (And) w.getSuchThat();
375380
assertThat(a.getOperand().get(0), instanceOf(Equal.class));
381+
Equal eq = (Equal) a.getOperand().get(0);
382+
assertThat(eq.getOperand().get(0), instanceOf(Property.class));
383+
Property p = (Property) eq.getOperand().get(0);
384+
assertThat(p.getScope(), is("M"));
385+
assertThat(p.getPath(), is("id.value"));
386+
assertThat(eq.getOperand().get(1), instanceOf(Last.class));
387+
Last l = (Last) eq.getOperand().get(1);
388+
assertThat(l.getSource(), instanceOf(Split.class));
389+
Split s = (Split) l.getSource();
390+
assertThat(s.getStringToSplit(), instanceOf(Property.class));
391+
p = (Property) s.getStringToSplit();
392+
assertThat(p.getScope(), is("MR"));
393+
assertThat(p.getPath(), is("medication.reference.value"));
394+
// assertThat(s.getSeparator(), is("/"));
376395
assertThat(a.getOperand().get(1), instanceOf(InValueSet.class));
377396
InValueSet ivs = (InValueSet) a.getOperand().get(1);
378397
assertThat(ivs.getValueset().getName(), is("Antithrombotic Therapy"));
398+
assertThat(ivs.getCode(), instanceOf(FunctionRef.class));
399+
FunctionRef fr = (FunctionRef) ivs.getCode();
400+
assertThat(fr.getLibraryName(), is("FHIRHelpers"));
401+
assertThat(fr.getName(), is("ToConcept"));
402+
assertThat(fr.getOperand().size(), is(1));
403+
assertThat(fr.getOperand().get(0), instanceOf(Property.class));
404+
p = (Property) fr.getOperand().get(0);
405+
assertThat(p.getScope(), is("M"));
406+
assertThat(p.getPath(), is("code"));
379407
}
380408

381409
@Test
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
library TestMedicationRequest
2+
3+
using FHIR version '4.0.1'
4+
5+
include FHIRHelpers version '4.0.1'
6+
7+
valueset "Antithrombotic Therapy": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1110.62'
8+
9+
context Patient
10+
11+
define "Antithrombotic Therapy at Discharge":
12+
["MedicationRequest": medication in "Antithrombotic Therapy"] Antithrombotic
13+
14+
define "Antithrombotic Therapy at Discharge (2)":
15+
["MedicationRequest": "Antithrombotic Therapy"]

Src/java/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/CMS645/CMS645-ModuleDefinitionLibrary.json

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,17 +45,21 @@
4545
"valueString": "G10002"
4646
}, {
4747
"url": "targetProperty",
48-
"valueString": "medication"
48+
"valueString": "medication.reference.value"
4949
} ]
5050
} ],
5151
"type": "Medication",
5252
"profile": [ "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-medication" ],
53-
"mustSupport": [ "id" ]
53+
"mustSupport": [ "id.value", "code" ],
54+
"codeFilter": [ {
55+
"path": "code",
56+
"valueSet": "tbd"
57+
} ]
5458
}, {
5559
"id": "G10002",
5660
"type": "MedicationRequest",
5761
"profile": [ "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-medicationrequest" ],
58-
"mustSupport": [ "medication.reference", "status", "status.value", "intent", "intent.value", "doNotPerform", "doNotPerform.value", "dosageInstruction" ]
62+
"mustSupport": [ "medication.reference.value", "status", "status.value", "intent", "intent.value", "doNotPerform", "doNotPerform.value", "dosageInstruction" ]
5963
}, {
6064
"type": "MedicationRequest",
6165
"profile": [ "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-medicationrequest" ],

Src/java/elm-fhir/src/test/resources/org/cqframework/cql/elm/requirements/fhir/PCSBMI/PCSBMI-ModuleDefinitionLibrary.json

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
{
22
"resourceType": "Library",
3-
"name": "EffectiveDataRequirements",
43
"extension": [ {
54
"url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-directReferenceCode",
65
"valueCoding": {
@@ -30,6 +29,7 @@
3029
"display": "virtual"
3130
}
3231
} ],
32+
"name": "EffectiveDataRequirements",
3333
"status": "active",
3434
"type": {
3535
"coding": [ {
@@ -455,11 +455,19 @@
455455
}, {
456456
"type": "Medication",
457457
"profile": [ "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-medication" ],
458-
"mustSupport": [ "id" ]
458+
"mustSupport": [ "id.value", "code" ],
459+
"codeFilter": [ {
460+
"path": "code",
461+
"valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.1561"
462+
} ]
459463
}, {
460464
"type": "Medication",
461465
"profile": [ "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-medication" ],
462-
"mustSupport": [ "id" ]
466+
"mustSupport": [ "id.value", "code" ],
467+
"codeFilter": [ {
468+
"path": "code",
469+
"valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.1562"
470+
} ]
463471
}, {
464472
"type": "MedicationRequest",
465473
"profile": [ "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-medicationrequest" ],
@@ -471,7 +479,7 @@
471479
}, {
472480
"type": "MedicationRequest",
473481
"profile": [ "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-medicationrequest" ],
474-
"mustSupport": [ "medication.reference" ]
482+
"mustSupport": [ "medication.reference.value" ]
475483
}, {
476484
"type": "MedicationRequest",
477485
"profile": [ "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-medicationrequest" ],

0 commit comments

Comments
 (0)