Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.hl7.fhir.r5.model.RelatedArtifact;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.StringType;
import org.opencds.cqf.tooling.utilities.LogicDefinitionUtils;
import org.opencds.cqf.tooling.utilities.constants.CrmiConstants;

public class MeasureRefreshProcessor {
Expand All @@ -39,6 +40,7 @@ public Measure refreshMeasure(Measure measureToUse, LibraryManager libraryManage

Library moduleDefinitionLibrary = getModuleDefinitionLibrary(measureToUse, libraryManager, compiledLibrary, options);
removeModelInfoDependencies(moduleDefinitionLibrary);
LogicDefinitionUtils.deduplicate(moduleDefinitionLibrary.getExtension());
measureToUse.setDate(new Date());
// http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/measure-cqfm
setMeta(measureToUse, moduleDefinitionLibrary);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.hl7.fhir.r5.model.*;
import org.opencds.cqf.tooling.parameter.RefreshIGParameters;
import org.opencds.cqf.tooling.utilities.BundleUtils;
import org.opencds.cqf.tooling.utilities.LogicDefinitionUtils;
import org.opencds.cqf.tooling.utilities.constants.CqfmConstants;
import org.opencds.cqf.tooling.utilities.constants.CrmiConstants;
import org.opencds.cqf.tooling.utilities.converters.ResourceAndTypeConverter;
Expand Down Expand Up @@ -66,10 +67,17 @@ public void refreshCqfmExtensions(MetadataResource resource, Library moduleDefin
resource.getExtension().removeAll(resource.getExtensionsByUrl(CqfmConstants.LOGIC_DEFINITION_EXT_URL));
resource.getExtension().removeAll(resource.getExtensionsByUrl(CqfmConstants.EFFECTIVE_DATA_REQS_EXT_URL));

Set<String> logicDefinitionKeys = new HashSet<>();
for (Extension extension : moduleDefinitionLibrary.getExtension()) {
if (extension.hasUrl() && extension.getUrl().equals(CqfmConstants.DIRECT_REF_CODE_EXT_URL)) {
continue;
}
if (LogicDefinitionUtils.isLogicDefinition(extension)) {
String key = LogicDefinitionUtils.getLogicDefinitionKey(extension);
if (key != null && !logicDefinitionKeys.add(key)) {
continue;
}
}
resource.addExtension(extension);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import org.cqframework.cql.cql2elm.model.CompiledLibrary;
import org.cqframework.cql.elm.requirements.fhir.DataRequirementsProcessor;
import org.hl7.fhir.r5.model.*;
import org.opencds.cqf.tooling.utilities.LogicDefinitionUtils;
import org.opencds.cqf.tooling.utilities.constants.CqfConstants;
import org.opencds.cqf.tooling.utilities.constants.CqfmConstants;
import org.opencds.cqf.tooling.utilities.constants.CrmiConstants;
Expand All @@ -23,6 +24,7 @@ public PlanDefinition refreshPlanDefinition(PlanDefinition planToUse, LibraryMan
var dqReqTrans = new DataRequirementsProcessor();
var moduleDefinitionLibrary = dqReqTrans.gatherDataRequirements(libraryManager, compiledLibrary,
options, expressions, true);
LogicDefinitionUtils.deduplicate(moduleDefinitionLibrary.getExtension());

// Clear all existing CQFM extensions
// These extensions are now deprecated, but may be in use for older artifacts
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package org.opencds.cqf.tooling.utilities;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.hl7.fhir.r5.model.Extension;
import org.opencds.cqf.tooling.utilities.constants.CqfConstants;
import org.opencds.cqf.tooling.utilities.constants.CqfmConstants;

public class LogicDefinitionUtils {

private LogicDefinitionUtils() {
}

public static String getLogicDefinitionKey(Extension logicDefinition) {
String libraryName = null;
String name = null;
for (Extension sub : logicDefinition.getExtension()) {
if ("libraryName".equals(sub.getUrl()) && sub.hasValue()) {
libraryName = sub.getValue().primitiveValue();
} else if ("name".equals(sub.getUrl()) && sub.hasValue()) {
name = sub.getValue().primitiveValue();
}
}
return (libraryName != null && name != null) ? libraryName + "|" + name : null;
}

public static boolean isLogicDefinition(Extension extension) {
return extension.hasUrl()
&& (CqfmConstants.LOGIC_DEFINITION_EXT_URL.equals(extension.getUrl())
|| CqfConstants.LOGIC_DEFINITION_EXT_URL.equals(extension.getUrl()));
}

public static void deduplicate(List<Extension> extensions) {
Set<String> seen = new HashSet<>();
extensions.removeIf(ext -> {
if (isLogicDefinition(ext)) {
String key = getLogicDefinitionKey(ext);
return key != null && !seen.add(key);
}
return false;
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package org.opencds.cqf.tooling.utilities;

import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNull;

import java.util.ArrayList;
import java.util.List;

import org.hl7.fhir.r5.model.Extension;
import org.hl7.fhir.r5.model.IntegerType;
import org.hl7.fhir.r5.model.StringType;
import org.opencds.cqf.tooling.utilities.constants.CqfConstants;
import org.opencds.cqf.tooling.utilities.constants.CqfmConstants;
import org.testng.annotations.Test;

public class LogicDefinitionUtilsTests {

private static Extension logicDefinition(String url, String libraryName, String name, int displaySequence) {
Extension ext = new Extension().setUrl(url);
ext.addExtension(new Extension().setUrl("libraryName").setValue(new StringType(libraryName)));
ext.addExtension(new Extension().setUrl("name").setValue(new StringType(name)));
ext.addExtension(new Extension().setUrl("displaySequence").setValue(new IntegerType(displaySequence)));
return ext;
}

@Test
public void TestKeyUsesLibraryNameAndName() {
Extension ext = logicDefinition(CqfConstants.LOGIC_DEFINITION_EXT_URL, "HRDMeasure", "Inpatient Beds Initial Population", 38);
assertEquals(LogicDefinitionUtils.getLogicDefinitionKey(ext), "HRDMeasure|Inpatient Beds Initial Population");
}

@Test
public void TestKeyIsNullWhenLibraryNameMissing() {
Extension ext = new Extension().setUrl(CqfConstants.LOGIC_DEFINITION_EXT_URL);
ext.addExtension(new Extension().setUrl("name").setValue(new StringType("X")));
assertNull(LogicDefinitionUtils.getLogicDefinitionKey(ext));
}

@Test
public void TestIsLogicDefinitionRecognizesBothUrls() {
Extension cqf = new Extension().setUrl(CqfConstants.LOGIC_DEFINITION_EXT_URL);
Extension cqfm = new Extension().setUrl(CqfmConstants.LOGIC_DEFINITION_EXT_URL);
Extension other = new Extension().setUrl("http://example.org/other");
assertEquals(LogicDefinitionUtils.isLogicDefinition(cqf), true);
assertEquals(LogicDefinitionUtils.isLogicDefinition(cqfm), true);
assertEquals(LogicDefinitionUtils.isLogicDefinition(other), false);
}

@Test
public void TestDeduplicateRemovesDuplicatesKeepingFirst() {
List<Extension> extensions = new ArrayList<>();
extensions.add(logicDefinition(CqfConstants.LOGIC_DEFINITION_EXT_URL, "HRDMeasure", "Inpatient Beds Initial Population", 38));
extensions.add(logicDefinition(CqfConstants.LOGIC_DEFINITION_EXT_URL, "HRDMeasure", "Adult Inpatient Beds Initial Population", 40));
extensions.add(logicDefinition(CqfConstants.LOGIC_DEFINITION_EXT_URL, "HRDMeasure", "Inpatient Beds Initial Population", 82));

LogicDefinitionUtils.deduplicate(extensions);

assertEquals(extensions.size(), 2);
assertEquals(LogicDefinitionUtils.getLogicDefinitionKey(extensions.get(0)), "HRDMeasure|Inpatient Beds Initial Population");
// The first-encountered entry is kept, so the displaySequence of the survivor is 38, not 82.
Extension kept = extensions.get(0);
int displaySequence = ((IntegerType) kept.getExtensionByUrl("displaySequence").getValue()).getValue();
assertEquals(displaySequence, 38);
}

@Test
public void TestDeduplicatePreservesNonLogicDefinitionExtensions() {
List<Extension> extensions = new ArrayList<>();
extensions.add(new Extension().setUrl("http://example.org/other").setValue(new StringType("a")));
extensions.add(logicDefinition(CqfConstants.LOGIC_DEFINITION_EXT_URL, "Lib", "Def", 1));
extensions.add(logicDefinition(CqfConstants.LOGIC_DEFINITION_EXT_URL, "Lib", "Def", 2));
extensions.add(new Extension().setUrl("http://example.org/other").setValue(new StringType("b")));

LogicDefinitionUtils.deduplicate(extensions);

// One logicDefinition removed; both unrelated extensions retained.
assertEquals(extensions.size(), 3);
}

@Test
public void TestDeduplicateDedupesAcrossCqfmAndCqfUrls() {
List<Extension> extensions = new ArrayList<>();
extensions.add(logicDefinition(CqfmConstants.LOGIC_DEFINITION_EXT_URL, "Lib", "Def", 1));
extensions.add(logicDefinition(CqfConstants.LOGIC_DEFINITION_EXT_URL, "Lib", "Def", 2));

LogicDefinitionUtils.deduplicate(extensions);

assertEquals(extensions.size(), 1);
}
}
Loading