collect-data and data-requirements operations and tests (#5854)

* collect-data and data-requirements operations and tests

* remove unused imports

* additional test coverage

* move assertions out of method to pass static code analysis

* camelCase test names

* remove underscore on test methods

* requested edits to changelog

* version bump to 7.1.10-SNAPSHOT

* condense unit tests to prevent pipeline timeout

* method camel case naming
This commit is contained in:
Justin McKelvy 2024-04-18 16:34:26 -06:00 committed by GitHub
parent a90ccde71c
commit e400db5fb8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
92 changed files with 598 additions and 285 deletions

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-bom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<packaging>pom</packaging>
<name>HAPI FHIR BOM</name>
@ -12,7 +12,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-cli</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -0,0 +1,6 @@
---
type: add
issue: 5850
title: "Add additional Clinical Reasoning operations $collect-data and $data-requirements to HAPI-FHIR to expand capability
for clinical reasoning module users. These operations will assist users in identifying data requirements for evaluating a measure, and what
data was used for a measure evaluation"

View File

@ -11,7 +11,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -3,7 +3,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -3,7 +3,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -7,7 +7,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -7,7 +7,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -123,7 +123,8 @@ public class ProviderConstants {
public static final String CR_OPERATION_EXTRACT = "$extract";
public static final String CR_OPERATION_PACKAGE = "$package";
public static final String CR_OPERATION_QUESTIONNAIRE = "$questionnaire";
public static final String CR_OPERATION_COLLECTDATA = "$collect-data";
public static final String CR_OPERATION_DATAREQUIREMENTS = "$data-requirements";
/**
* Operation name for the $meta operation
*/

View File

@ -7,7 +7,7 @@
<parent>
<artifactId>hapi-fhir-serviceloaders</artifactId>
<groupId>ca.uhn.hapi.fhir</groupId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -7,7 +7,7 @@
<parent>
<artifactId>hapi-fhir-serviceloaders</artifactId>
<groupId>ca.uhn.hapi.fhir</groupId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
@ -21,7 +21,7 @@
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-caching-api</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
</dependency>
<dependency>

View File

@ -7,7 +7,7 @@
<parent>
<artifactId>hapi-fhir-serviceloaders</artifactId>
<groupId>ca.uhn.hapi.fhir</groupId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -7,7 +7,7 @@
<parent>
<artifactId>hapi-fhir</artifactId>
<groupId>ca.uhn.hapi.fhir</groupId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>hapi-deployable-pom</artifactId>
<groupId>ca.uhn.hapi.fhir</groupId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
</parent>
<artifactId>hapi-fhir-spring-boot-sample-client-apache</artifactId>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -7,7 +7,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -7,7 +7,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -7,7 +7,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -26,13 +26,17 @@ import ca.uhn.fhir.cr.config.ProviderLoader;
import ca.uhn.fhir.cr.config.ProviderSelector;
import ca.uhn.fhir.cr.config.RepositoryConfig;
import ca.uhn.fhir.cr.r4.ICareGapsServiceFactory;
import ca.uhn.fhir.cr.r4.ICollectDataServiceFactory;
import ca.uhn.fhir.cr.r4.ICqlExecutionServiceFactory;
import ca.uhn.fhir.cr.r4.IDataRequirementsServiceFactory;
import ca.uhn.fhir.cr.r4.ILibraryEvaluationServiceFactory;
import ca.uhn.fhir.cr.r4.IMeasureServiceFactory;
import ca.uhn.fhir.cr.r4.ISubmitDataProcessorFactory;
import ca.uhn.fhir.cr.r4.cpg.CqlExecutionOperationProvider;
import ca.uhn.fhir.cr.r4.cpg.LibraryEvaluationOperationProvider;
import ca.uhn.fhir.cr.r4.measure.CareGapsOperationProvider;
import ca.uhn.fhir.cr.r4.measure.CollectDataOperationProvider;
import ca.uhn.fhir.cr.r4.measure.DataRequirementsOperationProvider;
import ca.uhn.fhir.cr.r4.measure.MeasureOperationsProvider;
import ca.uhn.fhir.cr.r4.measure.SubmitDataProvider;
import ca.uhn.fhir.rest.server.RestfulServer;
@ -42,6 +46,8 @@ import org.opencds.cqf.fhir.cr.cpg.r4.R4LibraryEvaluationService;
import org.opencds.cqf.fhir.cr.measure.CareGapsProperties;
import org.opencds.cqf.fhir.cr.measure.MeasureEvaluationOptions;
import org.opencds.cqf.fhir.cr.measure.r4.R4CareGapsService;
import org.opencds.cqf.fhir.cr.measure.r4.R4CollectDataService;
import org.opencds.cqf.fhir.cr.measure.r4.R4DataRequirementsService;
import org.opencds.cqf.fhir.cr.measure.r4.R4MeasureService;
import org.opencds.cqf.fhir.cr.measure.r4.R4SubmitDataService;
import org.springframework.beans.factory.annotation.Qualifier;
@ -86,6 +92,28 @@ public class CrR4Config {
return new CqlExecutionOperationProvider();
}
@Bean
CollectDataOperationProvider r4CollectDataOperationProvider() {
return new CollectDataOperationProvider();
}
@Bean
ICollectDataServiceFactory collectDataServiceFactory(
IRepositoryFactory theRepositoryFactory, MeasureEvaluationOptions theMeasureEvaluationOptions) {
return rd -> new R4CollectDataService(theRepositoryFactory.create(rd), theMeasureEvaluationOptions);
}
@Bean
DataRequirementsOperationProvider r4DataRequirementsOperationProvider() {
return new DataRequirementsOperationProvider();
}
@Bean
IDataRequirementsServiceFactory dataRequirementsServiceFactory(
IRepositoryFactory theRepositoryFactory, MeasureEvaluationOptions theMeasureEvaluationOptions) {
return rd -> new R4DataRequirementsService(theRepositoryFactory.create(rd), theMeasureEvaluationOptions);
}
@Bean
LibraryEvaluationOperationProvider r4LibraryEvaluationOperationProvider() {
return new LibraryEvaluationOperationProvider();
@ -132,7 +160,9 @@ public class CrR4Config {
SubmitDataProvider.class,
CareGapsOperationProvider.class,
CqlExecutionOperationProvider.class,
LibraryEvaluationOperationProvider.class)));
LibraryEvaluationOperationProvider.class,
CollectDataOperationProvider.class,
DataRequirementsOperationProvider.class)));
return new ProviderLoader(theRestfulServer, theApplicationContext, selector);
}

View File

@ -0,0 +1,9 @@
package ca.uhn.fhir.cr.r4;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import org.opencds.cqf.fhir.cr.measure.r4.R4CollectDataService;
@FunctionalInterface
public interface ICollectDataServiceFactory {
R4CollectDataService create(RequestDetails theRequestDetails);
}

View File

@ -0,0 +1,9 @@
package ca.uhn.fhir.cr.r4;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import org.opencds.cqf.fhir.cr.measure.r4.R4DataRequirementsService;
@FunctionalInterface
public interface IDataRequirementsServiceFactory {
R4DataRequirementsService create(RequestDetails theRequestDetails);
}

View File

@ -113,7 +113,7 @@ public class CqlExecutionOperationProvider {
* is a CQL system-defined or FHIR-defined type, the result is returned
* as a {@link Parameters} Parameters resource
*/
@Operation(name = ProviderConstants.CR_OPERATION_CQL)
@Operation(name = ProviderConstants.CR_OPERATION_CQL, idempotent = true)
@Description(
shortDefinition = "$cql",
value =
@ -145,6 +145,6 @@ public class CqlExecutionOperationProvider {
theDataEndpoint,
theContentEndpoint,
theTerminologyEndpoint,
null);
theContent);
}
}

View File

@ -0,0 +1,56 @@
package ca.uhn.fhir.cr.r4.measure;
import ca.uhn.fhir.cr.r4.ICollectDataServiceFactory;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.provider.ProviderConstants;
import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.Measure;
import org.hl7.fhir.r4.model.Parameters;
import org.springframework.beans.factory.annotation.Autowired;
public class CollectDataOperationProvider {
@Autowired
ICollectDataServiceFactory myR4CollectDataServiceFactory;
/**
* Implements the <a href=
* "http://hl7.org/fhir/R4/measure-operation-collect-data.html">$collect-data</a>
* operation found in the
* <a href="http://hl7.org/fhir/R4/clinicalreasoning-module.html">FHIR Clinical
* Reasoning Module</a>.
*
* <p>
* Returns a set of parameters with the generated MeasureReport and the
* resources that were used during the Measure evaluation
*
* @param theRequestDetails generally auto-populated by the HAPI server
* framework.
* @param theId the Id of the Measure to sub data for
* @param thePeriodStart The start of the reporting period
* @param thePeriodEnd The end of the reporting period
* @param theSubject the subject to use for the evaluation
* @param thePractitioner the practitioner to use for the evaluation
*
* @return Parameters the parameters containing the MeasureReport and the
* evaluated Resources
*/
@Description(
shortDefinition = "$collect-data",
value =
"Implements the <a href=\"http://hl7.org/fhir/R4/measure-operation-collect-data.html\">$collect-data</a> operation found in the <a href=\"http://hl7.org/fhir/R4/clinicalreasoning-module.html\">FHIR Clinical Reasoning Module</a>.")
@Operation(name = ProviderConstants.CR_OPERATION_COLLECTDATA, idempotent = true, type = Measure.class)
public Parameters collectData(
@IdParam IdType theId,
@OperationParam(name = "periodStart") String thePeriodStart,
@OperationParam(name = "periodEnd") String thePeriodEnd,
@OperationParam(name = "subject") String theSubject,
@OperationParam(name = "practitioner") String thePractitioner,
RequestDetails theRequestDetails) {
return myR4CollectDataServiceFactory
.create(theRequestDetails)
.collectData(theId, thePeriodStart, thePeriodEnd, theSubject, thePractitioner);
}
}

View File

@ -0,0 +1,42 @@
package ca.uhn.fhir.cr.r4.measure;
import ca.uhn.fhir.cr.r4.IDataRequirementsServiceFactory;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.provider.ProviderConstants;
import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.Library;
import org.hl7.fhir.r4.model.Measure;
import org.springframework.beans.factory.annotation.Autowired;
public class DataRequirementsOperationProvider {
@Autowired
IDataRequirementsServiceFactory myR4DataRequirementsServiceFactory;
/**
* Implements the <a href=
* "https://www.hl7.org/fhir/R4/measure-operation-data-requirements.html">$evaluate-measure</a>
* operation found in the
* <a href="http://www.hl7.org/fhir/clinicalreasoning-module.html">FHIR Clinical
* Reasoning Module</a>. This implementation aims to be compatible with the CQF
* IG.
*
* @param theId the id of the Measure to evaluate
* @param thePeriodStart The start of the reporting period
* @param thePeriodEnd The end of the reporting period
* @param theRequestDetails The details (such as tenant) of this request. Usually
* autopopulated HAPI.
* @return the calculated Library dataRequirements
*/
@Operation(name = ProviderConstants.CR_OPERATION_DATAREQUIREMENTS, idempotent = true, type = Measure.class)
public Library dataRequirements(
@IdParam IdType theId,
@OperationParam(name = "periodStart") String thePeriodStart,
@OperationParam(name = "periodEnd") String thePeriodEnd,
RequestDetails theRequestDetails) {
return myR4DataRequirementsServiceFactory
.create(theRequestDetails)
.dataRequirements(theId, thePeriodStart, thePeriodEnd);
}
}

View File

@ -0,0 +1,73 @@
package ca.uhn.fhir.cr.r4;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.provider.ProviderConstants;
import org.hl7.fhir.r4.model.DateType;
import org.hl7.fhir.r4.model.Parameters;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.junit.jupiter.api.Assertions.assertThrows;
@ExtendWith(SpringExtension.class)
public class CollectDataTest extends BaseCrR4TestServer{
public Parameters runCollectData(String thePeriodStart, String thePeriodEnd, String theMeasureId, String theSubject, String thePractitioner){
var parametersEval = new Parameters();
parametersEval.addParameter("periodStart", new DateType(thePeriodStart));
parametersEval.addParameter("periodEnd", new DateType(thePeriodEnd));
parametersEval.addParameter("practitioner", thePractitioner);
parametersEval.addParameter("subject", theSubject);
var report = ourClient.operation().onInstance("Measure/" + theMeasureId)
.named(ProviderConstants.CR_OPERATION_COLLECTDATA)
.withParameters(parametersEval)
.returnResourceType(Parameters.class)
.execute();
return report;
}
@Test
void testMeasureDataRequirements() {
loadBundle("ColorectalCancerScreeningsFHIR-bundle.json");
var reportAllSubjects = runCollectData("2019-01-01", "2019-12-31", "ColorectalCancerScreeningsFHIR", null, null);
Assertions.assertFalse(reportAllSubjects.getParameter().isEmpty());
// use same loaded bundle for all tests
testCollectDataSubject();
testCollectDataGroup();
testCollectDataPractitioner();
testCollectDataInvalidInterval();
testCollectDataInvalidMeasure();
}
void testCollectDataSubject() {
var report = runCollectData("2019-01-01", "2019-12-31", "ColorectalCancerScreeningsFHIR", "Patient/numer-EXM130", null);
Assertions.assertFalse(report.getParameter().isEmpty());
}
void testCollectDataGroup() {
var report = runCollectData("2019-01-01", "2019-12-31", "ColorectalCancerScreeningsFHIR", "Group/group-EXM130", null);
Assertions.assertFalse(report.getParameter().isEmpty());
}
void testCollectDataPractitioner() {
var report = runCollectData("2019-01-01", "2019-12-31", "ColorectalCancerScreeningsFHIR", null, "Practitioner/practitioner-EXM130");
Assertions.assertFalse(report.getParameter().isEmpty());
}
void testCollectDataInvalidInterval() {
assertThrows(InternalErrorException.class, ()->runCollectData("2020-01-01", "2019-12-31", "ColorectalCancerScreeningsFHIR", null, null));
}
void testCollectDataInvalidMeasure() {
assertThrows(ResourceNotFoundException.class, ()->runCollectData("2019-01-01", "2019-12-31", "ColorectalCancerScreeningsFHI", null, null));
}
}

View File

@ -30,23 +30,37 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
public class CpgOperationProviderTest extends BaseCrR4TestServer{
@BeforeEach
void setup() {
var reqDeets = setupRequestDetails();
loadResource(Library.class, "SimpleR4Library.json", reqDeets);
loadResource(Patient.class, "SimplePatient.json", reqDeets);
loadResource(Observation.class, "SimpleObservation.json", reqDeets);
loadResource(Condition.class, "SimpleCondition.json", reqDeets);
var requestDetails = setupRequestDetails();
loadResource(Library.class, "SimpleR4Library.json", requestDetails);
loadResource(Patient.class, "SimplePatient.json", requestDetails);
loadResource(Observation.class, "SimpleObservation.json", requestDetails);
loadResource(Condition.class, "SimpleCondition.json", requestDetails);
}
@Test
void cpgProviderTest(){
// reuse loaded resources for all tests
assertTrue(cqlExecutionProviderTestSimpleDate());
cqlExecutionProviderTestSimpleArithmetic();
evaluateLibraryProviderTestLibraryWithSubject();
evaluateLibraryProviderTestSimpleExpression();
cqlExecutionProviderTestReferencedLibrary();
cqlExecutionProviderTestDataBundle();
cqlExecutionProviderTestDataBundleWithSubject();
cqlExecutionProviderTestSimpleParameters();
cqlExecutionProviderTestExpression();
cqlExecutionProviderTestErrorExpression();
}
@Test
void cqlExecutionProvider_testSimpleDate() {
private Boolean cqlExecutionProviderTestSimpleDate() {
// execute cql expression on date interval
Parameters params = parameters(stringPart("expression", "Interval[Today() - 2 years, Today())"));
Parameters results = runCqlExecution(params);
assertTrue(results.getParameter("return").getValue() instanceof Period);
return results.getParameter("return").getValue() instanceof Period;
}
@Test
void cqlExecutionProvider_testSimpleArithmetic() {
void cqlExecutionProviderTestSimpleArithmetic() {
// execute simple cql expression
Parameters params = parameters(stringPart("expression", "5 * 5"));
Parameters results = runCqlExecution(params);
@ -54,8 +68,7 @@ public class CpgOperationProviderTest extends BaseCrR4TestServer{
assertEquals("25", ((IntegerType) results.getParameter("return").getValue()).asStringValue());
}
@Test
void evaluateLibraryProvider_testLibraryWithSubject() {
void evaluateLibraryProviderTestLibraryWithSubject() {
// evaluate library resource for a subject
var params = new Parameters();
params.addParameter("subject", new StringType("Patient/SimplePatient"));
@ -71,8 +84,8 @@ public class CpgOperationProviderTest extends BaseCrR4TestServer{
assertTrue(((BooleanType) report.getParameter("Denominator").getValue()).booleanValue());
}
@Test
void evaluateLibraryProvider_testSimpleExpression() {
void evaluateLibraryProviderTestSimpleExpression() {
// evaluate expression for subject from specified library resource
var params = new Parameters();
params.addParameter("subject", new StringType("Patient/SimplePatient"));
@ -84,8 +97,7 @@ public class CpgOperationProviderTest extends BaseCrR4TestServer{
assertTrue(((BooleanType) report.getParameter("Numerator").getValue()).booleanValue());
}
@Test
void cqlExecutionProvider_testReferencedLibrary() {
void cqlExecutionProviderTestReferencedLibrary() {
// execute cql expression from referenced library on subject
Parameters libraryParameter = parameters(
canonicalPart("url", ourClient.getServerBase() + "/Library/SimpleR4Library|0.0.1"),
@ -100,8 +112,7 @@ public class CpgOperationProviderTest extends BaseCrR4TestServer{
assertTrue(((BooleanType) results.getParameter("return").getValue()).booleanValue());
}
@Test
void cqlExecutionProvider_testDataBundle() {
void cqlExecutionProviderTestDataBundle() {
// execute cql expression from library over data from bundle with no subject
Parameters libraryParameter = parameters(
canonicalPart("url", ourClient.getServerBase() + "/Library/SimpleR4Library"),
@ -118,8 +129,8 @@ public class CpgOperationProviderTest extends BaseCrR4TestServer{
assertTrue(results.getParameter().get(0).getResource() instanceof Observation);
}
@Test
void cqlExecutionProvider_testDataBundleWithSubject() {
void cqlExecutionProviderTestDataBundleWithSubject() {
// execute cql expression from library over data from bundle with subject
Parameters libraryParameter = parameters(
canonicalPart("url", ourClient.getServerBase() + "/Library/SimpleR4Library"),
@ -135,8 +146,8 @@ public class CpgOperationProviderTest extends BaseCrR4TestServer{
assertTrue(results.getParameter().get(0).getResource() instanceof Observation);
}
@Test
void cqlExecutionProvider_testSimpleParameters() {
void cqlExecutionProviderTestSimpleParameters() {
// execute inline cql date expression with input valuemv
Parameters evaluationParams = parameters(
datePart("%inputDate", "2019-11-01"));
@ -148,8 +159,7 @@ public class CpgOperationProviderTest extends BaseCrR4TestServer{
assertTrue(((BooleanType) results.getParameter("return").getValue()).booleanValue());
}
@Test
void cqlExecutionProvider_testExpression() {
void cqlExecutionProviderTestExpression() {
// execute cql expression from referenced library
Parameters libraryParameter = parameters(
canonicalPart("url", ourClient.getServerBase() + "/Library/SimpleR4Library"),
@ -168,8 +178,7 @@ public class CpgOperationProviderTest extends BaseCrR4TestServer{
assertTrue(((BooleanType) results.getParameter("return").getValue()).booleanValue());
}
@Test
void cqlExecutionProvider_testErrorExpression() {
void cqlExecutionProviderTestErrorExpression() {
// execute invalid cql expression
Parameters params = parameters(stringPart("expression", "Interval[1,5]"));

View File

@ -0,0 +1,49 @@
package ca.uhn.fhir.cr.r4;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.provider.ProviderConstants;
import org.hl7.fhir.r4.model.DateType;
import org.hl7.fhir.r4.model.Library;
import org.hl7.fhir.r4.model.Parameters;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.junit.jupiter.api.Assertions.assertThrows;
@ExtendWith(SpringExtension.class)
public class DataRequirementsTest extends BaseCrR4TestServer{
public Library runDataRequirements(String periodStart, String periodEnd, String measureId){
var parametersEval = new Parameters();
parametersEval.addParameter("periodStart", new DateType(periodStart));
parametersEval.addParameter("periodEnd", new DateType(periodEnd));
var report = ourClient.operation().onInstance("Measure/" + measureId)
.named(ProviderConstants.CR_OPERATION_DATAREQUIREMENTS)
.withParameters(parametersEval)
.returnResourceType(Library.class)
.execute();
return report;
}
@Test
void testMeasureDataRequirements() {
loadBundle("ColorectalCancerScreeningsFHIR-bundle.json");
Assertions.assertFalse(runDataRequirements("2019-01-01", "2019-12-31", "ColorectalCancerScreeningsFHIR").getDataRequirement().isEmpty());
testMeasureDataRequirementsInvalidInterval();
testMeasureDataRequirementsInvalidMeasure();
}
void testMeasureDataRequirementsInvalidInterval() {
assertThrows(InternalErrorException.class, ()->runDataRequirements("2020-01-01", "2019-12-31", "ColorectalCancerScreeningsFHIR"));
}
void testMeasureDataRequirementsInvalidMeasure() {
assertThrows(ResourceNotFoundException.class, ()->runDataRequirements("2019-01-01", "2019-12-31", "ColorectalCancerScreeningsFHI"));
}
}

View File

@ -5,7 +5,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.*;
import java.util.stream.Collectors;
import ca.uhn.fhir.rest.param.*;
import ca.uhn.fhir.rest.server.RestfulServer;
@ -15,6 +14,7 @@ import org.hl7.fhir.r4.model.HumanName;
import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.Immunization;
import org.hl7.fhir.r4.model.Patient;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import ca.uhn.fhir.cr.repo.HapiFhirRepository;
@ -28,13 +28,28 @@ public class HapiFhirRepositoryR4Test extends BaseCrR4TestServer {
private static final String MY_TEST_DATA =
"ca/uhn/fhir/cr/r4/immunization/Patients_Encounters_Immunizations_Practitioners.json";
@BeforeEach
void setup() {
loadBundle("ColorectalCancerScreeningsFHIR-bundle.json");
loadBundle(MY_TEST_DATA);
}
@Test
void crudTest() {
var requestDetails = setupRequestDetails();
//register repo
//regster providers
var repository = new HapiFhirRepository(myDaoRegistry, requestDetails, myRestfulServer);
var result = repository
void repositoryTests(){
var repository = new HapiFhirRepository(myDaoRegistry, setupRequestDetails(), myRestfulServer);
//run repository tests
transactionReadsImmunizationResources(repository);
repositorySearchForEncounterWithMatchingCode(repository);
canSearchMoreThan50Patients(repository);
canSearchWithPagination(repository);
transactionReadsPatientResources(repository);
transactionReadsEncounterResources(repository);
assertTrue(crudTest(repository));
}
Boolean crudTest(HapiFhirRepository theRepository) {
var result = theRepository
.create(new Patient().addName(new HumanName().setFamily("Test").addGiven("Name1")));
assertEquals(true, result.getCreated());
var patient = (Patient) result.getResource();
@ -42,64 +57,24 @@ public class HapiFhirRepositoryR4Test extends BaseCrR4TestServer {
assertEquals("Test", patient.getName().get(0).getFamily());
assertEquals(1, patient.getName().get(0).getGiven().size());
patient.getName().get(0).addGiven("Name2");
repository.update(patient);
var updatedPatient = repository.read(Patient.class, patient.getIdElement());
theRepository.update(patient);
var updatedPatient = theRepository.read(Patient.class, patient.getIdElement());
assertEquals(2, updatedPatient.getName().get(0).getGiven().size());
repository.delete(Patient.class, patient.getIdElement());
theRepository.delete(Patient.class, patient.getIdElement());
var ex = assertThrows(Exception.class,
() -> repository.read(Patient.class, new IdType(patient.getIdElement().getIdPart())));
assertTrue(ex.getMessage().contains("Resource was deleted"));
() -> theRepository.read(Patient.class, new IdType(patient.getIdElement().getIdPart())));
return ex.getMessage().contains("Resource was deleted");
}
@Test
void canSearchMoreThan50Patients() {
loadBundle(MY_TEST_DATA);
var expectedPatientCount = 63;
void canSearchMoreThan50Patients(HapiFhirRepository theRepository) {
var expectedPatientCount = 65;
ourPagingProvider.setMaximumPageSize(100);
var repository = new HapiFhirRepository(myDaoRegistry, setupRequestDetails(), myRestfulServer);
// get all patient resources posted
var result = repository.search(Bundle.class, Patient.class, withCountParam(100));
var result = theRepository.search(Bundle.class, Patient.class, withCountParam(100));
assertEquals(expectedPatientCount, result.getTotal());
// count all resources in result
int counter = 0;
for (var e : result.getEntry()) {
counter++;
}
// verify all patient resources captured
assertEquals(expectedPatientCount, counter,
"Patient search results don't match available resources");
}
@Test
void canSearchWithPagination() {
loadBundle(MY_TEST_DATA);
var requestDetails = setupRequestDetails();
var repository = new HapiFhirRepository(myDaoRegistry, requestDetails, myRestfulServer);
var result = repository.search(Bundle.class, Patient.class, withCountParam(20));
assertEquals(20, result.getEntry().size());
var next = result.getLink().get(1);
assertEquals("next", next.getRelation());
var nextUrl = next.getUrl();
var nextResult = repository.link(Bundle.class, nextUrl);
assertEquals(20, nextResult.getEntry().size());
assertEquals(false,
result.getEntry().stream().map(e -> e.getResource().getIdPart()).anyMatch(
i -> nextResult.getEntry().stream().map(e -> e.getResource().getIdPart())
.collect(Collectors.toList()).contains(i)));
}
@Test
void transactionReadsPatientResources() {
var expectedPatientCount = 63;
var theBundle = readResource(Bundle.class, MY_TEST_DATA);
ourPagingProvider.setMaximumPageSize(100);
var repository = new HapiFhirRepository(myDaoRegistry, setupRequestDetails(), myRestfulServer);
repository.transaction(theBundle);
var result = repository.search(Bundle.class, Patient.class, withCountParam(100));
// count all resources in result
int counter = 0;
for (Object i : result.getEntry()) {
counter++;
}
@ -108,14 +83,41 @@ public class HapiFhirRepositoryR4Test extends BaseCrR4TestServer {
"Patient search results don't match available resources");
}
@Test
void transactionReadsEncounterResources() {
var expectedEncounterCount = 652;
var theBundle = readResource(Bundle.class, MY_TEST_DATA);
void canSearchWithPagination(HapiFhirRepository theRepository) {
var result = theRepository.search(Bundle.class, Patient.class, withCountParam(20));
assertEquals(20, result.getEntry().size());
var next = result.getLink().get(1);
assertEquals("next", next.getRelation());
var nextUrl = next.getUrl();
var nextResult = theRepository.link(Bundle.class, nextUrl);
assertEquals(20, nextResult.getEntry().size());
assertEquals(false,
result.getEntry().stream().map(e -> e.getResource().getIdPart()).anyMatch(
i -> nextResult.getEntry().stream().map(e -> e.getResource().getIdPart())
.toList().contains(i)));
}
void transactionReadsPatientResources(HapiFhirRepository theRepository) {
var expectedPatientCount = 65;
ourPagingProvider.setMaximumPageSize(100);
var result = theRepository.search(Bundle.class, Patient.class, withCountParam(100));
// count all resources in result
int counter = 0;
for (Object i : result.getEntry()) {
counter++;
}
// verify all patient resources captured
assertEquals(expectedPatientCount, counter,
"Patient search results don't match available resources");
}
void transactionReadsEncounterResources(HapiFhirRepository theRepository) {
var expectedEncounterCount = 654;
ourPagingProvider.setMaximumPageSize(1000);
var repository = new HapiFhirRepository(myDaoRegistry, setupRequestDetails(), myRestfulServer);
repository.transaction(theBundle);
var result = repository.search(Bundle.class, Encounter.class, withCountParam(1000));
var result = theRepository.search(Bundle.class, Encounter.class, withCountParam(1000));
// count all resources in result
int counter = 0;
for (Object i : result.getEntry()) {
@ -125,10 +127,8 @@ public class HapiFhirRepositoryR4Test extends BaseCrR4TestServer {
assertEquals(expectedEncounterCount, counter,
"Encounter search results don't match available resources");
}
@Test
void repositorySearchForEncounterWithMatchingCode() {
loadBundle("ColorectalCancerScreeningsFHIR-bundle.json");
void repositorySearchForEncounterWithMatchingCode(HapiFhirRepository theRepository) {
//SearchConverter validation test for repository
List<IQueryParameterType> codeList = new ArrayList<>();
@ -152,10 +152,8 @@ public class HapiFhirRepositoryR4Test extends BaseCrR4TestServer {
// replicate repository searchParam list
Map<String, List<IQueryParameterType>> searchParams = Map.of("type", codeList, "subject", Collections.singletonList(new ReferenceParam("Patient/numer-EXM130")));
var repository = new HapiFhirRepository(myDaoRegistry, setupRequestDetails(), myRestfulServer);
// replicate search for valueset codes
var result = repository.search(Bundle.class, Encounter.class, searchParams);
var result = theRepository.search(Bundle.class, Encounter.class, searchParams);
// count all resources in result
int counter = 0;
@ -166,15 +164,12 @@ public class HapiFhirRepositoryR4Test extends BaseCrR4TestServer {
assertEquals(1, counter,
"Encounter search results don't match available resources");
}
@Test
void transactionReadsImmunizationResources() {
void transactionReadsImmunizationResources(HapiFhirRepository theRepository) {
var expectedEncounterCount = 638;
var theBundle = readResource(Bundle.class, MY_TEST_DATA);
ourPagingProvider.setMaximumPageSize(1000);
var repository = new HapiFhirRepository(myDaoRegistry, setupRequestDetails(), myRestfulServer);
repository.transaction(theBundle);
var result = repository.search(Bundle.class, Immunization.class, withCountParam(1000));
var result = theRepository.search(Bundle.class, Immunization.class, withCountParam(1000));
// count all resources in result
int counter = 0;
for (Object i : result.getEntry()) {

View File

@ -7,6 +7,7 @@ import org.hl7.fhir.r4.model.MeasureReport;
import org.hl7.fhir.r4.model.Observation;
import org.hl7.fhir.r4.model.Parameters;
import org.hl7.fhir.r4.model.Resource;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@ -21,6 +22,27 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
@ExtendWith(SpringExtension.class)
class MeasureOperationProviderTest extends BaseCrR4TestServer {
@BeforeEach
void setup() {
// load resources
loadBundle("ColorectalCancerScreeningsFHIR-bundle.json");
loadBundle("Exm104FhirR4MeasureBundle.json");
loadBundle("ClientNonPatientBasedMeasureBundle.json");
loadBundle("multiversion/EXM124-7.0.000-bundle.json");
loadBundle("multiversion/EXM124-9.0.000-bundle.json");
loadBundle("largeValueSetMeasureTest-Bundle.json");
loadBundle("BCSEHEDISMY2022-bundle.json");
}
@Test
void runMeasureTests(){
// run tests
assertNotNull(testMeasureEvaluateExm130());
assertNotNull(testMeasureEvaluateExm104());
testClientNonPatientBasedMeasureEvaluate();
testMeasureEvaluateMultiVersion();
testLargeValuesetMeasure();
testHedis2022();
}
public MeasureReport runEvaluateMeasure(String periodStart, String periodEnd, String subject, String measureId, String reportType, String practitioner){
@ -31,43 +53,96 @@ class MeasureOperationProviderTest extends BaseCrR4TestServer {
parametersEval.addParameter("reportType", reportType);
parametersEval.addParameter("subject", subject);
var report = ourClient.operation().onInstance("Measure/" + measureId)
return ourClient.operation().onInstance("Measure/" + measureId)
.named(ProviderConstants.CR_OPERATION_EVALUATE_MEASURE)
.withParameters(parametersEval)
.returnResourceType(MeasureReport.class)
.execute();
assertNotNull(report);
return report;
}
MeasureReport testMeasureEvaluateExm130() {
return runEvaluateMeasure("2019-01-01", "2019-12-31", "Patient/numer-EXM130", "ColorectalCancerScreeningsFHIR", "Individual", null);
}
@Test
void testMeasureEvaluate_EXM130() {
loadBundle("ColorectalCancerScreeningsFHIR-bundle.json");
runEvaluateMeasure("2019-01-01", "2019-12-31", "Patient/numer-EXM130", "ColorectalCancerScreeningsFHIR", "Individual", null);
}
@Test
void testMeasureEvaluate_EXM104() {
loadBundle("Exm104FhirR4MeasureBundle.json");
runEvaluateMeasure("2019-01-01", "2019-12-31", "Patient/numer-EXM104", "measure-EXM104-8.2.000", "Individual", null);
MeasureReport testMeasureEvaluateExm104() {
return runEvaluateMeasure("2019-01-01", "2019-12-31", "Patient/numer-EXM104", "measure-EXM104-8.2.000", "Individual", null);
}
void testHedis2022() {
runWithPatient("BCSEHEDISMY2022", "Patient/Patient-5", 0, 0, 0, 0, false,
"Interval[2020-10-01T00:00:00.000, 2022-12-31T23:59:59.999]");
runWithPatient("BCSEHEDISMY2022", "Patient/Patient-7", 1, 1, 0, 0, true,
"Interval[2020-10-01T00:00:00.000, 2022-12-31T23:59:59.999]");
runWithPatient("BCSEHEDISMY2022", "Patient/Patient-9", 0, 0, 0, 0, true,
"Interval[2020-10-01T00:00:00.000, 2022-12-31T23:59:59.999]");
runWithPatient("BCSEHEDISMY2022", "Patient/Patient-21", 1, 0, 1, 0, true,
"Interval[2020-10-01T00:00:00.000, 2022-12-31T23:59:59.999]");
runWithPatient("BCSEHEDISMY2022", "Patient/Patient-23", 1, 1, 0, 0, true,
"Interval[2020-10-01T00:00:00.000, 2022-12-31T23:59:59.999]");
runWithPatient("BCSEHEDISMY2022", "Patient/Patient-65", 1, 1, 0, 1, true,
"Interval[2020-10-01T00:00:00.000, 2022-12-31T23:59:59.999]");
}
void testClientNonPatientBasedMeasureEvaluate() {
var measure = read(new IdType("Measure", "InitialInpatientPopulation"));
assertNotNull(measure);
var returnMeasureReport = runEvaluateMeasure("2019-01-01", "2020-01-01", "Patient/97f27374-8a5c-4aa1-a26f-5a1ab03caa47", "InitialInpatientPopulation", "Individual", null);
String populationName = "initial-population";
int expectedCount = 2;
Optional<MeasureReport.MeasureReportGroupPopulationComponent> population = returnMeasureReport.getGroup().get(0)
.getPopulation().stream().filter(x -> x.hasCode() && x.getCode().hasCoding()
&& x.getCode().getCoding().get(0).getCode().equals(populationName))
.findFirst();
assertTrue(population.isPresent(), String.format("Unable to locate a population with id \"%s\"", populationName));
assertEquals(population.get().getCount(), expectedCount,
String.format("expected count for population \"%s\" did not match", populationName));
}
void testMeasureEvaluateMultiVersion() {
assertNotNull(runEvaluateMeasure("2019-01-01", "2020-01-01", "Patient/numer-EXM124", "measure-EXM124-7.0.000", "Individual", null));
assertNotNull(runEvaluateMeasure("2019-01-01", "2020-01-01", "Patient/numer-EXM124", "measure-EXM124-9.0.000", "Individual", null));
}
void testLargeValuesetMeasure() throws NoSuchElementException {
var returnMeasureReport = runEvaluateMeasure("2023-01-01", "2024-01-01", null, "CMSTest", "population", null);
String populationName = "numerator";
int expectedCount = 1;
Optional<MeasureReport.MeasureReportGroupPopulationComponent> population = returnMeasureReport.getGroup().get(0)
.getPopulation().stream().filter(x -> x.hasCode() && x.getCode().hasCoding()
&& x.getCode().getCoding().get(0).getCode().equals(populationName))
.findFirst();
assertTrue(population.isPresent(), String.format("population \"%s\" not found in report", populationName));
assertEquals(
expectedCount,
population.get().getCount(),
String.format("expected count for population \"%s\" did not match", populationName)
);
}
private void runWithPatient(String measureId, String patientId, int initialPopulationCount, int denominatorCount,
int denominatorExclusionCount, int numeratorCount, boolean enrolledDuringParticipationPeriod,
String participationPeriod) {
int denominatorExclusionCount, int numeratorCount, boolean enrolledDuringParticipationPeriod,
String participationPeriod) {
var returnMeasureReport = runEvaluateMeasure("2022-01-01", "2022-12-31", patientId, measureId, "Individual", null);
for (MeasureReport.MeasureReportGroupPopulationComponent population : returnMeasureReport.getGroupFirstRep()
.getPopulation())
switch (population.getCode().getCodingFirstRep().getCode()) {
case "initial-population" -> assertEquals(initialPopulationCount, population.getCount());
case "denominator" -> assertEquals(denominatorCount, population.getCount());
case "denominator-exclusion" -> assertEquals(denominatorExclusionCount, population.getCount());
case "numerator" -> assertEquals(numeratorCount, population.getCount());
}
switch (population.getCode().getCodingFirstRep().getCode()) {
case "initial-population" -> assertEquals(initialPopulationCount, population.getCount());
case "denominator" -> assertEquals(denominatorCount, population.getCount());
case "denominator-exclusion" -> assertEquals(denominatorExclusionCount, population.getCount());
case "numerator" -> assertEquals(numeratorCount, population.getCount());
}
Observation enrolledDuringParticipationPeriodObs = null;
Observation participationPeriodObs = null;
@ -88,76 +163,4 @@ class MeasureOperationProviderTest extends BaseCrR4TestServer {
assertNotNull(participationPeriodObs);
assertEquals(participationPeriod, participationPeriodObs.getValueCodeableConcept().getCodingFirstRep().getCode());
}
@Test
void testBCSEHEDISMY2022() {
loadBundle("BCSEHEDISMY2022-bundle.json");
runWithPatient("BCSEHEDISMY2022", "Patient/Patient-5", 0, 0, 0, 0, false,
"Interval[2020-10-01T00:00:00.000, 2022-12-31T23:59:59.999]");
runWithPatient("BCSEHEDISMY2022", "Patient/Patient-7", 1, 1, 0, 0, true,
"Interval[2020-10-01T00:00:00.000, 2022-12-31T23:59:59.999]");
runWithPatient("BCSEHEDISMY2022", "Patient/Patient-9", 0, 0, 0, 0, true,
"Interval[2020-10-01T00:00:00.000, 2022-12-31T23:59:59.999]");
runWithPatient("BCSEHEDISMY2022", "Patient/Patient-21", 1, 0, 1, 0, true,
"Interval[2020-10-01T00:00:00.000, 2022-12-31T23:59:59.999]");
runWithPatient("BCSEHEDISMY2022", "Patient/Patient-23", 1, 1, 0, 0, true,
"Interval[2020-10-01T00:00:00.000, 2022-12-31T23:59:59.999]");
runWithPatient("BCSEHEDISMY2022", "Patient/Patient-65", 1, 1, 0, 1, true,
"Interval[2020-10-01T00:00:00.000, 2022-12-31T23:59:59.999]");
}
@Test
void testClientNonPatientBasedMeasureEvaluate() {
this.loadBundle("ClientNonPatientBasedMeasureBundle.json");
var measure = read(new IdType("Measure", "InitialInpatientPopulation"));
assertNotNull(measure);
var returnMeasureReport = runEvaluateMeasure("2019-01-01", "2020-01-01", "Patient/97f27374-8a5c-4aa1-a26f-5a1ab03caa47", "InitialInpatientPopulation", "Individual", null);
String populationName = "initial-population";
int expectedCount = 2;
Optional<MeasureReport.MeasureReportGroupPopulationComponent> population = returnMeasureReport.getGroup().get(0)
.getPopulation().stream().filter(x -> x.hasCode() && x.getCode().hasCoding()
&& x.getCode().getCoding().get(0).getCode().equals(populationName))
.findFirst();
assertTrue(population.isPresent(), String.format("Unable to locate a population with id \"%s\"", populationName));
assertEquals(population.get().getCount(), expectedCount,
String.format("expected count for population \"%s\" did not match", populationName));
}
@Test
void testMeasureEvaluateMultiVersion() {
this.loadBundle("multiversion/EXM124-7.0.000-bundle.json");
this.loadBundle("multiversion/EXM124-9.0.000-bundle.json");
runEvaluateMeasure("2019-01-01", "2020-01-01", "Patient/numer-EXM124", "measure-EXM124-7.0.000", "Individual", null);
runEvaluateMeasure("2019-01-01", "2020-01-01", "Patient/numer-EXM124", "measure-EXM124-9.0.000", "Individual", null);
}
@Test
void testLargeValuesetMeasure() throws NoSuchElementException {
this.loadBundle("largeValueSetMeasureTest-Bundle.json");
var returnMeasureReport = runEvaluateMeasure("2023-01-01", "2024-01-01", null, "CMSTest", "population", null);
String populationName = "numerator";
int expectedCount = 1;
Optional<MeasureReport.MeasureReportGroupPopulationComponent> population = returnMeasureReport.getGroup().get(0)
.getPopulation().stream().filter(x -> x.hasCode() && x.getCode().hasCoding()
&& x.getCode().getCoding().get(0).getCode().equals(populationName))
.findFirst();
assertTrue(population.isPresent(), String.format("population \"%s\" not found in report", populationName));
assertEquals(
expectedCount,
population.get().getCount(),
String.format("expected count for population \"%s\" did not match", populationName)
);
}
}

View File

@ -4,7 +4,6 @@ import ca.uhn.fhir.cr.TestCqlProperties;
import ca.uhn.fhir.cr.TestCrConfig;
import ca.uhn.fhir.cr.common.CqlThreadFactory;
import ca.uhn.fhir.cr.config.r4.CrR4Config;
import ca.uhn.fhir.rest.api.SearchStyleEnum;
import org.cqframework.cql.cql2elm.CqlCompilerOptions;
import org.cqframework.cql.cql2elm.model.CompiledLibrary;
import org.cqframework.cql.cql2elm.model.Model;
@ -13,7 +12,6 @@ import org.hl7.elm.r1.VersionedIdentifier;
import org.opencds.cqf.cql.engine.execution.CqlEngine;
import org.opencds.cqf.cql.engine.runtime.Code;
import org.opencds.cqf.fhir.cql.EvaluationSettings;
import org.opencds.cqf.fhir.cql.engine.retrieve.BaseRetrieveProvider;
import org.opencds.cqf.fhir.cql.engine.retrieve.RetrieveSettings;
import org.opencds.cqf.fhir.cql.engine.terminology.TerminologySettings;
import org.opencds.cqf.fhir.cr.measure.CareGapsProperties;

View File

@ -645,7 +645,7 @@
{
"type": "depends-on",
"display": "Code system SNOMEDCT:2017-09",
"resource": "http://snomed.info/sct|http://snomed.info/sct/version/201709"
"resource": "http://snomed.info/sct/version/201709"
},
{
"type": "depends-on",
@ -91120,7 +91120,7 @@
{
"type": "depends-on",
"display": "Code system SNOMEDCT:2017-09",
"resource": "http://snomed.info/sct|http://snomed.info/sct/version/201709"
"resource": "http://snomed.info"
},
{
"type": "depends-on",
@ -149191,13 +149191,46 @@
}
],
"gender": "male",
"birthDate": "1965-01-01"
"birthDate": "1965-01-01",
"generalPractitioner":[
{"reference": "Practitioner/practitioner-EXM130"}
]
},
"request": {
"method": "PUT",
"url": "Patient/numer-EXM130"
}
},
{"resource":{
"resourceType": "Group",
"id": "group-EXM130",
"type": "person",
"actual": true,
"member": [
{ "entity": { "reference": "Patient/denom-EXM130" } },
{ "entity": { "reference": "Patient/numer-EXM130" } },
{ "entity": { "reference": "Patient/neg-ip-EXM130" } }
]
},
"request": {
"method": "PUT",
"url": "Group/group-EXM130"
}
},
{"resource":{
"resourceType": "Practitioner",
"id": "practitioner-EXM130",
"name": [ {
"family": "Dawg",
"given": [ "Jay" ],
"prefix": [ "Dr" ]
} ]
},
"request": {
"method": "PUT",
"url": "Practitioner/practitioner-EXM130"
}
},
{
"resource": {
"resourceType": "ValueSet",

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -9,7 +9,7 @@
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<packaging>pom</packaging>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<name>HAPI-FHIR</name>
<description>An open-source implementation of the FHIR specification in Java.</description>
@ -1006,7 +1006,7 @@
<ucum_version>1.0.8</ucum_version>
<!-- CQL Support -->
<clinical-reasoning.version>3.3.1</clinical-reasoning.version>
<clinical-reasoning.version>3.4.0</clinical-reasoning.version>
<!-- Site properties -->
<fontawesomeVersion>5.4.1</fontawesomeVersion>

View File

@ -7,7 +7,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>7.1.9-SNAPSHOT</version>
<version>7.1.10-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>