Adding cqf-ruler project

This commit is contained in:
Chris Schuler 2017-11-12 16:09:58 -07:00
parent 32a4a081d9
commit 2ea8acdc33
177 changed files with 32180 additions and 0 deletions

137
hapi-fhir-jpaserver-cqf-ruler/.gitignore vendored Normal file
View File

@ -0,0 +1,137 @@
# Created by https://www.gitignore.io
.DS_Store
# RxNorm data
src/main/resources/cds/OpioidManagementTerminologyKnowledge.db
# log files
*.log
### Java ###
*.class
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.ear
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
### Maven ###
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
### Vim ###
[._]*.s[a-w][a-z]
[._]s[a-w][a-z]
*.un~
Session.vim
.netrwhist
*~
### Intellij ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm
*.iml
## Directory-based project format:
.idea/
# if you remove the above rule, at least ignore the following:
# User-specific stuff:
# .idea/workspace.xml
# .idea/tasks.xml
# .idea/dictionaries
# Sensitive or high-churn files:
# .idea/dataSources.ids
# .idea/dataSources.xml
# .idea/sqlDataSources.xml
# .idea/dynamic.xml
# .idea/uiDesigner.xml
# Gradle:
# .idea/gradle.xml
# .idea/libraries
# Mongo Explorer plugin:
# .idea/mongoSettings.xml
## File-based project format:
*.ipr
*.iws
## Plugin-specific files:
# IntelliJ
/out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
### Eclipse ###
*.pydevproject
.metadata
.gradle
bin/
tmp/
*.tmp
*.bak
*.swp
*~.nib
local.properties
.loadpath
# Eclipse Core
.project
# External tool builders
.externalToolBuilders/
# Locally stored "Eclipse launch configurations"
*.launch
# CDT-specific
.cproject
# JDT-specific (Eclipse Java Development Tools)
# PDT-specific
.buildpath
# sbteclipse plugin
.target
# TeXlipse plugin
.texlipse
*.data
*.lck
*.properties
*.script
/*.tmp
*.txt

View File

@ -0,0 +1,31 @@
# cqf-ruler
The CQF Ruler is an implementation of FHIR's [Clinical Reasoning Module](
http://hl7.org/fhir/clinicalreasoning-module.html) and serves as a
knowledge artifact repository and clinical decision support service.
## Usage
- `$ mvn install`
- `$ mvn -Djetty.http.port=XXXX jetty:run`
Visit the [wiki](https://github.com/DBCG/cqf-ruler/wiki) for more documentation.
## Dependencies
Before the instructions in the above "Usage" section will work, you need to
install several primary dependencies.
### Java
Go to [http://www.oracle.com/technetwork/java/javase/downloads/](
http://www.oracle.com/technetwork/java/javase/downloads/) and download the
latest (version 8 or higher) JDK for your platform, and install it.
### Apache Maven
Go to [https://maven.apache.org](https://maven.apache.org), visit the main
"Download" page, and under "Files" download a binary archive of your
choice. Then unpack that archive file and follow the installation
instructions in its README.txt. The end result of this should be that the
binary "mvn" is now in your path.

View File

@ -0,0 +1,357 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>3.1.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>cqf-ruler</artifactId>
<packaging>war</packaging>
<name>CQF Ruler</name>
<repositories>
<repository>
<id>oss-sonatype</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>oss-sonatype-public</id>
<url>https://oss.sonatype.org/content/groups/public/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.opencds.cqf</groupId>
<artifactId>cql-engine</artifactId>
<version>1.2.36-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.opencds.cqf</groupId>
<artifactId>cql-engine-fhir</artifactId>
<version>1.2.36-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>info.cqframework</groupId>
<artifactId>cql-to-elm</artifactId>
<version>1.2.16</version>
</dependency>
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.15.1</version>
</dependency>
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
<!-- This dependency includes the core HAPI-FHIR classes -->
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId>
<version>${project.version}</version>
</dependency>
<!-- At least one "structures" JAR must also be included -->
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu2</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu3</artifactId>
<version>${project.version}</version>
</dependency>
<!-- This dependency includes the JPA server itself, which is packaged separately from the rest of HAPI FHIR -->
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-jpaserver-base</artifactId>
<version>${project.version}</version>
</dependency>
<!-- This dependency is used for the "FHIR Tester" web app overlay -->
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-testpage-overlay</artifactId>
<version>${project.version}</version>
<type>war</type>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-testpage-overlay</artifactId>
<version>${project.version}</version>
<classifier>classes</classifier>
<scope>provided</scope>
</dependency>
<!-- HAPI-FHIR uses Logback for logging support. The logback library is included automatically by Maven as a part of the hapi-fhir-base dependency, but you also need to include a logging library. Logback
is used here, but log4j would also be fine. -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.22</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.15</version>
</dependency>
<!-- Needed for JEE/Servlet support -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<!-- If you are using HAPI narrative generation, you will need to include Thymeleaf as well. Otherwise the following can be omitted. -->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId>
</dependency>
<!-- Spring Web is used to deploy the server to a web container. -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<!-- You may not need this if you are deploying to an application server which provides database connection pools itself. -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>9.4.1212.jre7</version>
</dependency>
<dependency>
<groupId>org.apache.derby</groupId>
<artifactId>derby</artifactId>
</dependency>
<dependency>
<groupId>org.apache.derby</groupId>
<artifactId>derbynet</artifactId>
</dependency>
<dependency>
<groupId>org.apache.derby</groupId>
<artifactId>derbyclient</artifactId>
</dependency>
<!-- The following dependencies are only needed for automated unit tests, you do not neccesarily need them to run the example. -->
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlets</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.phloc</groupId>
<artifactId>phloc-schematron</artifactId>
</dependency>
<dependency>
<groupId>org.ebaysf.web</groupId>
<artifactId>cors-filter</artifactId>
<exclusions>
<exclusion>
<artifactId>servlet-api</artifactId>
<groupId>javax.servlet</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<!-- Tells Maven to name the generated WAR file as cql-measure-processor.war -->
<finalName>cqf-ruler</finalName>
<plugins>
<!-- Tell Maven which Java source version you want to use -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<configuration>
<webApp>
<contextPath>/cqf-ruler</contextPath>
<allowDuplicateFragmentNames>true</allowDuplicateFragmentNames>
</webApp>
</configuration>
</plugin>
<!-- The configuration here tells the WAR plugin to include the FHIR Tester overlay. You can omit it if you are not using that feature. -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<archive>
<manifestEntries>
<Build-Time>${maven.build.timestamp}</Build-Time>
</manifestEntries>
</archive>
<overlays>
<overlay>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-testpage-overlay</artifactId>
</overlay>
</overlays>
</configuration>
</plugin>
<!-- This plugin is just a part of the HAPI internal build process, you do not need to incude it in your own projects -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<!-- This is to run the integration tests -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<configuration>
<redirectTestOutputToFile>true</redirectTestOutputToFile>
</configuration>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.20.1</version>
<dependencies>
<dependency>
<groupId>org.apache.maven.surefire</groupId>
<artifactId>surefire-junit47</artifactId>
<version>2.20.1</version>
</dependency>
</dependencies>
</plugin>
<!--<plugin>-->
<!--<groupId>org.apache.tomcat.maven</groupId>-->
<!--<artifactId>tomcat7-maven-plugin</artifactId>-->
<!--<configuration>-->
<!--<path>/</path>-->
<!--</configuration>-->
<!--</plugin>-->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>exec</goal>
</goals>
</execution>
</executions>
<configuration>
<executable>maven</executable>
<mainClass>ca.uhn.fhir.jpa.cqf.ruler.helpers.XlsxToValueSet</mainClass>
<arguments>
<argument>-b</argument>
<argument>-o</argument>
<argument>-s</argument>
<argument>-v</argument>
<argument>-c</argument>
<argument>-d</argument>
<argument>-u</argument>
<argument>-outDir</argument>
</arguments>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,30 @@
package ca.uhn.fhir.jpa.cqf.ruler.builders;
import org.hl7.fhir.dstu3.model.Annotation;
import org.hl7.fhir.dstu3.model.Type;
import java.util.Date;
public class AnnotationBuilder extends BaseBuilder<Annotation> {
public AnnotationBuilder() {
super(new Annotation());
}
// Type is one of the following: Reference or String
public AnnotationBuilder buildAuthor(Type choice) {
complexProperty.setAuthor(choice);
return this;
}
public AnnotationBuilder buildTime(Date date) {
complexProperty.setTime(date);
return this;
}
// required
public AnnotationBuilder buildText(String text) {
complexProperty.setText(text);
return this;
}
}

View File

@ -0,0 +1,21 @@
package ca.uhn.fhir.jpa.cqf.ruler.builders;
/*
These builders are based off of work performed by Philips Healthcare.
I simplified their work with this generic base class and added/expanded builders.
Tip of the hat to Philips Healthcare developer nly98977
*/
public class BaseBuilder<T> {
protected T complexProperty;
public BaseBuilder(T complexProperty) {
this.complexProperty = complexProperty;
}
public T build() {
return complexProperty;
}
}

View File

@ -0,0 +1,68 @@
package ca.uhn.fhir.jpa.cqf.ruler.builders;
import org.hl7.fhir.dstu3.model.Annotation;
import org.hl7.fhir.dstu3.model.CarePlan;
import org.hl7.fhir.dstu3.model.CodeableConcept;
import org.hl7.fhir.dstu3.model.Reference;
import java.util.ArrayList;
import java.util.List;
public class CarePlanActivityBuilder extends BaseBuilder<CarePlan.CarePlanActivityComponent> {
public CarePlanActivityBuilder() {
super(new CarePlan.CarePlanActivityComponent());
}
public CarePlanActivityBuilder buildOutcomeConcept(List<CodeableConcept> concepts) {
complexProperty.setOutcomeCodeableConcept(concepts);
return this;
}
public CarePlanActivityBuilder buildOutcomeConcept(CodeableConcept concept) {
if (!complexProperty.hasOutcomeCodeableConcept()) {
complexProperty.setOutcomeCodeableConcept(new ArrayList<>());
}
complexProperty.addOutcomeCodeableConcept(concept);
return this;
}
public CarePlanActivityBuilder buildOutcomeReference(List<Reference> references) {
complexProperty.setOutcomeReference(references);
return this;
}
public CarePlanActivityBuilder buildOutcomeReference(Reference reference) {
if (!complexProperty.hasOutcomeReference()) {
complexProperty.setOutcomeReference(new ArrayList<>());
}
complexProperty.addOutcomeReference(reference);
return this;
}
public CarePlanActivityBuilder buildProgress(List<Annotation> annotations) {
complexProperty.setProgress(annotations);
return this;
}
public CarePlanActivityBuilder buildProgress(Annotation annotation) {
if (!complexProperty.hasProgress()) {
complexProperty.setProgress(new ArrayList<>());
}
complexProperty.addProgress(annotation);
return this;
}
public CarePlanActivityBuilder buildReference(Reference reference) {
complexProperty.setReference(reference);
return this;
}
public CarePlanActivityBuilder buildDetail(CarePlan.CarePlanActivityDetailComponent detail) {
complexProperty.setDetail(detail);
return this;
}
}

View File

@ -0,0 +1,139 @@
package ca.uhn.fhir.jpa.cqf.ruler.builders;
import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.exceptions.FHIRException;
import java.util.ArrayList;
import java.util.List;
public class CarePlanActivityDetailBuilder extends BaseBuilder<CarePlan.CarePlanActivityDetailComponent> {
public CarePlanActivityDetailBuilder() {
super(new CarePlan.CarePlanActivityDetailComponent());
}
public CarePlanActivityDetailBuilder buildCategory(CodeableConcept category) {
complexProperty.setCategory(category);
return this;
}
public CarePlanActivityDetailBuilder buildDefinition(Reference reference) {
complexProperty.setDefinition(reference);
return this;
}
public CarePlanActivityDetailBuilder buildCode(CodeableConcept code) {
complexProperty.setCode(code);
return this;
}
public CarePlanActivityDetailBuilder buildReasonCode(List<CodeableConcept> concepts) {
complexProperty.setReasonCode(concepts);
return this;
}
public CarePlanActivityDetailBuilder buildReasonCode(CodeableConcept concept) {
if (!complexProperty.hasReasonCode()) {
complexProperty.setReasonCode(new ArrayList<>());
}
complexProperty.addReasonCode(concept);
return this;
}
public CarePlanActivityDetailBuilder buildReasonReference(List<Reference> references) {
complexProperty.setReasonReference(references);
return this;
}
public CarePlanActivityDetailBuilder buildReasonReference(Reference reference) {
if (!complexProperty.hasReasonReference()) {
complexProperty.setReasonReference(new ArrayList<>());
}
complexProperty.addReasonReference(reference);
return this;
}
public CarePlanActivityDetailBuilder buildGoal(List<Reference> goals) {
complexProperty.setGoal(goals);
return this;
}
public CarePlanActivityDetailBuilder buildGoal(Reference goal) {
if (!complexProperty.hasGoal()) {
complexProperty.setGoal(new ArrayList<>());
}
complexProperty.addGoal(goal);
return this;
}
// required
public CarePlanActivityDetailBuilder buildStatus(CarePlan.CarePlanActivityStatus status) {
complexProperty.setStatus(status);
return this;
}
// String overload
public CarePlanActivityDetailBuilder buildStatus(String status) throws FHIRException {
complexProperty.setStatus(CarePlan.CarePlanActivityStatus.fromCode(status));
return this;
}
public CarePlanActivityDetailBuilder buildStatusReason(String reason) {
complexProperty.setStatusReason(reason);
return this;
}
public CarePlanActivityDetailBuilder buildProhibited(boolean prohibited) {
complexProperty.setProhibited(prohibited);
return this;
}
// Type is one of the following: Timing, Period, or String
public CarePlanActivityDetailBuilder buildScheduled(Type type) {
complexProperty.setScheduled(type);
return this;
}
public CarePlanActivityDetailBuilder buildLocation(Reference location) {
complexProperty.setLocation(location);
return this;
}
public CarePlanActivityDetailBuilder buildPerformer(List<Reference> performers) {
complexProperty.setPerformer(performers);
return this;
}
public CarePlanActivityDetailBuilder buildPerformer(Reference performer) {
if (!complexProperty.hasPerformer()) {
complexProperty.setPerformer(new ArrayList<>());
}
complexProperty.addPerformer(performer);
return this;
}
// Type is one of the following: CodeableConcept or Reference
public CarePlanActivityDetailBuilder buildProduct(Type type) {
complexProperty.setProduct(type);
return this;
}
public CarePlanActivityDetailBuilder buildDailyAmount(SimpleQuantity amount) {
complexProperty.setDailyAmount(amount);
return this;
}
public CarePlanActivityDetailBuilder buildQuantity(SimpleQuantity quantity) {
complexProperty.setQuantity(quantity);
return this;
}
public CarePlanActivityDetailBuilder buildDescription(String description) {
complexProperty.setDescription(description);
return this;
}
}

View File

@ -0,0 +1,252 @@
package ca.uhn.fhir.jpa.cqf.ruler.builders;
import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.exceptions.FHIRException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class CarePlanBuilder extends BaseBuilder<CarePlan> {
public CarePlanBuilder() {
super(new CarePlan());
}
public CarePlanBuilder buildIdentifier(List<Identifier> identifiers) {
complexProperty.setIdentifier(identifiers);
return this;
}
public CarePlanBuilder buildIdentifier(Identifier identifier) {
if (!complexProperty.hasIdentifier()) {
complexProperty.setIdentifier(new ArrayList<>());
}
complexProperty.addIdentifier(identifier);
return this;
}
public CarePlanBuilder buildDefinition(List<Reference> references) {
complexProperty.setDefinition(references);
return this;
}
public CarePlanBuilder buildDefinition(Reference reference) {
if (!complexProperty.hasDefinition()) {
complexProperty.setDefinition(new ArrayList<>());
}
complexProperty.addDefinition(reference);
return this;
}
public CarePlanBuilder buildBasedOn(List<Reference> references) {
complexProperty.setBasedOn(references);
return this;
}
public CarePlanBuilder buildBasedOn(Reference reference) {
if (!complexProperty.hasBasedOn()) {
complexProperty.setBasedOn(new ArrayList<>());
}
complexProperty.addBasedOn(reference);
return this;
}
public CarePlanBuilder buildReplaces(List<Reference> references) {
complexProperty.setReplaces(references);
return this;
}
public CarePlanBuilder buildReplaces(Reference reference) {
if (!complexProperty.hasReplaces()) {
complexProperty.setReplaces(new ArrayList<>());
}
complexProperty.addReplaces(reference);
return this;
}
public CarePlanBuilder buildPartOf(List<Reference> references) {
complexProperty.setPartOf(references);
return this;
}
public CarePlanBuilder buildPartOf(Reference reference) {
if (!complexProperty.hasPartOf()) {
complexProperty.setPartOf(new ArrayList<>());
}
complexProperty.addPartOf(reference);
return this;
}
// required
public CarePlanBuilder buildStatus(CarePlan.CarePlanStatus status) {
complexProperty.setStatus(status);
return this;
}
// String overload
public CarePlanBuilder buildStatus(String status) throws FHIRException {
complexProperty.setStatus(CarePlan.CarePlanStatus.fromCode(status));
return this;
}
// required
public CarePlanBuilder buildIntent(CarePlan.CarePlanIntent intent) {
complexProperty.setIntent(intent);
return this;
}
// String overload
public CarePlanBuilder buildIntent(String intent) throws FHIRException {
complexProperty.setIntent(CarePlan.CarePlanIntent.fromCode(intent));
return this;
}
public CarePlanBuilder buildCategory(List<CodeableConcept> categories) {
complexProperty.setCategory(categories);
return this;
}
public CarePlanBuilder buildCategory(CodeableConcept category) {
if (!complexProperty.hasCategory()) {
complexProperty.setCategory(new ArrayList<>());
}
complexProperty.addCategory(category);
return this;
}
public CarePlanBuilder buildTitle(String title) {
complexProperty.setTitle(title);
return this;
}
public CarePlanBuilder buildDescription(String description) {
complexProperty.setDescription(description);
return this;
}
// required
public CarePlanBuilder buildSubject(Reference reference) {
complexProperty.setSubject(reference);
return this;
}
public CarePlanBuilder buildContext(Reference reference) {
complexProperty.setContext(reference);
return this;
}
public CarePlanBuilder buildPeriod(Period period) {
complexProperty.setPeriod(period);
return this;
}
public CarePlanBuilder buildAuthor(List<Reference> references) {
complexProperty.setAuthor(references);
return this;
}
public CarePlanBuilder buildAuthor(Reference reference) {
if (!complexProperty.hasAuthor()) {
complexProperty.setAuthor(new ArrayList<>());
}
complexProperty.addAuthor(reference);
return this;
}
public CarePlanBuilder buildCareTeam(List<Reference> careTeams) {
complexProperty.setCareTeam(careTeams);
return this;
}
public CarePlanBuilder buildCareTeam(Reference careTeam) {
if (!complexProperty.hasCareTeam()) {
complexProperty.setCareTeam(new ArrayList<>());
}
complexProperty.addCareTeam(careTeam);
return this;
}
public CarePlanBuilder buildAddresses(List<Reference> addresses) {
complexProperty.setAddresses(addresses);
return this;
}
public CarePlanBuilder buildAddresses(Reference address) {
if (!complexProperty.hasAddresses()) {
complexProperty.setAddresses(new ArrayList<>());
}
complexProperty.addAddresses(address);
return this;
}
public CarePlanBuilder buildSupportingInfo(List<Reference> supportingInfo) {
complexProperty.setSupportingInfo(supportingInfo);
return this;
}
public CarePlanBuilder buildSupportingInfo(Reference supportingInfo) {
if (!complexProperty.hasSupportingInfo()) {
complexProperty.setSupportingInfo(new ArrayList<>());
}
complexProperty.addSupportingInfo(supportingInfo);
return this;
}
public CarePlanBuilder buildGoal(List<Reference> goals) {
complexProperty.setGoal(goals);
return this;
}
public CarePlanBuilder buildGoal(Reference goal) {
if (!complexProperty.hasGoal()) {
complexProperty.setGoal(new ArrayList<>());
}
complexProperty.addGoal(goal);
return this;
}
public CarePlanBuilder buildActivity(List<CarePlan.CarePlanActivityComponent> activities) {
complexProperty.setActivity(activities);
return this;
}
public CarePlanBuilder buildActivity(CarePlan.CarePlanActivityComponent activity) {
if (!complexProperty.hasActivity()) {
complexProperty.setActivity(Collections.singletonList(new CarePlan.CarePlanActivityComponent()));
}
complexProperty.getActivity().add(activity);
return this;
}
public CarePlanBuilder buildNotes(List<Annotation> notes) {
complexProperty.setNote(notes);
return this;
}
public CarePlanBuilder buildNotes(Annotation note) {
if (!complexProperty.hasNote()) {
complexProperty.setNote(new ArrayList<>());
}
complexProperty.addNote(note);
return this;
}
public CarePlanBuilder buildLanguage(String language) {
complexProperty.setLanguage(language);
return this;
}
}

View File

@ -0,0 +1,33 @@
package ca.uhn.fhir.jpa.cqf.ruler.builders;
import org.hl7.fhir.dstu3.model.CodeableConcept;
import org.hl7.fhir.dstu3.model.Coding;
import java.util.ArrayList;
import java.util.List;
public class CodeableConceptBuilder extends BaseBuilder<CodeableConcept> {
public CodeableConceptBuilder() {
super(new CodeableConcept());
}
public CodeableConceptBuilder buildCoding(List<Coding> coding) {
complexProperty.setCoding(coding);
return this;
}
public CodeableConceptBuilder buildCoding(Coding coding) {
if (!complexProperty.hasCoding()) {
complexProperty.setCoding(new ArrayList<>());
}
complexProperty.addCoding(coding);
return this;
}
public CodeableConceptBuilder buildText(String text) {
complexProperty.setText(text);
return this;
}
}

View File

@ -0,0 +1,35 @@
package ca.uhn.fhir.jpa.cqf.ruler.builders;
import org.hl7.fhir.dstu3.model.Coding;
public class CodingBuilder extends BaseBuilder<Coding> {
public CodingBuilder() {
super(new Coding());
}
public CodingBuilder buildSystem(String system) {
complexProperty.setSystem(system);
return this;
}
public CodingBuilder buildVersion(String version) {
complexProperty.setVersion(version);
return this;
}
public CodingBuilder buildCode(String code) {
complexProperty.setCode(code);
return this;
}
public CodingBuilder buildDisplay(String display) {
complexProperty.setDisplay(display);
return this;
}
public CodingBuilder buildUserSelected(boolean selected) {
complexProperty.setUserSelected(selected);
return this;
}
}

View File

@ -0,0 +1,49 @@
package ca.uhn.fhir.jpa.cqf.ruler.builders;
import org.hl7.fhir.dstu3.model.CodeableConcept;
import org.hl7.fhir.dstu3.model.Identifier;
import org.hl7.fhir.dstu3.model.Period;
import org.hl7.fhir.dstu3.model.Reference;
import org.hl7.fhir.exceptions.FHIRException;
public class IdentifierBuilder extends BaseBuilder<Identifier> {
public IdentifierBuilder() {
super(new Identifier());
}
public IdentifierBuilder buildUse(Identifier.IdentifierUse use) {
complexProperty.setUse(use);
return this;
}
public IdentifierBuilder buildUse(String use) throws FHIRException {
complexProperty.setUse(Identifier.IdentifierUse.fromCode(use));
return this;
}
public IdentifierBuilder buildType(CodeableConcept type) {
complexProperty.setType(type);
return this;
}
public IdentifierBuilder buildSystem(String system) {
complexProperty.setSystem(system);
return this;
}
public IdentifierBuilder buildValue(String value) {
complexProperty.setValue(value);
return this;
}
public IdentifierBuilder buildPeriod(Period period) {
complexProperty.setPeriod(period);
return this;
}
public IdentifierBuilder buildAssigner(Reference assigner) {
complexProperty.setAssigner(assigner);
return this;
}
}

View File

@ -0,0 +1,18 @@
package ca.uhn.fhir.jpa.cqf.ruler.builders;
import org.opencds.cqf.cql.runtime.DateTime;
import java.util.Date;
public class JavaDateBuilder extends BaseBuilder<Date> {
public JavaDateBuilder() {
super(new Date());
}
public JavaDateBuilder buildFromDateTime(DateTime dateTime) {
org.joda.time.DateTime dt = new org.joda.time.DateTime(dateTime.getPartial());
complexProperty = dt.toDate();
return this;
}
}

View File

@ -0,0 +1,22 @@
package ca.uhn.fhir.jpa.cqf.ruler.builders;
import org.hl7.fhir.dstu3.model.Period;
import java.util.Date;
public class PeriodBuilder extends BaseBuilder<Period> {
public PeriodBuilder() {
super(new Period());
}
public PeriodBuilder buildStart(Date start) {
complexProperty.setStart(start);
return this;
}
public PeriodBuilder buildEnd(Date end) {
complexProperty.setEnd(end);
return this;
}
}

View File

@ -0,0 +1,26 @@
package ca.uhn.fhir.jpa.cqf.ruler.builders;
import org.hl7.fhir.dstu3.model.Identifier;
import org.hl7.fhir.dstu3.model.Reference;
public class ReferenceBuilder extends BaseBuilder<Reference> {
public ReferenceBuilder() {
super(new Reference());
}
public ReferenceBuilder buildReference(String reference) {
complexProperty.setReference(reference);
return this;
}
public ReferenceBuilder buildIdentifier(Identifier identifier) {
complexProperty.setIdentifier(identifier);
return this;
}
public ReferenceBuilder buildDisplay(String display) {
complexProperty.setDisplay(display);
return this;
}
}

View File

@ -0,0 +1,47 @@
package ca.uhn.fhir.jpa.cqf.ruler.builders;
import org.hl7.fhir.dstu3.model.Enumerations;
import org.hl7.fhir.dstu3.model.ValueSet;
import org.hl7.fhir.exceptions.FHIRException;
public class ValueSetBuider extends BaseBuilder<ValueSet> {
public ValueSetBuider(ValueSet complexProperty) {
super(complexProperty);
}
public ValueSetBuider buildId(String id) {
complexProperty.setId(id);
return this;
}
public ValueSetBuider buildUrl(String url) {
complexProperty.setUrl(url);
return this;
}
public ValueSetBuider buildTitle(String title) {
complexProperty.setTitle(title);
return this;
}
public ValueSetBuider buildStatus() {
complexProperty.setStatus(Enumerations.PublicationStatus.DRAFT);
return this;
}
public ValueSetBuider buildStatus(String status) throws FHIRException {
complexProperty.setStatus(Enumerations.PublicationStatus.fromCode(status));
return this;
}
public ValueSetBuider buildStatus(Enumerations.PublicationStatus status) {
complexProperty.setStatus(status);
return this;
}
public ValueSetBuider buildCompose(ValueSet.ValueSetComposeComponent compose) {
complexProperty.setCompose(compose);
return this;
}
}

View File

@ -0,0 +1,22 @@
package ca.uhn.fhir.jpa.cqf.ruler.builders;
import org.hl7.fhir.dstu3.model.ValueSet;
import java.util.List;
public class ValueSetComposeBuilder extends BaseBuilder<ValueSet.ValueSetComposeComponent> {
public ValueSetComposeBuilder(ValueSet.ValueSetComposeComponent complexProperty) {
super(complexProperty);
}
public ValueSetComposeBuilder buildIncludes(List<ValueSet.ConceptSetComponent> includes) {
complexProperty.setInclude(includes);
return this;
}
public ValueSetComposeBuilder buildIncludes(ValueSet.ConceptSetComponent include) {
complexProperty.addInclude(include);
return this;
}
}

View File

@ -0,0 +1,37 @@
package ca.uhn.fhir.jpa.cqf.ruler.builders;
import org.hl7.fhir.dstu3.model.ValueSet;
import java.util.List;
public class ValueSetIncludesBuilder extends BaseBuilder<ValueSet.ConceptSetComponent> {
public ValueSetIncludesBuilder(ValueSet.ConceptSetComponent complexProperty) {
super(complexProperty);
}
public ValueSetIncludesBuilder buildSystem(String system) {
complexProperty.setSystem(system);
return this;
}
public ValueSetIncludesBuilder buildVersion(String version) {
complexProperty.setVersion(version);
return this;
}
public ValueSetIncludesBuilder buildConcept(List<ValueSet.ConceptReferenceComponent> concepts) {
complexProperty.setConcept(concepts);
return this;
}
public ValueSetIncludesBuilder buildConcept(ValueSet.ConceptReferenceComponent concept) {
complexProperty.addConcept(concept);
return this;
}
public ValueSetIncludesBuilder buildConcept(String code, String display) {
complexProperty.addConcept(new ValueSet.ConceptReferenceComponent().setCode(code).setDisplay(display));
return this;
}
}

View File

@ -0,0 +1,353 @@
package ca.uhn.fhir.jpa.cqf.ruler.cds;
import ca.uhn.fhir.context.FhirContext;
import com.google.gson.*;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.json.simple.JSONObject;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Christopher on 5/4/2017.
*/
public class CdsCard {
private String summary;
private String detail;
private String indicator;
public boolean hasSummary() {
return this.summary != null && !this.summary.isEmpty();
}
public String getSummary() {
return this.summary;
}
public CdsCard setSummary(String summary) {
this.summary = summary;
return this;
}
public boolean hasDetail() {
return this.detail != null && !this.detail.isEmpty();
}
public String getDetail() {
return this.detail;
}
public CdsCard setDetail(String detail) {
this.detail = detail;
return this;
}
public boolean hasIndicator() {
return this.indicator != null && !this.indicator.isEmpty();
}
public String getIndicator() {
return this.detail;
}
public CdsCard setIndicator(String indicator) {
this.indicator = indicator;
return this;
}
private Source source;
public static class Source {
private String label;
private String url;
public boolean hasLabel() {
return this.label != null && !this.label.isEmpty();
}
public String getLabel() {
return this.label;
}
public CdsCard.Source setLabel(String label) {
this.label = label;
return this;
}
public boolean hasUrl() {
return this.url != null && !this.url.isEmpty();
}
public String getUrl() {
return this.url;
}
public CdsCard.Source setUrl(String url) {
this.url = url;
return this;
}
}
public boolean hasSource() {
return source.hasLabel() || source.hasUrl();
}
public Source getSource() {
return this.source;
}
public CdsCard setSource(Source source) {
this.source = source;
return this;
}
private List<Suggestions> suggestions;
public static class Suggestions {
private String label;
private String uuid;
private List<Action> actions;
public boolean hasLabel() {
return this.label != null && !this.label.isEmpty();
}
public String getLabel() {
return this.label;
}
public CdsCard.Suggestions setLabel(String label) {
this.label = label;
return this;
}
public boolean hasUuid() {
return this.uuid != null && !this.uuid.isEmpty();
}
public String getUuid() {
return this.uuid;
}
public CdsCard.Suggestions setUuid(String uuid) {
this.uuid = uuid;
return this;
}
public boolean hasActions() {
return this.actions != null && !this.actions.isEmpty();
}
public List<Action> getActions() {
return this.actions;
}
public CdsCard.Suggestions setActions(List<Action> actions) {
this.actions = actions;
return this;
}
public static class Action {
enum ActionType {create, update, delete}
private ActionType type;
private String description;
private IBaseResource resource;
public boolean hasType() {
return this.type != null;
}
public ActionType getType() {
return this.type;
}
public Action setType(ActionType type) {
this.type = type;
return this;
}
public boolean hasDescription() {
return this.description != null && !this.description.isEmpty();
}
public String getDescription() {
return this.description;
}
public Action setDescription(String description) {
this.description = description;
return this;
}
public boolean hasResource() {
return this.resource != null;
}
public IBaseResource getResource() {
return this.resource;
}
public Action setResource(IBaseResource resource) {
this.resource = resource;
return this;
}
}
}
public boolean hasSuggestions() {
return this.suggestions != null && !this.suggestions.isEmpty();
}
public List<Suggestions> getSuggestions() {
return this.suggestions;
}
public CdsCard setSuggestions(List<Suggestions> suggestions) {
this.suggestions = suggestions;
return this;
}
private List<Links> links;
public static class Links {
private String label;
private String url;
private String type;
public boolean hasLabel() {
return this.label != null && !this.label.isEmpty();
}
public String getLabel() {
return this.label;
}
public CdsCard.Links setLabel(String label) {
this.label = label;
return this;
}
public boolean hasUrl() {
return this.url != null && !this.url.isEmpty();
}
public String getUrl() {
return this.url;
}
public CdsCard.Links setUrl(String url) {
this.url = url;
return this;
}
public boolean hasType() {
return this.type != null && !this.type.isEmpty();
}
public String getType() {
return this.type;
}
public CdsCard.Links setType(String type) {
this.type = type;
return this;
}
}
public boolean hasLinks() {
return this.links != null && !this.links.isEmpty();
}
public List<Links> getLinks() {
return this.links;
}
public CdsCard setLinks(List<Links> links) {
this.links = links;
return this;
}
public CdsCard() {
this.source = new Source();
this.suggestions = new ArrayList<>();
this.links = new ArrayList<>();
}
public JsonObject toJson() {
JsonObject card = new JsonObject();
if (hasSummary()) {
card.addProperty("summary", summary);
}
if (hasIndicator()) {
card.addProperty("indicator", indicator);
}
if (hasDetail()) {
card.addProperty("detail", detail);
}
// TODO: Source
if (hasSuggestions()) {
JsonArray suggestionArray = new JsonArray();
for (Suggestions suggestion : getSuggestions()) {
JsonObject suggestionObj = new JsonObject();
if (suggestion.hasLabel()) {
suggestionObj.addProperty("label", suggestion.getLabel());
}
if (suggestion.hasUuid()) {
suggestionObj.addProperty("uuid", suggestion.getUuid());
}
if (suggestion.hasActions()) {
JsonArray actionArray = new JsonArray();
for (Suggestions.Action action : suggestion.getActions()) {
JsonObject actionObj = new JsonObject();
if (action.hasDescription()) {
actionObj.addProperty("description", action.getDescription());
}
if (action.hasType()) {
actionObj.addProperty("type", action.getType().toString());
}
if (action.hasResource()) {
JsonElement res = new JsonParser().parse(FhirContext.forDstu3().newJsonParser().setPrettyPrint(true).encodeResourceToString(action.getResource()));
actionObj.add("resource", res);
}
actionArray.add(actionObj);
}
suggestionObj.add("actions", actionArray);
}
suggestionArray.add(suggestionObj);
}
card.add("suggestions", suggestionArray);
}
if (hasLinks()) {
JsonArray linksArray = new JsonArray();
for (Links linkElement : getLinks()) {
JsonObject link = new JsonObject();
if (linkElement.hasLabel()) {
link.addProperty("label", linkElement.getLabel());
}
if (linkElement.hasUrl()) {
link.addProperty("url", linkElement.getUrl());
}
if (linkElement.hasType()) {
link.addProperty("type", linkElement.getType());
}
linksArray.add(link);
}
card.add("links", linksArray);
}
return card;
}
// public JSONObject toJson() {
// JSONObject card = new JSONObject();
// if (hasSummary()) {
// card.put("summary", summary);
// }
// if (hasIndicator()) {
// card.put("indicator", indicator);
// }
// if (hasDetail()) {
// card.put("detail", detail);
// }
// // TODO: Source & Suggestions
// if (hasLinks()) {
// JSONArray linksArray = new JSONArray();
// for (Links linkElement : getLinks()) {
// JSONObject link = new JSONObject();
// if (linkElement.hasLabel()) {
// link.put("label", linkElement.getLabel());
// }
// if (linkElement.hasUrl()) {
// link.put("url", linkElement.getUrl());
// }
// if (linkElement.hasType()) {
// link.put("type", linkElement.getType());
// }
// linksArray.add(link);
// }
// card.put("links", linksArray);
// }
// return card;
// }
public JSONObject returnSuccess() {
JSONObject success = new JSONObject();
success.put("summary", "Success");
success.put("indicator", "success");
success.put("detail", "The MME is within the recommended range.");
return success;
}
}

View File

@ -0,0 +1,70 @@
package ca.uhn.fhir.jpa.cqf.ruler.cds;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* Created by Christopher Schuler on 5/1/2017.
*/
public class CdsHooksHelper {
public static void DisplayDiscovery(HttpServletResponse response) throws IOException {
response.setContentType("application/json");
JSONObject jsonResponse = new JSONObject();
JSONArray jsonArray = new JSONArray();
JSONObject opioidGuidance = new JSONObject();
opioidGuidance.put("hook", "medication-prescribe");
opioidGuidance.put("name", "Opioid Morphine Milligram Equivalence (MME) Guidance Service");
opioidGuidance.put("description", "CDS Service that finds the MME of an opioid medication and provides guidance to the prescriber if the MME exceeds the recommended range.");
opioidGuidance.put("id", "cdc-opioid-guidance");
JSONObject prefetchContent = new JSONObject();
prefetchContent.put("medication", "MedicationOrder?patient={{Patient.id}}&status=active");
opioidGuidance.put("prefetch", prefetchContent);
jsonArray.add(opioidGuidance);
// JSONObject medicationPrescribe = new JSONObject();
// medicationPrescribe.put("hook", "medication-prescribe");
// medicationPrescribe.put("name", "User-defined medication-prescribe service");
// medicationPrescribe.put("description", "Enables user to define a CDS Hooks service using naming conventions");
// medicationPrescribe.put("id", "user-medication-prescribe");
//
// medicationPrescribe.put("prefetch", prefetchContent);
//
// jsonArray.add(medicationPrescribe);
//
JSONObject patientView = new JSONObject();
patientView.put("hook", "patient-view");
patientView.put("name", "Zika Virus Intervention");
patientView.put("description", "Identifies possible Zika exposure and offers suggestions for suggested actions for pregnant patients");
patientView.put("id", "zika-virus-intervention");
prefetchContent = new JSONObject();
prefetchContent.put("patient", "Patient/{{Patient.id}}");
patientView.put("prefetch", prefetchContent);
jsonArray.add(patientView);
jsonResponse.put("services", jsonArray);
response.getWriter().println(getPrettyJson(jsonResponse.toJSONString()));
}
public static String getPrettyJson(String uglyJson) {
Gson gson = new GsonBuilder().setPrettyPrinting().create();
JsonParser parser = new JsonParser();
JsonElement element = parser.parse(uglyJson);
return gson.toJson(element);
}
}

View File

@ -0,0 +1,158 @@
package ca.uhn.fhir.jpa.cqf.ruler.cds;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.dstu2.resource.Bundle;
import com.google.gson.*;
import ca.uhn.fhir.jpa.cqf.ruler.exceptions.MissingHookException;
import java.io.IOException;
import java.io.Reader;
public class CdsHooksRequest {
private JsonObject requestJson;
private String hook;
private String hookInstance;
private String fhirServerEndpoint;
// TODO
//private Object oauth;
private String redirectEndpoint;
private String userReference; // this is really a Reference (Resource/ID)
private String patientId;
private String encounterId;
private JsonArray context;
private JsonObject prefetch;
public CdsHooksRequest(Reader cdsHooksRequest) throws IOException {
JsonParser parser = new JsonParser();
this.requestJson = parser.parse(cdsHooksRequest).getAsJsonObject();
this.hook = requestJson.getAsJsonPrimitive("hook").getAsString();
if (this.hook == null || this.hook.isEmpty()) {
throw new MissingHookException("The CDS Service call must contain the hook that triggered its initiation.");
}
}
public String getHook() {
return this.hook;
}
public String getHookInstance() {
if (hookInstance == null) {
String temp = requestJson.getAsJsonPrimitive("hookInstance").getAsString();
this.hookInstance = temp == null ? "" : temp;
}
return hookInstance;
}
public String getFhirServerEndpoint() {
if (fhirServerEndpoint == null) {
String temp = requestJson.getAsJsonPrimitive("fhirServer").getAsString();
this.fhirServerEndpoint = temp == null || temp.isEmpty() ? "http://measure.eval.kanvix.com/cqf-ruler/baseDstu3" : temp;
}
return fhirServerEndpoint;
}
public String getRedirectEndpoint() {
if (redirectEndpoint == null) {
String temp = requestJson.getAsJsonPrimitive("redirect").getAsString();
this.redirectEndpoint = temp == null ? "" : temp;
}
return redirectEndpoint;
}
public String getUserReference() {
if (userReference == null) {
String temp = requestJson.getAsJsonPrimitive("user").getAsString();
this.userReference = temp == null ? "" : temp;
}
return userReference;
}
public String getPatientId() {
if (patientId == null) {
String temp = requestJson.getAsJsonPrimitive("patient").getAsString();
this.patientId = temp == null ? "" : temp;
}
return patientId;
}
public String getEncounterId() {
if (encounterId == null) {
String temp = requestJson.getAsJsonPrimitive("encounter").getAsString();
this.encounterId = temp == null ? "" : temp;
}
return encounterId;
}
public JsonArray getContext() {
if (context == null) {
JsonArray temp = requestJson.getAsJsonArray("context");
this.context = temp == null ? new JsonArray() : temp;
}
return context;
}
/*
Prefetch format:
"prefetch": {
"medication": { // for medication-prescribe and order-review
"response": {},
"resources": []
},
"diagnosticOrders": { // for order-review
"response": {},
"resources": []
},
"deviceUseRequests": { // for order-review
"response": {},
"resources": []
},
"procedureRequests": { // for order-review
"response": {},
"resources": []
},
"supplyRequests": { // for order-review
"response": {},
"resources": []
},
"patientToGreet": { // for patient-view
"response": {},
"resource": {}
}
}
*/
public JsonObject getPrefetch() {
if (prefetch == null) {
JsonObject temp = requestJson.getAsJsonObject("prefetch");
this.prefetch = temp == null ? new JsonObject() : temp;
}
return prefetch;
}
public void setPrefetch(JsonObject prefetch) {
this.prefetch = prefetch;
}
// Convenience method
// Populates resources array for sub-element of prefetch i.e. "supplyRequests" for order-review hook
// TODO - this won't do for patient-view
public void setPrefetch(Bundle prefetchBundle, String sub) {
JsonObject subJson = new JsonObject();
JsonArray resources = new JsonArray();
for (Bundle.Entry entry : prefetchBundle.getEntry()) {
JsonParser parser = new JsonParser();
JsonObject resource = parser.parse(FhirContext.forDstu2().newJsonParser().encodeResourceToString(entry.getResource())).getAsJsonObject();
resources.add(resource);
}
subJson.add("resources", resources);
this.prefetch.add(sub, subJson);
}
}

View File

@ -0,0 +1,122 @@
package ca.uhn.fhir.jpa.cqf.ruler.cds;
import ca.uhn.fhir.jpa.rp.dstu3.LibraryResourceProvider;
import ca.uhn.fhir.model.primitive.IdDt;
import org.hl7.fhir.dstu3.model.*;
import org.opencds.cqf.cql.data.fhir.BaseFhirDataProvider;
import org.opencds.cqf.cql.execution.Context;
import javax.xml.namespace.QName;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public abstract class CdsRequestProcessor implements Processor {
CdsHooksRequest request;
PlanDefinition planDefinition;
LibraryResourceProvider libraryResourceProvider;
CdsRequestProcessor(CdsHooksRequest request, PlanDefinition planDefinition, LibraryResourceProvider libraryResourceProvider) {
this.request = request;
this.planDefinition = planDefinition;
this.libraryResourceProvider = libraryResourceProvider;
}
List<CdsCard> resolveActions(Context executionContext) {
List<CdsCard> cards = new ArrayList<>();
walkAction(executionContext, cards, planDefinition.getAction());
return cards;
}
private void walkAction(Context executionContext, List<CdsCard> cards, List<PlanDefinition.PlanDefinitionActionComponent> actions) {
for (PlanDefinition.PlanDefinitionActionComponent action : actions) {
boolean conditionsMet = true;
for (PlanDefinition.PlanDefinitionActionConditionComponent condition: action.getCondition()) {
if (condition.getKind() == PlanDefinition.ActionConditionKind.APPLICABILITY) {
if (!condition.hasExpression()) {
continue;
}
Object result = executionContext.resolveExpressionRef(condition.getExpression()).getExpression().evaluate(executionContext);
if (!(result instanceof Boolean)) {
continue;
}
if (!(Boolean) result) {
conditionsMet = false;
}
}
}
if (conditionsMet) {
/*
Cases:
Definition element provides guidance for action
Nested actions
Standardized CQL (when first 2 aren't present)
*/
if (action.hasDefinition()) {
if (action.getDefinition().getReferenceElement().getResourceType().equals("ActivityDefinition")) {
BaseFhirDataProvider provider = (BaseFhirDataProvider) executionContext.resolveDataProvider(new QName("http://hl7.org/fhir", ""));
Parameters inParams = new Parameters();
inParams.addParameter().setName("patient").setValue(new StringType(request.getPatientId()));
Parameters outParams = provider.getFhirClient()
.operation()
.onInstance(new IdDt("ActivityDefinition", action.getDefinition().getReferenceElement().getIdPart()))
.named("$apply")
.withParameters(inParams)
.useHttpGet()
.execute();
List<Parameters.ParametersParameterComponent> response = outParams.getParameter();
Resource resource = response.get(0).getResource();
if (resource == null) {
continue;
}
// TODO - currently only have suggestions that create resources - implement delete and update.
CdsCard card = new CdsCard();
card.setIndicator("info");
CdsCard.Suggestions suggestion = new CdsCard.Suggestions();
suggestion.setActions(
Collections.singletonList(
new CdsCard.Suggestions.Action()
.setType(CdsCard.Suggestions.Action.ActionType.create)
.setResource(resource)
)
);
card.getSuggestions().add(suggestion);
cards.add(card);
}
else {
// PlanDefinition $apply
// TODO
// TODO - suggestion to create CarePlan
}
}
else if (action.hasAction()) {
walkAction(executionContext, cards, action.getAction());
}
// TODO - dynamicValues
else {
CdsCard card = new CdsCard();
card.setSummary((String) executionContext.resolveExpressionRef("getSummary").getExpression().evaluate(executionContext));
card.setDetail((String) executionContext.resolveExpressionRef("getDetail").getExpression().evaluate(executionContext));
card.setIndicator((String) executionContext.resolveExpressionRef("getIndicator").getExpression().evaluate(executionContext));
cards.add(card);
}
}
}
}
}

View File

@ -0,0 +1,121 @@
package ca.uhn.fhir.jpa.cqf.ruler.cds;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.rp.dstu3.LibraryResourceProvider;
import ca.uhn.fhir.model.dstu2.resource.Bundle;
import ca.uhn.fhir.model.dstu2.resource.MedicationOrder;
import org.cqframework.cql.cql2elm.LibraryManager;
import org.cqframework.cql.cql2elm.ModelManager;
import org.cqframework.cql.elm.execution.Library;
import org.hl7.fhir.dstu3.model.MedicationRequest;
import org.hl7.fhir.dstu3.model.PlanDefinition;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.jpa.cqf.ruler.config.STU3LibraryLoader;
import ca.uhn.fhir.jpa.cqf.ruler.config.STU3LibrarySourceProvider;
import org.opencds.cqf.cql.data.fhir.BaseFhirDataProvider;
import org.opencds.cqf.cql.data.fhir.FhirDataProviderStu3;
import org.opencds.cqf.cql.execution.Context;
import org.opencds.cqf.cql.execution.LibraryLoader;
import org.opencds.cqf.cql.terminology.fhir.FhirTerminologyProvider;
import ca.uhn.fhir.jpa.cqf.ruler.exceptions.MissingContextException;
import ca.uhn.fhir.jpa.cqf.ruler.helpers.Dstu2ToStu3;
import java.util.ArrayList;
import java.util.List;
public class MedicationPrescribeProcessor extends CdsRequestProcessor {
MedicationRequest contextPrescription;
List<MedicationRequest> activePrescriptions;
private ModelManager modelManager;
private ModelManager getModelManager() {
if (modelManager == null) {
modelManager = new ModelManager();
}
return modelManager;
}
private LibraryManager libraryManager;
private LibraryManager getLibraryManager() {
if (libraryManager == null) {
libraryManager = new LibraryManager(getModelManager());
libraryManager.getLibrarySourceLoader().clearProviders();
libraryManager.getLibrarySourceLoader().registerProvider(getLibrarySourceProvider());
}
return libraryManager;
}
private LibraryLoader libraryLoader;
private LibraryLoader getLibraryLoader() {
if (libraryLoader == null) {
libraryLoader = new STU3LibraryLoader(libraryResourceProvider, getLibraryManager(), getModelManager());
}
return libraryLoader;
}
private STU3LibrarySourceProvider librarySourceProvider;
private STU3LibrarySourceProvider getLibrarySourceProvider() {
if (librarySourceProvider == null) {
librarySourceProvider = new STU3LibrarySourceProvider(libraryResourceProvider);
}
return librarySourceProvider;
}
public MedicationPrescribeProcessor(CdsHooksRequest request, PlanDefinition planDefinition, LibraryResourceProvider libraryResourceProvider)
throws FHIRException
{
super(request, planDefinition, libraryResourceProvider);
this.activePrescriptions = new ArrayList<>();
resolveContextPrescription();
resolveActivePrescriptions();
}
@Override
public List<CdsCard> process() {
// TODO - need a better way to determine library id
Library library = getLibraryLoader().load(new org.cqframework.cql.elm.execution.VersionedIdentifier().withId("medication-prescribe"));
BaseFhirDataProvider dstu3Provider = new FhirDataProviderStu3().setEndpoint(request.getFhirServerEndpoint());
// TODO - assuming terminology service is same as data provider - not a great assumption...
dstu3Provider.setTerminologyProvider(new FhirTerminologyProvider().withEndpoint(request.getFhirServerEndpoint()));
dstu3Provider.setExpandValueSets(true);
Context executionContext = new Context(library);
executionContext.registerLibraryLoader(getLibraryLoader());
executionContext.registerDataProvider("http://hl7.org/fhir", dstu3Provider);
executionContext.setExpressionCaching(true);
executionContext.setParameter(null, "Orders", activePrescriptions);
return resolveActions(executionContext);
}
private void resolveContextPrescription() throws FHIRException {
if (request.getContext().size() == 0) {
throw new MissingContextException("The medication-prescribe request requires the context to contain a prescription order.");
}
String resourceName = request.getContext().get(0).getAsJsonObject().getAsJsonPrimitive("resourceType").getAsString();
this.contextPrescription = getMedicationRequest(resourceName, FhirContext.forDstu2().newJsonParser().parseResource(request.getContext().get(0).toString()));
}
private void resolveActivePrescriptions() throws FHIRException {
this.activePrescriptions.add(contextPrescription); // include the context prescription
String resourceName;
Bundle bundle = (Bundle) FhirContext.forDstu2().newJsonParser().parseResource(request.getPrefetch().getAsJsonObject("medication").getAsJsonObject("resource").toString());
for (Bundle.Entry entry : bundle.getEntry()) {
this.activePrescriptions.add(getMedicationRequest(entry.getResource().getResourceName(), entry.getResource()));
}
}
private MedicationRequest getMedicationRequest(String resourceName, IBaseResource resource) throws FHIRException {
if (resourceName.equals("MedicationOrder")) {
MedicationOrder order = (MedicationOrder) resource;
return Dstu2ToStu3.resolveMedicationRequest(order);
}
return (MedicationRequest) resource;
}
}

View File

@ -0,0 +1,115 @@
package ca.uhn.fhir.jpa.cqf.ruler.cds;
import ca.uhn.fhir.jpa.rp.dstu3.LibraryResourceProvider;
import org.cqframework.cql.cql2elm.LibraryManager;
import org.cqframework.cql.cql2elm.ModelInfoLoader;
import org.cqframework.cql.cql2elm.ModelInfoProvider;
import org.cqframework.cql.cql2elm.ModelManager;
import org.cqframework.cql.elm.execution.Library;
import org.hl7.elm.r1.VersionedIdentifier;
import org.hl7.elm_modelinfo.r1.ModelInfo;
import org.hl7.fhir.dstu3.model.PlanDefinition;
import org.hl7.fhir.exceptions.FHIRException;
import ca.uhn.fhir.jpa.cqf.ruler.config.STU3LibraryLoader;
import ca.uhn.fhir.jpa.cqf.ruler.config.STU3LibrarySourceProvider;
import org.opencds.cqf.cql.data.fhir.BaseFhirDataProvider;
import org.opencds.cqf.cql.data.fhir.FhirDataProviderStu3;
import org.opencds.cqf.cql.execution.Context;
import org.opencds.cqf.cql.execution.LibraryLoader;
import ca.uhn.fhir.jpa.cqf.ruler.omtk.OmtkDataProvider;
import javax.xml.bind.JAXB;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.List;
public class OpioidGuidanceProcessor extends MedicationPrescribeProcessor {
private ModelManager modelManager;
private ModelManager getModelManager() {
if (modelManager == null) {
modelManager = new ModelManager();
ModelInfoProvider infoProvider = () -> {
Path p = Paths.get("src/main/resources/cds/OMTK-modelinfo-0.1.0.xml").toAbsolutePath();
return JAXB.unmarshal(new File(p.toString()), ModelInfo.class);
};
ModelInfoLoader.registerModelInfoProvider(new VersionedIdentifier().withId("OMTK").withVersion("0.1.0"), infoProvider);
}
return modelManager;
}
private LibraryManager libraryManager;
private LibraryManager getLibraryManager() {
if (libraryManager == null) {
libraryManager = new LibraryManager(getModelManager());
libraryManager.getLibrarySourceLoader().clearProviders();
libraryManager.getLibrarySourceLoader().registerProvider(getLibrarySourceProvider());
}
return libraryManager;
}
private LibraryLoader libraryLoader;
private LibraryLoader getLibraryLoader() {
if (libraryLoader == null) {
libraryLoader = new STU3LibraryLoader(libraryResourceProvider, getLibraryManager(), getModelManager());
}
return libraryLoader;
}
private STU3LibrarySourceProvider librarySourceProvider;
private STU3LibrarySourceProvider getLibrarySourceProvider() {
if (librarySourceProvider == null) {
librarySourceProvider = new STU3LibrarySourceProvider(libraryResourceProvider);
}
return librarySourceProvider;
}
public OpioidGuidanceProcessor(CdsHooksRequest request, PlanDefinition planDefinition, LibraryResourceProvider libraryResourceProvider)
throws FHIRException
{
super(request, planDefinition, libraryResourceProvider);
}
@Override
public List<CdsCard> process() {
// read opioid library
Library library = getLibraryLoader().load(new org.cqframework.cql.elm.execution.VersionedIdentifier().withId("OpioidCdsStu3").withVersion("0.1.0"));
// resolve data providers
// the db file is issued to properly licensed implementers -- see README for more info
String path = Paths.get("src/main/resources/cds/OpioidManagementTerminologyKnowledge.db").toAbsolutePath().toString().replace("\\", "/");
String connString = "jdbc:sqlite://" + path;
OmtkDataProvider omtkProvider = new OmtkDataProvider(connString);
BaseFhirDataProvider dstu3Provider = new FhirDataProviderStu3().setEndpoint(request.getFhirServerEndpoint());
Context executionContext = new Context(library);
executionContext.registerLibraryLoader(getLibraryLoader());
executionContext.registerDataProvider("http://org.opencds/opioid-cds", omtkProvider);
executionContext.registerDataProvider("http://hl7.org/fhir", dstu3Provider);
executionContext.setExpressionCaching(true);
// executionContext.setEnableTraceLogging(true);
executionContext.setParameter(null, "Orders", activePrescriptions);
List<CdsCard> cards = resolveActions(executionContext);
if (cards.isEmpty()) {
cards.add(
new CdsCard()
.setSummary("Success")
.setDetail("Prescription satisfies recommendation #5 of the cdc opioid guidance.")
.setIndicator("info")
.setLinks(
Collections.singletonList(
new CdsCard.Links()
.setLabel("CDC Recommendations for prescribing opioids")
.setUrl("https://guidelines.gov/summaries/summary/50153/cdc-guideline-for-prescribing-opioids-for-chronic-pain---united-states-2016#420")
)
)
);
}
return cards;
}
}

View File

@ -0,0 +1,93 @@
package ca.uhn.fhir.jpa.cqf.ruler.cds;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.rp.dstu3.LibraryResourceProvider;
import org.cqframework.cql.cql2elm.LibraryManager;
import org.cqframework.cql.cql2elm.ModelManager;
import org.cqframework.cql.elm.execution.Library;
import org.hl7.fhir.dstu3.model.PlanDefinition;
import org.hl7.fhir.dstu3.model.ProcedureRequest;
import ca.uhn.fhir.jpa.cqf.ruler.config.STU3LibraryLoader;
import ca.uhn.fhir.jpa.cqf.ruler.config.STU3LibrarySourceProvider;
import org.opencds.cqf.cql.data.fhir.BaseFhirDataProvider;
import org.opencds.cqf.cql.data.fhir.FhirDataProviderStu3;
import org.opencds.cqf.cql.execution.Context;
import org.opencds.cqf.cql.execution.LibraryLoader;
import org.opencds.cqf.cql.terminology.fhir.FhirTerminologyProvider;
import ca.uhn.fhir.jpa.cqf.ruler.exceptions.MissingContextException;
import java.util.List;
public class OrderReviewProcessor extends CdsRequestProcessor {
private ProcedureRequest contextOrder;
private ModelManager modelManager;
private ModelManager getModelManager() {
if (modelManager == null) {
modelManager = new ModelManager();
}
return modelManager;
}
private LibraryManager libraryManager;
private LibraryManager getLibraryManager() {
if (libraryManager == null) {
libraryManager = new LibraryManager(getModelManager());
libraryManager.getLibrarySourceLoader().clearProviders();
libraryManager.getLibrarySourceLoader().registerProvider(getLibrarySourceProvider());
}
return libraryManager;
}
private LibraryLoader libraryLoader;
private LibraryLoader getLibraryLoader() {
if (libraryLoader == null) {
libraryLoader = new STU3LibraryLoader(libraryResourceProvider, getLibraryManager(), getModelManager());
}
return libraryLoader;
}
private STU3LibrarySourceProvider librarySourceProvider;
private STU3LibrarySourceProvider getLibrarySourceProvider() {
if (librarySourceProvider == null) {
librarySourceProvider = new STU3LibrarySourceProvider(libraryResourceProvider);
}
return librarySourceProvider;
}
public OrderReviewProcessor(CdsHooksRequest request, PlanDefinition planDefinition, LibraryResourceProvider libraryResourceProvider) {
super(request, planDefinition, libraryResourceProvider);
resolveOrder();
}
@Override
public List<CdsCard> process() {
// TODO - need a better way to determine library id
Library library = getLibraryLoader().load(new org.cqframework.cql.elm.execution.VersionedIdentifier().withId("OrderReview"));
BaseFhirDataProvider dstu3Provider = new FhirDataProviderStu3().setEndpoint(request.getFhirServerEndpoint());
// TODO - assuming terminology service is same as data provider - not a great assumption...
dstu3Provider.setTerminologyProvider(new FhirTerminologyProvider().withEndpoint(request.getFhirServerEndpoint()));
dstu3Provider.setExpandValueSets(true);
Context executionContext = new Context(library);
executionContext.registerLibraryLoader(getLibraryLoader());
executionContext.registerDataProvider("http://hl7.org/fhir", dstu3Provider);
executionContext.registerTerminologyProvider(dstu3Provider.getTerminologyProvider());
executionContext.setContextValue("Patient", request.getPatientId());
executionContext.setParameter(null, "Order", contextOrder);
executionContext.setExpressionCaching(true);
return resolveActions(executionContext);
}
private void resolveOrder() {
if (request.getContext().size() == 0) {
throw new MissingContextException("The order-review request requires the context to contain an order.");
}
// Assuming STU3 here as per the example here: http://cds-hooks.org/#radiology-appropriateness
this.contextOrder = (ProcedureRequest) FhirContext.forDstu3().newJsonParser().parseResource(request.getContext().get(0).toString());
}
}

View File

@ -0,0 +1,77 @@
package ca.uhn.fhir.jpa.cqf.ruler.cds;
import ca.uhn.fhir.jpa.rp.dstu3.LibraryResourceProvider;
import org.cqframework.cql.cql2elm.LibraryManager;
import org.cqframework.cql.cql2elm.ModelManager;
import org.cqframework.cql.elm.execution.Library;
import org.hl7.fhir.dstu3.model.PlanDefinition;
import ca.uhn.fhir.jpa.cqf.ruler.config.STU3LibraryLoader;
import ca.uhn.fhir.jpa.cqf.ruler.config.STU3LibrarySourceProvider;
import org.opencds.cqf.cql.data.fhir.BaseFhirDataProvider;
import org.opencds.cqf.cql.data.fhir.FhirDataProviderStu3;
import org.opencds.cqf.cql.execution.Context;
import org.opencds.cqf.cql.execution.LibraryLoader;
import org.opencds.cqf.cql.terminology.fhir.FhirTerminologyProvider;
import java.util.List;
public class PatientViewProcessor extends CdsRequestProcessor {
private ModelManager modelManager;
private ModelManager getModelManager() {
if (modelManager == null) {
modelManager = new ModelManager();
}
return modelManager;
}
private LibraryManager libraryManager;
private LibraryManager getLibraryManager() {
if (libraryManager == null) {
libraryManager = new LibraryManager(getModelManager());
libraryManager.getLibrarySourceLoader().clearProviders();
libraryManager.getLibrarySourceLoader().registerProvider(getLibrarySourceProvider());
}
return libraryManager;
}
private LibraryLoader libraryLoader;
private LibraryLoader getLibraryLoader() {
if (libraryLoader == null) {
libraryLoader = new STU3LibraryLoader(libraryResourceProvider, getLibraryManager(), getModelManager());
}
return libraryLoader;
}
private STU3LibrarySourceProvider librarySourceProvider;
private STU3LibrarySourceProvider getLibrarySourceProvider() {
if (librarySourceProvider == null) {
librarySourceProvider = new STU3LibrarySourceProvider(libraryResourceProvider);
}
return librarySourceProvider;
}
public PatientViewProcessor(CdsHooksRequest request, PlanDefinition planDefinition, LibraryResourceProvider libraryResourceProvider) {
super(request, planDefinition, libraryResourceProvider);
}
@Override
public List<CdsCard> process() {
// TODO - need a better way to determine library id
Library library = getLibraryLoader().load(new org.cqframework.cql.elm.execution.VersionedIdentifier().withId("patient-view"));
BaseFhirDataProvider dstu3Provider = new FhirDataProviderStu3().setEndpoint(request.getFhirServerEndpoint());
// TODO - assuming terminology service is same as data provider - not a great assumption...
dstu3Provider.setTerminologyProvider(new FhirTerminologyProvider().withEndpoint(request.getFhirServerEndpoint()));
dstu3Provider.setExpandValueSets(true);
Context executionContext = new Context(library);
executionContext.registerLibraryLoader(getLibraryLoader());
executionContext.registerDataProvider("http://hl7.org/fhir", dstu3Provider);
executionContext.registerTerminologyProvider(dstu3Provider.getTerminologyProvider());
executionContext.setContextValue("Patient", request.getPatientId());
executionContext.setExpressionCaching(true);
return resolveActions(executionContext);
}
}

View File

@ -0,0 +1,7 @@
package ca.uhn.fhir.jpa.cqf.ruler.cds;
import java.util.List;
public interface Processor {
List<CdsCard> process();
}

View File

@ -0,0 +1,137 @@
package ca.uhn.fhir.jpa.cqf.ruler.config;
import ca.uhn.fhir.jpa.config.BaseJavaConfigDstu3;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.util.SubscriptionsRequireManualActivationInterceptorDstu3;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.rest.server.interceptor.LoggingInterceptor;
import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor;
import org.apache.commons.dbcp2.BasicDataSource;
import org.hibernate.jpa.HibernatePersistenceProvider;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import java.util.Properties;
/**
* Created by Chris Schuler on 12/11/2016.
*/
@Configuration
@EnableTransactionManagement()
public class FhirServerConfigDstu3 extends BaseJavaConfigDstu3 {
@Bean()
public DaoConfig daoConfig() {
DaoConfig retVal = new DaoConfig();
// retVal.setSubscriptionEnabled(true);
// retVal.setSubscriptionPollDelay(5000);
// retVal.setSubscriptionPurgeInactiveAfterMillis(DateUtils.MILLIS_PER_HOUR);
retVal.setAllowMultipleDelete(true);
return retVal;
}
// PostgreSQL config
// @Bean(destroyMethod = "close")
// public DataSource dataSource() {
// BasicDataSource retVal = new BasicDataSource();
// retVal.setDriver(new org.postgresql.Driver());
// retVal.setUrl("jdbc:postgresql://localhost:5432/fhir");
// retVal.setUsername("hapi");
// retVal.setPassword("hapi");
// return retVal;
// }
// Derby config
@Bean(destroyMethod = "close")
public DataSource dataSource() {
BasicDataSource retVal = new BasicDataSource();
retVal.setDriver(new org.apache.derby.jdbc.EmbeddedDriver());
retVal.setUrl("jdbc:derby:directory:target/jpaserver_derby_files;create=true");
retVal.setUsername("");
retVal.setPassword("");
return retVal;
}
@Bean()
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean retVal = new LocalContainerEntityManagerFactoryBean();
retVal.setPersistenceUnitName("HAPI_PU");
retVal.setDataSource(dataSource());
retVal.setPackagesToScan("ca.uhn.fhir.jpa.entity");
retVal.setPersistenceProvider(new HibernatePersistenceProvider());
retVal.setJpaProperties(jpaProperties());
return retVal;
}
// PostgreSQL config
// private Properties jpaProperties() {
// Properties extraProperties = new Properties();
// extraProperties.put("hibernate.dialect", org.hibernate.dialect.PostgreSQL94Dialect.class.getName());
// extraProperties.put("hibernate.format_sql", "true");
// extraProperties.put("hibernate.show_sql", "false");
// extraProperties.put("hibernate.hbm2ddl.auto", "update");
// extraProperties.put("hibernate.jdbc.batch_size", "20");
// extraProperties.put("hibernate.cache.use_query_cache", "false");
// extraProperties.put("hibernate.cache.use_second_level_cache", "false");
// extraProperties.put("hibernate.cache.use_structured_entries", "false");
// extraProperties.put("hibernate.cache.use_minimal_puts", "false");
// extraProperties.put("hibernate.search.default.directory_provider", "filesystem");
// extraProperties.put("hibernate.search.default.indexBase", "target/lucenefiles");
// extraProperties.put("hibernate.search.lucene_version", "LUCENE_CURRENT");
//// extraProperties.put("hibernate.search.default.worker.execution", "async");
// return extraProperties;
// }
// Derby config
private Properties jpaProperties() {
Properties extraProperties = new Properties();
extraProperties.put("hibernate.dialect", org.hibernate.dialect.DerbyTenSevenDialect.class.getName());
extraProperties.put("hibernate.format_sql", "true");
extraProperties.put("hibernate.show_sql", "false");
extraProperties.put("hibernate.hbm2ddl.auto", "update");
extraProperties.put("hibernate.jdbc.batch_size", "20");
extraProperties.put("hibernate.cache.use_query_cache", "false");
extraProperties.put("hibernate.cache.use_second_level_cache", "false");
extraProperties.put("hibernate.cache.use_structured_entries", "false");
extraProperties.put("hibernate.cache.use_minimal_puts", "false");
extraProperties.put("hibernate.search.default.directory_provider", "filesystem");
extraProperties.put("hibernate.search.default.indexBase", "target/lucenefiles");
extraProperties.put("hibernate.search.lucene_version", "LUCENE_CURRENT");
// extraProperties.put("hibernate.search.default.worker.execution", "async");
return extraProperties;
}
public IServerInterceptor loggingInterceptor() {
LoggingInterceptor retVal = new LoggingInterceptor();
retVal.setLoggerName("fhirtest.access");
retVal.setMessageFormat(
"Path[${servletPath}] Source[${requestHeader.x-forwarded-for}] Operation[${operationType} ${operationName} ${idOrResourceName}] UA[${requestHeader.user-agent}] Params[${requestParameters}] ResponseEncoding[${responseEncodingNoDefault}]");
retVal.setLogExceptions(true);
retVal.setErrorMessageFormat("ERROR - ${requestVerb} ${requestUrl}");
return retVal;
}
@Bean(autowire = Autowire.BY_TYPE)
public IServerInterceptor responseHighlighterInterceptor() {
return new ResponseHighlighterInterceptor();
}
@Bean(autowire = Autowire.BY_TYPE)
public IServerInterceptor subscriptionSecurityInterceptor() {
return new SubscriptionsRequireManualActivationInterceptorDstu3();
}
@Bean()
public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager retVal = new JpaTransactionManager();
retVal.setEntityManagerFactory(entityManagerFactory);
return retVal;
}
}

View File

@ -0,0 +1,33 @@
package ca.uhn.fhir.jpa.cqf.ruler.config;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.to.FhirTesterMvcConfig;
import ca.uhn.fhir.to.TesterConfig;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* Created by Chris Schuler on 12/11/2016.
*/
@Configuration
@Import(FhirTesterMvcConfig.class)
public class FhirTesterConfigDstu3 {
@Bean
public TesterConfig testerConfig() {
TesterConfig retVal = new TesterConfig();
retVal
.addServer()
.withId("home")
.withFhirVersion(FhirVersionEnum.DSTU3)
.withBaseUrl("${serverBase}/baseDstu3")
.withName("Local Tester")
.addServer()
.withId("hapi")
.withFhirVersion(FhirVersionEnum.DSTU3)
.withBaseUrl("http://fhirtest.uhn.ca/baseDstu3")
.withName("Public HAPI Test Server");
return retVal;
}
}

View File

@ -0,0 +1,108 @@
package ca.uhn.fhir.jpa.cqf.ruler.config;
import ca.uhn.fhir.jpa.rp.dstu3.LibraryResourceProvider;
import org.cqframework.cql.cql2elm.CqlTranslator;
import org.cqframework.cql.cql2elm.CqlTranslatorException;
import org.cqframework.cql.cql2elm.LibraryManager;
import org.cqframework.cql.cql2elm.ModelManager;
import org.cqframework.cql.elm.execution.Library;
import org.cqframework.cql.elm.execution.VersionedIdentifier;
import org.hl7.fhir.dstu3.model.IdType;
import org.opencds.cqf.cql.execution.LibraryLoader;
import javax.xml.bind.JAXBException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import static ca.uhn.fhir.jpa.cqf.ruler.helpers.LibraryHelper.errorsToString;
import static ca.uhn.fhir.jpa.cqf.ruler.helpers.LibraryHelper.readLibrary;
import static ca.uhn.fhir.jpa.cqf.ruler.helpers.LibraryHelper.translateLibrary;
/**
* Created by Christopher on 1/11/2017.
*/
public class STU3LibraryLoader implements LibraryLoader {
private LibraryManager libraryManager;
private ModelManager modelManager;
private LibraryResourceProvider provider;
private Map<String, Library> libraries = new HashMap<>();
public Map<String, Library> getLibraries() {
return this.libraries;
}
public STU3LibraryLoader(LibraryResourceProvider provider, LibraryManager libraryManager, ModelManager modelManager) {
this.libraryManager = libraryManager;
this.modelManager = modelManager;
this.provider = provider;
}
private Library resolveLibrary(VersionedIdentifier libraryIdentifier) {
if (libraryIdentifier == null) {
throw new IllegalArgumentException("Library identifier is null.");
}
if (libraryIdentifier.getId() == null) {
throw new IllegalArgumentException("Library identifier id is null.");
}
Library library = libraries.get(libraryIdentifier.getId());
if (library != null && libraryIdentifier.getVersion() != null
&& !libraryIdentifier.getVersion().equals(library.getIdentifier().getVersion())) {
throw new IllegalArgumentException(String.format("Could not load library %s, version %s because version %s is already loaded.",
libraryIdentifier.getId(), libraryIdentifier.getVersion(), library.getIdentifier().getVersion()));
}
else {
library = loadLibrary(libraryIdentifier);
libraries.put(libraryIdentifier.getId(), library);
}
return library;
}
private Library loadLibrary(VersionedIdentifier libraryIdentifier) {
IdType id = new IdType(libraryIdentifier.getId());
org.hl7.fhir.dstu3.model.Library library = provider.getDao().read(id);
InputStream is = null;
for (org.hl7.fhir.dstu3.model.Attachment content : library.getContent()) {
is = new ByteArrayInputStream(content.getData());
if (content.getContentType().equals("application/elm+xml")) {
return readLibrary(is);
}
else if (content.getContentType().equals("text/cql")) {
return translateLibrary(is, libraryManager, modelManager);
}
}
org.hl7.elm.r1.VersionedIdentifier identifier = new org.hl7.elm.r1.VersionedIdentifier()
.withId(libraryIdentifier.getId())
.withSystem(libraryIdentifier.getSystem())
.withVersion(libraryIdentifier.getVersion());
ArrayList<CqlTranslatorException> errors = new ArrayList<>();
org.hl7.elm.r1.Library translatedLibrary = libraryManager.resolveLibrary(identifier, errors).getLibrary();
if (errors.size() > 0) {
throw new IllegalArgumentException(errorsToString(errors));
}
try {
return readLibrary(new ByteArrayInputStream(CqlTranslator.fromStream(is == null ? new ByteArrayInputStream(new byte[]{}) : is, modelManager, libraryManager).convertToXml(translatedLibrary).getBytes(StandardCharsets.UTF_8)));
} catch (JAXBException | IOException e) {
throw new IllegalArgumentException(String.format("Errors occurred translating library %s%s.",
identifier.getId(), identifier.getVersion() != null ? ("-" + identifier.getVersion()) : ""));
}
}
@Override
public Library load(VersionedIdentifier versionedIdentifier) {
return resolveLibrary(versionedIdentifier);
}
}

View File

@ -0,0 +1,35 @@
package ca.uhn.fhir.jpa.cqf.ruler.config;
import ca.uhn.fhir.jpa.rp.dstu3.LibraryResourceProvider;
import org.cqframework.cql.cql2elm.LibrarySourceProvider;
import org.hl7.elm.r1.VersionedIdentifier;
import org.hl7.fhir.dstu3.model.IdType;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
/**
* Created by Christopher on 1/12/2017.
*/
public class STU3LibrarySourceProvider implements LibrarySourceProvider {
private LibraryResourceProvider provider;
public STU3LibrarySourceProvider(LibraryResourceProvider provider) {
this.provider = provider;
}
@Override
public InputStream getLibrarySource(VersionedIdentifier versionedIdentifier) {
IdType id = new IdType(versionedIdentifier.getId());
org.hl7.fhir.dstu3.model.Library lib = provider.getDao().read(id);
for (org.hl7.fhir.dstu3.model.Attachment content : lib.getContent()) {
if (content.getContentType().equals("text/cql")) {
return new ByteArrayInputStream(content.getData());
}
}
throw new IllegalArgumentException(String.format("Library %s%s does not contain CQL source content.", versionedIdentifier.getId(),
versionedIdentifier.getVersion() != null ? ("-" + versionedIdentifier.getVersion()) : ""));
}
}

View File

@ -0,0 +1,8 @@
package ca.uhn.fhir.jpa.cqf.ruler.exceptions;
public class ActivityDefinitionApplyException extends RuntimeException {
public ActivityDefinitionApplyException(String message) {
super(message);
}
}

View File

@ -0,0 +1,9 @@
package ca.uhn.fhir.jpa.cqf.ruler.exceptions;
public class InvalidHookException extends RuntimeException {
public InvalidHookException(String message) {
super(message);
}
}

View File

@ -0,0 +1,9 @@
package ca.uhn.fhir.jpa.cqf.ruler.exceptions;
public class MissingContextException extends RuntimeException {
public MissingContextException(String message) {
super(message);
}
}

View File

@ -0,0 +1,9 @@
package ca.uhn.fhir.jpa.cqf.ruler.exceptions;
public class MissingHookException extends RuntimeException {
public MissingHookException(String message) {
super(message);
}
}

View File

@ -0,0 +1,46 @@
package ca.uhn.fhir.jpa.cqf.ruler.helpers;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
public class DateHelper {
// Helper class to resolve period dates
public static Date resolveRequestDate(String date, boolean start) {
// split it up - support dashes or slashes
String[] dissect = date.contains("-") ? date.split("-") : date.split("/");
List<Integer> dateVals = new ArrayList<>();
for (String dateElement : dissect) {
dateVals.add(Integer.parseInt(dateElement));
}
if (dateVals.isEmpty()) {
throw new IllegalArgumentException("Invalid date");
}
// for now support dates up to day precision
Calendar calendar = Calendar.getInstance();
calendar.clear();
calendar.set(Calendar.YEAR, dateVals.get(0));
if (dateVals.size() > 1) {
// java.util.Date months are zero based, hence the negative 1 -- 2014-01 == February 2014
calendar.set(Calendar.MONTH, dateVals.get(1) - 1);
}
if (dateVals.size() > 2)
calendar.set(Calendar.DAY_OF_MONTH, dateVals.get(2));
else {
if (start) {
calendar.set(Calendar.DAY_OF_MONTH, 1);
}
else {
// get last day of month for end period
calendar.add(Calendar.MONTH, 1);
calendar.set(Calendar.DAY_OF_MONTH, 1);
calendar.add(Calendar.DATE, -1);
}
}
return calendar.getTime();
}
}

View File

@ -0,0 +1,84 @@
package ca.uhn.fhir.jpa.cqf.ruler.helpers;
import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt;
import ca.uhn.fhir.model.dstu2.composite.CodingDt;
import ca.uhn.fhir.model.dstu2.composite.SimpleQuantityDt;
import ca.uhn.fhir.model.dstu2.resource.MedicationOrder;
import ca.uhn.fhir.model.primitive.BooleanDt;
import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.exceptions.FHIRException;
import java.util.ArrayList;
import java.util.List;
public class Dstu2ToStu3 {
// MedicationRequest
public static MedicationRequest resolveMedicationRequest(MedicationOrder order) throws FHIRException {
/*
* Required fields:
* MedicationOrder -> MedicationRequest
* medication -> medication
* dosageInstruction (Backbone) -> Dosage (Element)
*/
return new MedicationRequest()
.setStatus(MedicationRequest.MedicationRequestStatus.fromCode(order.getStatus()))
.setMedication(convertToCodeableConcept((CodeableConceptDt) order.getMedication()))
.setDosageInstruction(convertToDosage(order.getDosageInstruction()));
}
private static CodeableConcept convertToCodeableConcept(CodeableConceptDt conceptDt) {
CodeableConcept concept = new CodeableConcept().setText(conceptDt.getText() == null ? "" : conceptDt.getText());
concept.setId(conceptDt.getElementSpecificId());
List<Coding> codes = new ArrayList<>();
for (CodingDt code : conceptDt.getCoding()) {
codes.add(new Coding()
.setCode(code.getCode())
.setSystem(code.getSystem())
.setDisplay(code.getDisplay())
.setVersion(code.getVersion())
);
}
return concept.setCoding(codes);
}
private static List<Dosage> convertToDosage(List<MedicationOrder.DosageInstruction> instructions) throws FHIRException {
List<Dosage> dosages = new ArrayList<>();
for (MedicationOrder.DosageInstruction dosageInstruction : instructions) {
Dosage dosage = new Dosage();
dosage.setText(dosageInstruction.getText());
dosage.setAsNeeded(dosageInstruction.getAsNeeded() == null ? new BooleanType(true) : new BooleanType(((BooleanDt) dosageInstruction.getAsNeeded()).getValue()));
Integer frequency = dosageInstruction.getTiming().getRepeat().getFrequency();
Integer frequencyMax = dosageInstruction.getTiming().getRepeat().getFrequencyMax();
Timing.TimingRepeatComponent repeat = new Timing.TimingRepeatComponent();
if (frequency != null) {
repeat.setFrequency(frequency);
}
if (frequencyMax != null) {
repeat.setFrequencyMax(frequencyMax);
}
repeat.setPeriod(dosageInstruction.getTiming().getRepeat().getPeriod())
.setPeriodUnit(Timing.UnitsOfTime.fromCode(dosageInstruction.getTiming().getRepeat().getPeriodUnits()));
Timing timing = new Timing();
timing.setRepeat(repeat);
dosage.setTiming(timing);
SimpleQuantityDt quantityDt = (SimpleQuantityDt) dosageInstruction.getDose();
dosage.setDose(new SimpleQuantity()
.setValue(quantityDt.getValue())
.setUnit(quantityDt.getUnit())
.setCode(quantityDt.getCode())
.setSystem(quantityDt.getSystem())
);
dosages.add(dosage);
}
return dosages;
}
}

View File

@ -0,0 +1,40 @@
package ca.uhn.fhir.jpa.cqf.ruler.helpers;
import org.opencds.cqf.cql.execution.Context;
import org.hl7.fhir.dstu3.model.Bundle;
import org.hl7.fhir.dstu3.model.Resource;
/**
* Created by Bryn on 5/7/2016.
*/
public class FhirMeasureBundler {
// Adds the resources returned from the given expressions to a bundle
public Bundle bundle(Context context, String... expressionNames) {
Bundle bundle = new Bundle();
bundle.setType(Bundle.BundleType.COLLECTION);
for (String expressionName : expressionNames) {
Object result = context.resolveExpressionRef(expressionName).evaluate(context);
for (Object element : (Iterable)result) {
Bundle.BundleEntryComponent entry = new Bundle.BundleEntryComponent();
entry.setResource((Resource)element);
entry.setFullUrl(((Resource)element).getId());
bundle.getEntry().add(entry);
}
}
return bundle;
}
public Bundle bundle(Iterable<Resource> resources) {
Bundle bundle = new Bundle();
bundle.setType(Bundle.BundleType.COLLECTION);
for (Resource resource : resources) {
Bundle.BundleEntryComponent entry = new Bundle.BundleEntryComponent();
entry.setResource(resource);
entry.setFullUrl(resource.getId());
bundle.getEntry().add(entry);
}
return bundle;
}
}

View File

@ -0,0 +1,177 @@
package ca.uhn.fhir.jpa.cqf.ruler.helpers;
import org.hl7.fhir.dstu3.model.*;
import org.opencds.cqf.cql.execution.Context;
import org.opencds.cqf.cql.runtime.DateTime;
import org.opencds.cqf.cql.runtime.Interval;
import java.util.*;
/**
* Created by Bryn on 5/7/2016.
*/
public class FhirMeasureEvaluator {
private MeasureReport resolveGroupings(MeasureReport report, Measure measure, Context context)
{
HashMap<String,Resource> resources = new HashMap<>();
// for each measure group
for (Measure.MeasureGroupComponent group : measure.getGroup()) {
MeasureReport.MeasureReportGroupComponent reportGroup = new MeasureReport.MeasureReportGroupComponent();
// TODO: Do I need to do this copy? Will HAPI FHIR do this automatically?
reportGroup.setIdentifier(group.getIdentifier().copy());
report.getGroup().add(reportGroup);
for (Measure.MeasureGroupPopulationComponent population : group.getPopulation()) {
// evaluate the criteria expression, should return true/false, translate to 0/1 for report
Object result = context.resolveExpressionRef(population.getCriteria()).evaluate(context);
int count = 0;
if (result instanceof Boolean) {
count = (Boolean)result ? 1 : 0;
}
else if (result instanceof Iterable) {
for (Object item : (Iterable)result) {
count++;
if (item instanceof Resource) {
resources.put(((Resource)item).getId(), (Resource)item);
}
}
}
MeasureReport.MeasureReportGroupPopulationComponent populationReport = new MeasureReport.MeasureReportGroupPopulationComponent();
populationReport.setCount(count);
populationReport.setCode(population.getCode());
reportGroup.getPopulation().add(populationReport);
}
}
ArrayList<String> expressionNames = new ArrayList<>();
// HACK: Hijacking Supplemental data to specify the evaluated resources
// In reality, this should be specified explicitly, but I'm not sure what else to do here....
for (Measure.MeasureSupplementalDataComponent supplementalData : measure.getSupplementalData()) {
expressionNames.add(supplementalData.getCriteria());
}
// TODO: Need to return both the MeasureReport and the EvaluatedResources Bundle
FhirMeasureBundler bundler = new FhirMeasureBundler();
//String[] expressionNameArray = new String[expressionNames.size()];
//expressionNameArray = expressionNames.toArray(expressionNameArray);
//org.hl7.fhir.dstu3.model.Bundle evaluatedResources = bundler.bundle(context, expressionNameArray);
org.hl7.fhir.dstu3.model.Bundle evaluatedResources = bundler.bundle(resources.values());
evaluatedResources.setId(UUID.randomUUID().toString());
//String jsonString = fhirClient.getFhirContext().newJsonParser().encodeResourceToString(evaluatedResources);
//ca.uhn.fhir.rest.api.MethodOutcome result = fhirClient.create().resource(evaluatedResources).execute();
report.setEvaluatedResources(new Reference('#' + evaluatedResources.getId()));
report.addContained(evaluatedResources);
return report;
}
// Patient Evaluation
public MeasureReport evaluate(Context context, Measure measure, Patient patient, Date periodStart, Date periodEnd) {
MeasureReport report = new MeasureReport();
report.setMeasure(new Reference(measure));
report.setPatient(new Reference(patient));
Period reportPeriod = new Period();
reportPeriod.setStart(periodStart);
reportPeriod.setEnd(periodEnd);
report.setPeriod(reportPeriod);
report.setType(MeasureReport.MeasureReportType.INDIVIDUAL);
Interval measurementPeriod = new Interval(DateTime.fromJavaDate(periodStart), true, DateTime.fromJavaDate(periodEnd), true);
context.setParameter(null, "Measurement Period", measurementPeriod);
return resolveGroupings(report, measure, context);
}
public MeasureReport evaluate(Context context, Measure measure, Patient patient, Interval measurementPeriod) {
return evaluate(context, measure, patient, (Date)measurementPeriod.getStart(), (Date)measurementPeriod.getEnd());
}
// Population evaluation
// public MeasureReport evaluate(Context context, Measure measure, List<Patient> patients,
// Interval measurementPeriod, MeasureReport.MeasureReportType type)
// {
// MeasureReport report = new MeasureReport();
// report.setMeasure(new Reference(measure));
// Period reportPeriod = new Period();
// reportPeriod.setStart((Date) measurementPeriod.getStart());
// reportPeriod.setEnd((Date) measurementPeriod.getEnd());
// report.setPeriod(reportPeriod);
// report.setType(type);
//
// return resolveGroupings(report, measure, context, patients);
// }
public MeasureReport evaluate(Context context, Measure measure, List<Patient> population,
Interval measurementPeriod, MeasureReport.MeasureReportType type)
{
MeasureReport report = new MeasureReport();
report.setMeasure(new Reference(measure));
Period reportPeriod = new Period();
reportPeriod.setStart((Date) measurementPeriod.getStart());
reportPeriod.setEnd((Date) measurementPeriod.getEnd());
report.setPeriod(reportPeriod);
report.setType(type);
context.setParameter(null, "Measurement Period", measurementPeriod);
HashMap<String,Resource> resources = new HashMap<>();
// for each measure group
for (Measure.MeasureGroupComponent group : measure.getGroup()) {
MeasureReport.MeasureReportGroupComponent reportGroup = new MeasureReport.MeasureReportGroupComponent();
reportGroup.setIdentifier(group.getIdentifier());
report.getGroup().add(reportGroup);
for (Measure.MeasureGroupPopulationComponent pop : group.getPopulation()) {
int count = 0;
// Worried about performance here with big populations...
for (Patient patient : population) {
context.setContextValue("Patient", patient);
Object result = context.resolveExpressionRef(pop.getCriteria()).evaluate(context);
if (result instanceof Boolean) {
count += (Boolean) result ? 1 : 0;
}
else if (result instanceof Iterable) {
for (Object item : (Iterable) result) {
count++;
if (item instanceof Resource) {
resources.put(((Resource) item).getId(), (Resource) item);
}
}
}
}
MeasureReport.MeasureReportGroupPopulationComponent populationReport = new MeasureReport.MeasureReportGroupPopulationComponent();
populationReport.setCount(count);
populationReport.setCode(pop.getCode());
/*
TODO - it is a reference to a list...
Probably want to create the list and POST it, then include a reference to it.
*/
// if (patients != null) {
// ListResource list = new ListResource();
// populationReport.setPatients();
// }
reportGroup.getPopulation().add(populationReport);
}
}
ArrayList<String> expressionNames = new ArrayList<>();
// HACK: Hijacking Supplemental data to specify the evaluated resources
// In reality, this should be specified explicitly, but I'm not sure what else to do here....
for (Measure.MeasureSupplementalDataComponent supplementalData : measure.getSupplementalData()) {
expressionNames.add(supplementalData.getCriteria());
}
FhirMeasureBundler bundler = new FhirMeasureBundler();
org.hl7.fhir.dstu3.model.Bundle evaluatedResources = bundler.bundle(resources.values());
evaluatedResources.setId(UUID.randomUUID().toString());
report.setEvaluatedResources(new Reference('#' + evaluatedResources.getId()));
report.addContained(evaluatedResources);
return report;
}
}

View File

@ -0,0 +1,81 @@
package ca.uhn.fhir.jpa.cqf.ruler.helpers;
import org.cqframework.cql.cql2elm.CqlTranslator;
import org.cqframework.cql.cql2elm.CqlTranslatorException;
import org.cqframework.cql.cql2elm.LibraryManager;
import org.cqframework.cql.cql2elm.ModelManager;
import org.cqframework.cql.elm.execution.Library;
import org.cqframework.cql.elm.tracking.TrackBack;
import org.opencds.cqf.cql.execution.CqlLibraryReader;
import javax.xml.bind.JAXBException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
/**
* Created by Christopher on 1/11/2017.
*/
public class LibraryHelper {
public static Library readLibrary(InputStream xmlStream) {
try {
return CqlLibraryReader.read(xmlStream);
} catch (IOException | JAXBException e) {
throw new IllegalArgumentException("Error encountered while reading ELM xml: " + e.getMessage());
}
}
public static String errorsToString(Iterable<CqlTranslatorException> exceptions) {
ArrayList<String> errors = new ArrayList<>();
for (CqlTranslatorException error : exceptions) {
TrackBack tb = error.getLocator();
String lines = tb == null ? "[n/a]" : String.format("%s[%d:%d, %d:%d]",
(tb.getLibrary() != null ? tb.getLibrary().getId() + (tb.getLibrary().getVersion() != null
? ("-" + tb.getLibrary().getVersion()) : "") : ""),
tb.getStartLine(), tb.getStartChar(), tb.getEndLine(), tb.getEndChar());
errors.add(lines + error.getMessage());
}
return errors.toString();
}
public static CqlTranslator getTranslator(String cql, LibraryManager libraryManager, ModelManager modelManager) {
return getTranslator(new ByteArrayInputStream(cql.getBytes(StandardCharsets.UTF_8)), libraryManager, modelManager);
}
public static CqlTranslator getTranslator(InputStream cqlStream, LibraryManager libraryManager, ModelManager modelManager) {
ArrayList<CqlTranslator.Options> options = new ArrayList<>();
options.add(CqlTranslator.Options.EnableDateRangeOptimization);
options.add(CqlTranslator.Options.EnableAnnotations);
options.add(CqlTranslator.Options.EnableDetailedErrors);
CqlTranslator translator;
try {
translator = CqlTranslator.fromStream(cqlStream, modelManager, libraryManager,
options.toArray(new CqlTranslator.Options[options.size()]));
} catch (IOException e) {
throw new IllegalArgumentException(String.format("Errors occurred translating library: %s", e.getMessage()));
}
if (translator.getErrors().size() > 0) {
throw new IllegalArgumentException(errorsToString(translator.getErrors()));
}
return translator;
}
public static Library translateLibrary(String cql, LibraryManager libraryManager, ModelManager modelManager) {
return translateLibrary(new ByteArrayInputStream(cql.getBytes(StandardCharsets.UTF_8)), libraryManager, modelManager);
}
public static Library translateLibrary(InputStream cqlStream, LibraryManager libraryManager, ModelManager modelManager) {
CqlTranslator translator = getTranslator(cqlStream, libraryManager, modelManager);
return readLibrary(new ByteArrayInputStream(translator.toXml().getBytes(StandardCharsets.UTF_8)));
}
public static Library translateLibrary(CqlTranslator translator) {
return readLibrary(new ByteArrayInputStream(translator.toXml().getBytes(StandardCharsets.UTF_8)));
}
}

View File

@ -0,0 +1,211 @@
package ca.uhn.fhir.jpa.cqf.ruler.helpers;
import ca.uhn.fhir.context.FhirContext;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.hl7.fhir.dstu3.model.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class XlsxToValueSet {
// Default indexes and output directory path
private static boolean valueSet = true;
private static int startLine = 0;
private static int oidCol = 0;
private static int systemCol = 1;
private static int versionCol = 2;
private static int codeCol = 3;
private static int displayCol = 4;
private static int urlCol = 1;
private static String outDir = "src/main/resources/valuesets/";
private static final String validFlags = "-b (line to start conversion)\n-o (oid column index)\n-s (code system column index)\n-v (version column index)\n-c (code column index)\n-d (display column index)\n-u (url column index) only used for CodeSystem conversion";
private static Map<String, ValueSet> valuesets = new HashMap<>();
private static Map<String, CodeSystem> codeSystems = new HashMap<>();
private static void populateOidVs(String oid) {
ValueSet vs = new ValueSet();
vs.setId(oid);
vs.setStatus(Enumerations.PublicationStatus.DRAFT);
valuesets.put(oid, vs);
}
private static void populateOidCs(String oid) {
CodeSystem cs = new CodeSystem();
cs.setId(oid);
cs.setStatus(Enumerations.PublicationStatus.DRAFT);
cs.setContent(CodeSystem.CodeSystemContentMode.EXAMPLE);
codeSystems.put(oid, cs);
}
private static String getNextCellString(Cell cell) {
cell.setCellType(CellType.STRING);
return cell.getStringCellValue();
}
// default format: oid ... system ... version ... code ... display
private static void resolveRowVs(Row row) {
String oid = getNextCellString(row.getCell(oidCol));
if (!valuesets.containsKey(oid)) {
populateOidVs(oid);
}
String system = getNextCellString(row.getCell(systemCol));
String version = getNextCellString(row.getCell(versionCol));
String code = getNextCellString(row.getCell(codeCol));
String display = getNextCellString(row.getCell(displayCol));
ValueSet.ValueSetComposeComponent vscc = valuesets.get(oid).getCompose();
ValueSet.ConceptSetComponent component = new ValueSet.ConceptSetComponent();
component.setSystem(system).setVersion(version);
component.addConcept(new ValueSet.ConceptReferenceComponent().setCode(code).setDisplay(display));
vscc.addInclude(component);
}
// default format: oid ... url ... version ... code ... display
private static void resolveRowCs(Row row) {
String oid = getNextCellString(row.getCell(oidCol));
if (!codeSystems.containsKey(oid)) {
populateOidCs(oid);
}
String url = getNextCellString(row.getCell(urlCol));
String version = getNextCellString(row.getCell(versionCol));
String code = getNextCellString(row.getCell(codeCol));
String display = getNextCellString(row.getCell(displayCol));
CodeSystem cs = codeSystems.get(oid);
cs.setUrl(url).setVersion(version);
cs.getConcept().add(new CodeSystem.ConceptDefinitionComponent().setCode(code).setDisplay(display));
}
private static Bundle valuesetBundle() {
Bundle temp = new Bundle();
for (String key : valuesets.keySet())
temp.addEntry(new Bundle.BundleEntryComponent().setResource(valuesets.get(key)));
return temp;
}
private static Bundle codesystemBundle() {
Bundle temp = new Bundle();
for (String key : codeSystems.keySet())
temp.addEntry(new Bundle.BundleEntryComponent().setResource(codeSystems.get(key)));
return temp;
}
// library function use
public static Bundle convertCs(Workbook workbook, String[] args) {
resolveArgs(args);
Iterator<Row> rowIterator = workbook.getSheetAt(0).iterator();
int currentRow = 0;
while (currentRow++ != startLine) {
rowIterator.next();
}
while (rowIterator.hasNext()) {
resolveRowCs(rowIterator.next());
}
return codesystemBundle();
}
public static Bundle convertCs(String[] args) {
return convertCs(getWorkbook(args[0]), args);
}
public static Bundle convertVs(Workbook workbook, String[] args) {
resolveArgs(args);
Iterator<Row> rowIterator = workbook.getSheetAt(0).iterator();
int currentRow = 0;
while (currentRow++ != startLine) {
rowIterator.next();
}
while (rowIterator.hasNext()) {
resolveRowVs(rowIterator.next());
}
return valuesetBundle();
}
public static Bundle convertVs(String[] args) {
return convertVs(getWorkbook(args[0]), args);
}
public static Workbook getWorkbook(String workbookPath) {
Workbook workbook = null;
try {
FileInputStream spreadsheetStream = new FileInputStream(new File(workbookPath));
workbook = new XSSFWorkbook(spreadsheetStream);
} catch (IOException e) {
e.printStackTrace();
}
return workbook;
}
private static void resolveArgs(String[] args) {
for (String arg : args) {
String[] flagAndValue = arg.split("=");
switch (flagAndValue[0]) {
case "-b": startLine = Integer.parseInt(flagAndValue[1]); break;
case "-o": oidCol = Integer.parseInt(flagAndValue[1]); break;
case "-s": systemCol = Integer.parseInt(flagAndValue[1]); break;
case "-v": versionCol = Integer.parseInt(flagAndValue[1]); break;
case "-c": codeCol = Integer.parseInt(flagAndValue[1]); break;
case "-d": displayCol = Integer.parseInt(flagAndValue[1]); break;
case "-u": urlCol = Integer.parseInt(flagAndValue[1]); break;
case "-outDir": outDir = flagAndValue[1]; break;
case "-cs": valueSet = false; break;
default:
if (flagAndValue[0].startsWith("-")) {
throw new IllegalArgumentException(String.format("Invalid flag: %s\n%s", flagAndValue[0], validFlags));
}
break;
}
}
}
public static void main(String[] args) throws IOException {
if (args.length < 1) {
System.err.println("Path to excel file is required");
return;
}
if (args.length > 1) {
resolveArgs(args);
}
Bundle temp = valueSet ? convertVs(args) : convertCs(args);
FhirContext context = FhirContext.forDstu3();
for (Bundle.BundleEntryComponent component : temp.getEntry()) {
File f = new File(outDir + component.getResource().getId() + ".json");
if (f.createNewFile()) {
PrintWriter writer = new PrintWriter(f);
writer.println(context.newJsonParser().setPrettyPrint(true).encodeResourceToString(component.getResource()));
writer.println();
writer.close();
}
}
}
}

View File

@ -0,0 +1,157 @@
package ca.uhn.fhir.jpa.cqf.ruler.omtk;
import org.opencds.cqf.cql.data.DataProvider;
import org.opencds.cqf.cql.runtime.Code;
import org.opencds.cqf.cql.runtime.Interval;
import java.math.BigDecimal;
import java.sql.DriverManager;
import java.sql.SQLException;
/**
* Created by Bryn on 4/24/2017.
*/
public class OmtkDataProvider implements DataProvider {
public static final String RXNORM = "http://www.nlm.nih.gov/research/umls/rxnorm";
public OmtkDataProvider(String connectionString) {
if (connectionString == null) {
throw new IllegalArgumentException("connectionString is null");
}
this.connectionString = connectionString;
}
private String connectionString;
private java.sql.Connection connection;
private java.sql.Connection getConnection() {
if (connection == null) {
connection = getNewConnection();
}
try {
if (!connection.isValid(0)) {
connection = getNewConnection();
}
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
return connection;
}
private java.sql.Connection getNewConnection() {
try {
return DriverManager.getConnection(connectionString);
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
@Override
public Iterable<Object> retrieve(String context, Object contextValue, String dataType, String templateId,
String codePath, Iterable<Code> codes, String valueSet, String datePath,
String dateLowPath, String dateHighPath, Interval dateRange) {
java.sql.Statement statement = null;
try {
statement = getConnection().createStatement();
// TODO: Construct the SELECT statement based on the code path
// TODO: Throw an error if an attempt is made to limit based on date range
StringBuilder select = new StringBuilder();
select.append(String.format("SELECT * FROM %s", dataType));
if (codePath != null) {
StringBuilder codeList = new StringBuilder();
boolean plural = false;
for (Code code : codes) {
if (codeList.length() > 0) {
codeList.append(", ");
plural = true;
}
codeList.append(code.getCode()); // TODO: Need to handle the case when code is a string type...
}
if (plural) {
select.append(String.format(" WHERE %s IN ( %s )", codePath, codeList.toString()));
}
else {
select.append(String.format(" WHERE %S = %s", codePath, codeList.toString()));
}
}
if (datePath != null) {
throw new UnsupportedOperationException("OmtkDataProvider does not support filtering by date range.");
}
java.sql.ResultSet rs = statement.executeQuery(select.toString());
return new OmtkDataWrapper(rs);
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
@Override
public String getPackageName() {
return "ca.uhn.fhir.jpaca.uhn.fhir.jpa.cqf.ruler.omtk";
}
@Override
public void setPackageName(String s) {
}
@Override
public Object resolvePath(Object target, String path) {
if (target == null) {
return null;
}
if (target instanceof OmtkRow) {
OmtkRow row = (OmtkRow)target;
return mapType(row.getValue(path), path);
}
throw new UnsupportedOperationException(String.format("Could not retrieve value of property %s from object of type %s.",
path, target.getClass().getName()));
}
@Override
public Class resolveType(String typeName) {
throw new UnsupportedOperationException("OmtkProvider does not support write.");
}
@Override
public Class resolveType(Object o) {
throw new UnsupportedOperationException("OmtkProvider does not support write.");
}
@Override
public Object createInstance(String s) {
throw new UnsupportedOperationException("OmtkProvider does not support write.");
}
@Override
public void setValue(Object target, String path, Object value) {
throw new UnsupportedOperationException("OmtkProvider does not support write.");
}
private Object mapType(Object type, String path) {
// not all integers are codes
if (path.equals("STRENGTH_VALUE")) {
return new BigDecimal(type.toString());
}
if (type instanceof Double) {
return new BigDecimal((Double) type);
}
else if (type instanceof Integer) {
return new Code().withCode(type.toString()).withSystem("http://www.nlm.nih.gov/research/umls/rxnorm");
}
return type;
}
}

View File

@ -0,0 +1,124 @@
package ca.uhn.fhir.jpa.cqf.ruler.omtk;
import org.opencds.cqf.cql.runtime.Code;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* Created by Bryn on 4/24/2017.
*/
public class OmtkDataWrapper implements Iterable<Object> {
// TODO: Lifecycle management.... assuming GC for now
private ResultSet rs;
public OmtkDataWrapper(ResultSet rs) {
if (rs == null) {
throw new IllegalArgumentException("rs is null");
}
this.rs = rs;
}
/**
* Returns an iterator over elements of type {@code T}.
*
* @return an Iterator.
*/
@Override
public Iterator<Object> iterator() {
return new OmtkDataWrapperIterator();
}
private class OmtkDataWrapperIterator implements Iterator<Object> {
public OmtkDataWrapperIterator() {
try {
hasNext = rs.next();
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
private boolean hasNext = false;
/**
* Returns {@code true} if the iteration has more elements.
* (In other words, returns {@code true} if {@link #next} would
* return an element rather than throwing an exception.)
*
* @return {@code true} if the iteration has more elements
*/
@Override
public boolean hasNext() {
return hasNext;
}
/**
* Returns the next element in the iteration.
*
* @return the next element in the iteration
* @throws NoSuchElementException if the iteration has no more elements
*/
@Override
public Object next() {
if (!hasNext) {
throw new NoSuchElementException();
}
Object result = getRowData();
try {
hasNext = rs.next();
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
return result;
}
/**
* Removes from the underlying collection the last element returned
* by this iterator (optional operation). This method can be called
* only once per call to {@link #next}. The behavior of an iterator
* is unspecified if the underlying collection is modified while the
* iteration is in progress in any way other than by calling this
* method.
*
* @throws UnsupportedOperationException if the {@code remove}
* operation is not supported by this iterator
* @throws IllegalStateException if the {@code next} method has not
* yet been called, or the {@code remove} method has already
* been called after the last call to the {@code next}
* method
* @implSpec The default implementation throws an instance of
* {@link UnsupportedOperationException} and performs no other action.
*/
@Override
public void remove() {
throw new UnsupportedOperationException();
}
private Object getRowData() {
try {
java.sql.ResultSetMetaData metadata = rs.getMetaData();
OmtkRow row = new OmtkRow();
for (int i = 1; i <= metadata.getColumnCount(); i++) {
if (metadata.getColumnName(i).endsWith("_RXCUI")) {
row.setValue(metadata.getColumnName(i), new Code().withCode(((Integer)rs.getInt(i)).toString())
.withSystem(OmtkDataProvider.RXNORM));
}
row.setValue(metadata.getColumnName(i), rs.getObject(i));
}
return row;
}
catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
}
}

View File

@ -0,0 +1,21 @@
package ca.uhn.fhir.jpa.cqf.ruler.omtk;
import java.util.HashMap;
import java.util.Map;
/**
* Created by Bryn on 4/24/2017.
*/
public class OmtkRow {
private Map<String, Object> data = new HashMap<String, Object>();
public Object getValue(String key) {
return data.get(key);
}
public void setValue(String key, Object value) {
data.put(key, value);
}
}

View File

@ -0,0 +1,148 @@
package ca.uhn.fhir.jpa.cqf.ruler.providers;
import ca.uhn.fhir.jpa.rp.dstu3.LibraryResourceProvider;
import ca.uhn.fhir.rest.server.IResourceProvider;
import org.cqframework.cql.cql2elm.LibraryManager;
import org.cqframework.cql.cql2elm.ModelManager;
import org.hl7.fhir.dstu3.model.*;
import ca.uhn.fhir.jpa.cqf.ruler.config.STU3LibraryLoader;
import ca.uhn.fhir.jpa.cqf.ruler.config.STU3LibrarySourceProvider;
import org.opencds.cqf.cql.data.fhir.FhirDataProviderStu3;
import org.opencds.cqf.cql.execution.Context;
import org.opencds.cqf.cql.execution.LibraryLoader;
import ca.uhn.fhir.jpa.cqf.ruler.helpers.LibraryHelper;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* Created by Bryn on 1/16/2017.
*/
public class CqlExecutionProvider {
private JpaDataProvider provider;
public CqlExecutionProvider(Collection<IResourceProvider> providers) {
this.provider = new JpaDataProvider(providers);
}
private ModelManager modelManager;
private ModelManager getModelManager() {
if (modelManager == null) {
modelManager = new ModelManager();
}
return modelManager;
}
private LibraryManager libraryManager;
private LibraryManager getLibraryManager() {
if (libraryManager == null) {
libraryManager = new LibraryManager(getModelManager());
libraryManager.getLibrarySourceLoader().clearProviders();
libraryManager.getLibrarySourceLoader().registerProvider(getLibrarySourceProvider());
}
return libraryManager;
}
private LibraryLoader libraryLoader;
private LibraryLoader getLibraryLoader() {
if (libraryLoader == null) {
libraryLoader = new STU3LibraryLoader(getLibraryResourceProvider(), getLibraryManager(), getModelManager());
}
return libraryLoader;
}
private STU3LibrarySourceProvider librarySourceProvider;
private STU3LibrarySourceProvider getLibrarySourceProvider() {
if (librarySourceProvider == null) {
librarySourceProvider = new STU3LibrarySourceProvider(getLibraryResourceProvider());
}
return librarySourceProvider;
}
private LibraryResourceProvider getLibraryResourceProvider() {
return (LibraryResourceProvider)provider.resolveResourceProvider("Library");
}
public static Iterable<Reference> getLibraryReferences(DomainResource instance) {
List<Reference> references = new ArrayList<>();
if (instance instanceof ActivityDefinition) {
references.addAll(((ActivityDefinition)instance).getLibrary());
}
else if (instance instanceof PlanDefinition) {
references.addAll(((PlanDefinition)instance).getLibrary());
}
else if (instance instanceof Measure) {
references.addAll(((Measure)instance).getLibrary());
}
for (Extension extension : instance.getExtensionsByUrl("http://hl7.org/fhir/StructureDefinition/cqif-library"))
{
Type value = extension.getValue();
if (value instanceof Reference) {
references.add((Reference)value);
}
else {
throw new RuntimeException("Library extension does not have a value of type reference");
}
}
return references;
}
private String buildIncludes(Iterable<Reference> references) {
StringBuilder builder = new StringBuilder();
for (Reference reference : references) {
if (builder.length() > 0) {
builder.append(" ");
}
// TODO: Would be nice not to have to resolve the reference here and just be able to specify the include...
Library library = getLibraryResourceProvider().getDao().read(new IdType(reference.getReference()));
builder.append("include ");
// TODO: This assumes the libraries resource id is the same as the library name, need to work this out better
builder.append(library.getIdElement().getIdPart());
if (library.getVersion() != null) {
builder.append(" version '");
builder.append(library.getVersion());
builder.append("'");
}
builder.append(" called ");
builder.append(library.getIdElement().getIdPart());
}
return builder.toString();
}
/* Evaluates the given CQL expression in the context of the given resource */
/* If the resource has a library extension, or a library element, that library is loaded into the context for the expression */
public Object evaluateInContext(DomainResource instance, String cql, String patientId) {
Iterable<Reference> libraries = getLibraryReferences(instance);
// Provide the instance as the value of the '%context' parameter, as well as the value of a parameter named the same as the resource
// This enables expressions to access the resource by root, as well as through the %context attribute
String source = String.format("library LocalLibrary using FHIR version '3.0.0' include FHIRHelpers version '3.0.0' called FHIRHelpers %s parameter %s %s parameter \"%%context\" %s define Expression: %s",
buildIncludes(libraries), instance.fhirType(), instance.fhirType(), instance.fhirType(), cql);
// String source = String.format("library LocalLibrary using FHIR version '1.8' include FHIRHelpers version '1.8' called FHIRHelpers %s parameter %s %s parameter \"%%context\" %s define Expression: %s",
// buildIncludes(libraries), instance.fhirType(), instance.fhirType(), instance.fhirType(), cql);
org.cqframework.cql.elm.execution.Library library = LibraryHelper.translateLibrary(source, getLibraryManager(), getModelManager());
Context context = new Context(library);
context.setParameter(null, instance.fhirType(), instance);
context.setParameter(null, "%context", instance);
context.setExpressionCaching(true);
context.registerLibraryLoader(getLibraryLoader());
context.setContextValue("Patient", patientId);
context.registerDataProvider("http://hl7.org/fhir", new FhirDataProviderStu3().setEndpoint("http://localhost:8080/cqf-ruler/baseDstu3"));
return context.resolveExpressionRef("Expression").evaluate(context);
}
}

View File

@ -0,0 +1,462 @@
package ca.uhn.fhir.jpa.cqf.ruler.providers;
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
import ca.uhn.fhir.jpa.provider.dstu3.JpaResourceProviderDstu3;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.rest.annotation.*;
import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.param.*;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.exceptions.FHIRException;
import ca.uhn.fhir.jpa.cqf.ruler.exceptions.ActivityDefinitionApplyException;
import java.util.*;
/**
* Created by Bryn on 1/16/2017.
*/
public class FHIRActivityDefinitionResourceProvider extends JpaResourceProviderDstu3<ActivityDefinition> {
private JpaDataProvider provider;
private CqlExecutionProvider executionProvider;
public FHIRActivityDefinitionResourceProvider(Collection<IResourceProvider> providers) {
this.provider = new JpaDataProvider(providers);
this.executionProvider = new CqlExecutionProvider(providers);
}
@Operation(name = "$apply", idempotent = true)
public Resource apply(@IdParam IdType theId, @RequiredParam(name="patient") String patientId,
@OptionalParam(name="encounter") String encounterId,
@OptionalParam(name="practitioner") String practitionerId,
@OptionalParam(name="organization") String organizationId,
@OptionalParam(name="userType") String userType,
@OptionalParam(name="userLanguage") String userLanguage,
@OptionalParam(name="userTaskContext") String userTaskContext,
@OptionalParam(name="setting") String setting,
@OptionalParam(name="settingContext") String settingContext)
throws InternalErrorException, FHIRException, ClassNotFoundException, IllegalAccessException,
InstantiationException, ActivityDefinitionApplyException
{
ActivityDefinition activityDefinition = this.getDao().read(theId);
Resource result = null;
try {
// This is a little hacky...
result = (Resource) Class.forName("org.hl7.fhir.dstu3.model." + activityDefinition.getKind().toCode()).newInstance();
}
catch (Exception e) {
e.printStackTrace();
throw new FHIRException("Could not find org.hl7.fhir.dstu3.model." + activityDefinition.getKind().toCode());
}
switch (result.fhirType()) {
case "ProcedureRequest":
result = resolveProcedureRequest(activityDefinition, patientId, practitionerId, organizationId);
break;
case "MedicationRequest":
result = resolveMedicationRequest(activityDefinition, patientId);
break;
case "SupplyRequest":
result = resolveSupplyRequest(activityDefinition, practitionerId, organizationId);
break;
case "Procedure":
result = resolveProcedure(activityDefinition, patientId);
break;
case "DiagnosticReport":
result = resolveDiagnosticReport(activityDefinition, patientId);
break;
case "Communication":
result = resolveCommunication(activityDefinition, patientId);
break;
}
// TODO: Apply expression extensions on any element?
for (ActivityDefinition.ActivityDefinitionDynamicValueComponent dynamicValue : activityDefinition.getDynamicValue())
{
if (dynamicValue.getExpression() != null) {
/*
TODO: Passing the activityDefinition as context here because that's what will have the libraries,
but perhaps the "context" here should be the result resource?
*/
Object value =
executionProvider.evaluateInContext(activityDefinition, dynamicValue.getExpression(), patientId);
// TODO need to verify type... yay
if (value instanceof Boolean) {
value = new BooleanType((Boolean) value);
}
this.provider.setValue(result, dynamicValue.getPath(), value);
}
}
return result;
}
private ProcedureRequest resolveProcedureRequest(ActivityDefinition activityDefinition, String patientId,
String practitionerId, String organizationId)
throws ActivityDefinitionApplyException
{
// status, intent, code, and subject are required
ProcedureRequest procedureRequest = new ProcedureRequest();
procedureRequest.setStatus(ProcedureRequest.ProcedureRequestStatus.DRAFT);
procedureRequest.setIntent(ProcedureRequest.ProcedureRequestIntent.ORDER);
procedureRequest.setSubject(new Reference(patientId));
if (practitionerId != null) {
procedureRequest.setRequester(
new ProcedureRequest.ProcedureRequestRequesterComponent()
.setAgent(new Reference(practitionerId))
);
}
else if (organizationId != null) {
procedureRequest.setRequester(
new ProcedureRequest.ProcedureRequestRequesterComponent()
.setAgent(new Reference(organizationId))
);
}
if (activityDefinition.hasExtension()) {
procedureRequest.setExtension(activityDefinition.getExtension());
}
if (activityDefinition.hasCode()) {
procedureRequest.setCode(activityDefinition.getCode());
}
// code can be set as a dynamicValue
else if (!activityDefinition.hasCode() && !activityDefinition.hasDynamicValue()) {
throw new ActivityDefinitionApplyException("Missing required code property");
}
if (activityDefinition.hasBodySite()) {
procedureRequest.setBodySite( activityDefinition.getBodySite());
}
if (activityDefinition.hasProduct()) {
throw new ActivityDefinitionApplyException("Product does not map to "+activityDefinition.getKind());
}
if (activityDefinition.hasDosage()) {
throw new ActivityDefinitionApplyException("Dosage does not map to "+activityDefinition.getKind());
}
return procedureRequest;
}
private MedicationRequest resolveMedicationRequest(ActivityDefinition activityDefinition, String patientId)
throws ActivityDefinitionApplyException
{
// intent, medication, and subject are required
MedicationRequest medicationRequest = new MedicationRequest();
medicationRequest.setIntent(MedicationRequest.MedicationRequestIntent.ORDER);
medicationRequest.setSubject(new Reference(patientId));
if (activityDefinition.hasProduct()) {
medicationRequest.setMedication( activityDefinition.getProduct());
}
else {
throw new ActivityDefinitionApplyException("Missing required product property");
}
if (activityDefinition.hasDosage()) {
medicationRequest.setDosageInstruction( activityDefinition.getDosage());
}
if (activityDefinition.hasBodySite()) {
throw new ActivityDefinitionApplyException("Bodysite does not map to " + activityDefinition.getKind());
}
if (activityDefinition.hasCode()) {
throw new ActivityDefinitionApplyException("Code does not map to " + activityDefinition.getKind());
}
if (activityDefinition.hasQuantity()) {
throw new ActivityDefinitionApplyException("Quantity does not map to " + activityDefinition.getKind());
}
return medicationRequest;
}
private SupplyRequest resolveSupplyRequest(ActivityDefinition activityDefinition, String practionerId,
String organizationId) throws ActivityDefinitionApplyException
{
SupplyRequest supplyRequest = new SupplyRequest();
if (practionerId != null) {
supplyRequest.setRequester(
new SupplyRequest.SupplyRequestRequesterComponent()
.setAgent(new Reference(practionerId))
);
}
if (organizationId != null) {
supplyRequest.setRequester(
new SupplyRequest.SupplyRequestRequesterComponent()
.setAgent(new Reference(organizationId))
);
}
if (activityDefinition.hasQuantity()){
supplyRequest.setOrderedItem(
new SupplyRequest.SupplyRequestOrderedItemComponent()
.setQuantity( activityDefinition.getQuantity())
);
}
else {
throw new ActivityDefinitionApplyException("Missing required orderedItem.quantity property");
}
if (activityDefinition.hasCode()) {
supplyRequest.getOrderedItem().setItem(activityDefinition.getCode());
}
if (activityDefinition.hasProduct()) {
throw new ActivityDefinitionApplyException("Product does not map to "+activityDefinition.getKind());
}
if (activityDefinition.hasDosage()) {
throw new ActivityDefinitionApplyException("Dosage does not map to "+activityDefinition.getKind());
}
if (activityDefinition.hasBodySite()) {
throw new ActivityDefinitionApplyException("Bodysite does not map to "+activityDefinition.getKind());
}
return supplyRequest;
}
private Procedure resolveProcedure(ActivityDefinition activityDefinition, String patientId) {
Procedure procedure = new Procedure();
// TODO - set the appropriate status
procedure.setStatus(Procedure.ProcedureStatus.UNKNOWN);
procedure.setSubject(new Reference(patientId));
if (activityDefinition.hasCode()) {
procedure.setCode(activityDefinition.getCode());
}
if (activityDefinition.hasBodySite()) {
procedure.setBodySite(activityDefinition.getBodySite());
}
return procedure;
}
private DiagnosticReport resolveDiagnosticReport(ActivityDefinition activityDefinition, String patientId) {
DiagnosticReport diagnosticReport = new DiagnosticReport();
diagnosticReport.setStatus(DiagnosticReport.DiagnosticReportStatus.UNKNOWN);
diagnosticReport.setSubject(new Reference(patientId));
if (activityDefinition.hasCode()) {
diagnosticReport.setCode(activityDefinition.getCode());
}
else {
throw new ActivityDefinitionApplyException("Missing required ActivityDefinition.code property for DiagnosticReport");
}
if (activityDefinition.hasRelatedArtifact()) {
List<Attachment> presentedFormAttachments = new ArrayList<>();
for (RelatedArtifact artifact : activityDefinition.getRelatedArtifact()) {
Attachment attachment = new Attachment();
if (artifact.hasUrl()) {
attachment.setUrl(artifact.getUrl());
}
if (artifact.hasDisplay()) {
attachment.setTitle(artifact.getDisplay());
}
presentedFormAttachments.add(attachment);
}
diagnosticReport.setPresentedForm(presentedFormAttachments);
}
return diagnosticReport;
}
private Communication resolveCommunication(ActivityDefinition activityDefinition, String patientId) {
Communication communication = new Communication();
communication.setStatus(Communication.CommunicationStatus.UNKNOWN);
communication.setSubject(new Reference(patientId));
if (activityDefinition.hasCode()) {
communication.setReasonCode(Collections.singletonList(activityDefinition.getCode()));
}
if (activityDefinition.hasRelatedArtifact()) {
for (RelatedArtifact artifact : activityDefinition.getRelatedArtifact()) {
if (artifact.hasUrl()) {
Attachment attachment = new Attachment().setUrl(artifact.getUrl());
if (artifact.hasDisplay()) {
attachment.setTitle(artifact.getDisplay());
}
Communication.CommunicationPayloadComponent payload = new Communication.CommunicationPayloadComponent();
payload.setContent(artifact.hasDisplay() ? attachment.setTitle(artifact.getDisplay()) : attachment);
communication.setPayload(Collections.singletonList(payload));
}
// TODO - other relatedArtifact types
}
}
return communication;
}
@Search(allowUnknownParams=true)
public IBundleProvider search(
javax.servlet.http.HttpServletRequest theServletRequest,
RequestDetails theRequestDetails,
@Description(shortDefinition="Search the contents of the resource's data using a fulltext search")
@OptionalParam(name=ca.uhn.fhir.rest.api.Constants.PARAM_CONTENT)
StringAndListParam theFtContent,
@Description(shortDefinition="Search the contents of the resource's narrative using a fulltext search")
@OptionalParam(name=ca.uhn.fhir.rest.api.Constants.PARAM_TEXT)
StringAndListParam theFtText,
@Description(shortDefinition="Search for resources which have the given tag")
@OptionalParam(name=ca.uhn.fhir.rest.api.Constants.PARAM_TAG)
TokenAndListParam theSearchForTag,
@Description(shortDefinition="Search for resources which have the given security labels")
@OptionalParam(name=ca.uhn.fhir.rest.api.Constants.PARAM_SECURITY)
TokenAndListParam theSearchForSecurity,
@Description(shortDefinition="Search for resources which have the given profile")
@OptionalParam(name=ca.uhn.fhir.rest.api.Constants.PARAM_PROFILE)
UriAndListParam theSearchForProfile,
@Description(shortDefinition="Return resources linked to by the given target")
@OptionalParam(name="_has")
HasAndListParam theHas,
@Description(shortDefinition="The ID of the resource")
@OptionalParam(name="_id")
TokenAndListParam the_id,
@Description(shortDefinition="The language of the resource")
@OptionalParam(name="_language")
StringAndListParam the_language,
@Description(shortDefinition="What resource is being referenced")
@OptionalParam(name="composed-of", targetTypes={ } )
ReferenceAndListParam theComposed_of,
@Description(shortDefinition="The activity definition publication date")
@OptionalParam(name="date")
DateRangeParam theDate,
@Description(shortDefinition="What resource is being referenced")
@OptionalParam(name="depends-on", targetTypes={ } )
ReferenceAndListParam theDepends_on,
@Description(shortDefinition="What resource is being referenced")
@OptionalParam(name="derived-from", targetTypes={ } )
ReferenceAndListParam theDerived_from,
@Description(shortDefinition="The description of the activity definition")
@OptionalParam(name="description")
StringAndListParam theDescription,
@Description(shortDefinition="The time during which the activity definition is intended to be in use")
@OptionalParam(name="effective")
DateRangeParam theEffective,
@Description(shortDefinition="External identifier for the activity definition")
@OptionalParam(name="identifier")
TokenAndListParam theIdentifier,
@Description(shortDefinition="Intended jurisdiction for the activity definition")
@OptionalParam(name="jurisdiction")
TokenAndListParam theJurisdiction,
@Description(shortDefinition="Computationally friendly name of the activity definition")
@OptionalParam(name="name")
StringAndListParam theName,
@Description(shortDefinition="What resource is being referenced")
@OptionalParam(name="predecessor", targetTypes={ } )
ReferenceAndListParam thePredecessor,
@Description(shortDefinition="Name of the publisher of the activity definition")
@OptionalParam(name="publisher")
StringAndListParam thePublisher,
@Description(shortDefinition="The current status of the activity definition")
@OptionalParam(name="status")
TokenAndListParam theStatus,
@Description(shortDefinition="What resource is being referenced")
@OptionalParam(name="successor", targetTypes={ } )
ReferenceAndListParam theSuccessor,
@Description(shortDefinition="The human-friendly name of the activity definition")
@OptionalParam(name="title")
StringAndListParam theTitle,
@Description(shortDefinition="Topics associated with the module")
@OptionalParam(name="topic")
TokenAndListParam theTopic,
@Description(shortDefinition="The uri that identifies the activity definition")
@OptionalParam(name="url")
UriAndListParam theUrl,
@Description(shortDefinition="The business version of the activity definition")
@OptionalParam(name="version")
TokenAndListParam theVersion,
@RawParam
Map<String, List<String>> theAdditionalRawParams,
@IncludeParam(reverse=true)
Set<Include> theRevIncludes,
@Description(shortDefinition="Only return resources which were last updated as specified by the given range")
@OptionalParam(name="_lastUpdated")
DateRangeParam theLastUpdated,
@IncludeParam(allow= {
"ActivityDefinition:composed-of" , "ActivityDefinition:depends-on" , "ActivityDefinition:derived-from" , "ActivityDefinition:predecessor" , "ActivityDefinition:successor" , "ActivityDefinition:composed-of" , "ActivityDefinition:depends-on" , "ActivityDefinition:derived-from" , "ActivityDefinition:predecessor" , "ActivityDefinition:successor" , "ActivityDefinition:composed-of" , "ActivityDefinition:depends-on" , "ActivityDefinition:derived-from" , "ActivityDefinition:predecessor" , "ActivityDefinition:successor" , "ActivityDefinition:composed-of" , "ActivityDefinition:depends-on" , "ActivityDefinition:derived-from" , "ActivityDefinition:predecessor" , "ActivityDefinition:successor" , "ActivityDefinition:composed-of" , "ActivityDefinition:depends-on" , "ActivityDefinition:derived-from" , "ActivityDefinition:predecessor" , "ActivityDefinition:successor" , "*"
})
Set<Include> theIncludes,
@Sort
SortSpec theSort,
@ca.uhn.fhir.rest.annotation.Count
Integer theCount
) {
startRequest(theServletRequest);
try {
SearchParameterMap paramMap = new SearchParameterMap();
paramMap.add(ca.uhn.fhir.rest.api.Constants.PARAM_CONTENT, theFtContent);
paramMap.add(ca.uhn.fhir.rest.api.Constants.PARAM_TEXT, theFtText);
paramMap.add(ca.uhn.fhir.rest.api.Constants.PARAM_TAG, theSearchForTag);
paramMap.add(ca.uhn.fhir.rest.api.Constants.PARAM_SECURITY, theSearchForSecurity);
paramMap.add(ca.uhn.fhir.rest.api.Constants.PARAM_PROFILE, theSearchForProfile);
paramMap.add("_has", theHas);
paramMap.add("_id", the_id);
paramMap.add("_language", the_language);
paramMap.add("composed-of", theComposed_of);
paramMap.add("date", theDate);
paramMap.add("depends-on", theDepends_on);
paramMap.add("derived-from", theDerived_from);
paramMap.add("description", theDescription);
paramMap.add("effective", theEffective);
paramMap.add("identifier", theIdentifier);
paramMap.add("jurisdiction", theJurisdiction);
paramMap.add("name", theName);
paramMap.add("predecessor", thePredecessor);
paramMap.add("publisher", thePublisher);
paramMap.add("status", theStatus);
paramMap.add("successor", theSuccessor);
paramMap.add("title", theTitle);
paramMap.add("topic", theTopic);
paramMap.add("url", theUrl);
paramMap.add("version", theVersion);
paramMap.setRevIncludes(theRevIncludes);
paramMap.setLastUpdated(theLastUpdated);
paramMap.setIncludes(theIncludes);
paramMap.setSort(theSort);
paramMap.setCount(theCount);
// paramMap.setRequestDetails(theRequestDetails);
getDao().translateRawParameters(theAdditionalRawParams, paramMap);
return getDao().search(paramMap, theRequestDetails);
} finally {
endRequest(theServletRequest);
}
}
}

View File

@ -0,0 +1,494 @@
package ca.uhn.fhir.jpa.cqf.ruler.providers;
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
import ca.uhn.fhir.jpa.provider.dstu3.JpaResourceProviderDstu3;
import ca.uhn.fhir.jpa.rp.dstu3.CodeSystemResourceProvider;
import ca.uhn.fhir.jpa.rp.dstu3.LibraryResourceProvider;
import ca.uhn.fhir.jpa.rp.dstu3.PatientResourceProvider;
import ca.uhn.fhir.jpa.rp.dstu3.ValueSetResourceProvider;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.rest.annotation.*;
import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.param.*;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import org.cqframework.cql.cql2elm.LibraryManager;
import org.cqframework.cql.cql2elm.ModelManager;
import org.cqframework.cql.elm.execution.Library;
import org.cqframework.cql.elm.execution.VersionedIdentifier;
import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.jpa.cqf.ruler.config.STU3LibraryLoader;
import ca.uhn.fhir.jpa.cqf.ruler.config.STU3LibrarySourceProvider;
import org.opencds.cqf.cql.execution.Context;
import org.opencds.cqf.cql.execution.LibraryLoader;
import org.opencds.cqf.cql.runtime.Interval;
import org.opencds.cqf.cql.terminology.TerminologyProvider;
import org.opencds.cqf.cql.terminology.fhir.FhirTerminologyProvider;
import ca.uhn.fhir.jpa.cqf.ruler.helpers.DateHelper;
import ca.uhn.fhir.jpa.cqf.ruler.helpers.FhirMeasureEvaluator;
import java.util.*;
/*
IN periodStart 1..1 date
The start of the measurement period. In keeping with the semantics of the date parameter used in the FHIR search operation, the period will start at the beginning of the period implied by the supplied timestamp. E.g. a value of 2014 would set the period start to be 2014-01-01T00:00:00 inclusive
IN periodEnd 1..1 date
The end of the measurement period. The period will end at the end of the period implied by the supplied timestamp. E.g. a value of 2014 would set the period end to be 2014-12-31T23:59:59 inclusive
IN measure 0..1 Reference
The measure to evaluate. This parameter is only required when the operation is invoked on the resource type, it is not used when invoking the operation on a Measure instance
IN reportType 0..1 code
The type of measure report, patient, patient-list, or population. If not specified, a default value of patient will be used if the patient parameter is supplied, otherwise, population will be used
IN patient 0..1 Reference
Patient to evaluate against. If not specified, the measure will be evaluated for all patients that meet the requirements of the measure. If specified, only the referenced patient will be evaluated
IN practitioner 0..1 Reference
Practitioner to evaluate. If specified, the measure will be evaluated only for patients whose primary practitioner is the identified practitioner
IN lastReceivedOn 0..1 dateTime
The date the results of this measure were last received. This parameter is only valid for patient-level reports and is used to indicate when the last time a result for this patient was received. This information can be used to limit the set of resources returned for a patient-level report
OUT return 1..1 MeasureReport
The results of the measure calculation. See the MeasureReport resource for a complete description of the output of this operation
*/
public class FHIRMeasureResourceProvider extends JpaResourceProviderDstu3<Measure> {
private JpaDataProvider provider;
private TerminologyProvider terminologyProvider;
private Context context;
private Interval measurementPeriod;
private MeasureReport report = new MeasureReport();
private FhirMeasureEvaluator evaluator = new FhirMeasureEvaluator();
public FHIRMeasureResourceProvider(Collection<IResourceProvider> providers) {
this.provider = new JpaDataProvider(providers);
}
private LibraryResourceProvider getLibraryResourceProvider() {
return (LibraryResourceProvider)provider.resolveResourceProvider("Library");
}
private ModelManager modelManager;
private ModelManager getModelManager() {
if (modelManager == null) {
modelManager = new ModelManager();
}
return modelManager;
}
private LibraryManager libraryManager;
private LibraryManager getLibraryManager() {
if (libraryManager == null) {
libraryManager = new LibraryManager(getModelManager());
libraryManager.getLibrarySourceLoader().clearProviders();
libraryManager.getLibrarySourceLoader().registerProvider(getLibrarySourceProvider());
}
return libraryManager;
}
private LibraryLoader libraryLoader;
private LibraryLoader getLibraryLoader() {
if (libraryLoader == null) {
libraryLoader = new STU3LibraryLoader(getLibraryResourceProvider(), getLibraryManager(), getModelManager());
}
return libraryLoader;
}
private STU3LibrarySourceProvider librarySourceProvider;
private STU3LibrarySourceProvider getLibrarySourceProvider() {
if (librarySourceProvider == null) {
librarySourceProvider = new STU3LibrarySourceProvider(getLibraryResourceProvider());
}
return librarySourceProvider;
}
/*
This is not "pure" FHIR.
The "source", "user", "pass", and "primaryLibraryName" parameters were added to simplify the operation.
*/
@Operation(name = "$evaluate", idempotent = true)
public MeasureReport evaluateMeasure(
@IdParam IdType theId,
@OptionalParam(name="reportType") String reportType,
@OptionalParam(name="patient") String patientId,
@OptionalParam(name="practitioner") String practitioner,
@OptionalParam(name="lastReceivedOn") String lastReceivedOn,
@RequiredParam(name="startPeriod") String startPeriod,
@RequiredParam(name="endPeriod") String endPeriod,
@OptionalParam(name="source") String source,
@OptionalParam(name="user") String user,
@OptionalParam(name="pass") String pass,
@OptionalParam(name="primaryLibraryName") String primaryLibraryName) throws InternalErrorException, FHIRException
{
Measure measure = this.getDao().read(theId);
// load libraries referenced in measure
// TODO: need better way to determine primary library
// - for now using a name convention: <measure ID>-library or primaryLibraryName param
Library primary = null;
for (Reference ref : measure.getLibrary()) {
VersionedIdentifier vid = new VersionedIdentifier().withId(ref.getReference());
Library temp = getLibraryLoader().load(vid);
if (vid.getId().equals(measure.getIdElement().getIdPart() + "-logic")
|| vid.getId().equals("Library/" + measure.getIdElement().getIdPart() + "-logic")
|| (primaryLibraryName != null && temp.getIdentifier().getId().equals(primaryLibraryName)))
{
primary = temp;
context = new Context(primary);
}
}
if (primary == null) {
throw new IllegalArgumentException(
"Primary library not found.\nFollow the naming conventions <measureID>-library or specify primary library in request."
);
}
if (((STU3LibraryLoader)getLibraryLoader()).getLibraries().isEmpty()) {
throw new IllegalArgumentException(String.format("Could not load library source for libraries referenced in %s measure.", measure.getId()));
}
// Prep - defining the context, measurementPeriod, terminology provider, and data provider
context.registerLibraryLoader(getLibraryLoader());
measurementPeriod =
new Interval(
DateHelper.resolveRequestDate(startPeriod, true), true,
DateHelper.resolveRequestDate(endPeriod, false), true
);
if (source == null) {
JpaResourceProviderDstu3<ValueSet> vs = (ValueSetResourceProvider) provider.resolveResourceProvider("ValueSet");
JpaResourceProviderDstu3<CodeSystem> cs = (CodeSystemResourceProvider) provider.resolveResourceProvider("CodeSystem");
terminologyProvider = new JpaTerminologyProvider(vs, cs);
}
else {
terminologyProvider = user == null || pass == null ? new FhirTerminologyProvider().withEndpoint(source)
: new FhirTerminologyProvider().withBasicAuth(user, pass).withEndpoint(source);
}
provider.setTerminologyProvider(terminologyProvider);
provider.setExpandValueSets(true);
context.registerDataProvider("http://hl7.org/fhir", provider);
// determine the report type (patient, patient-list, or population (summary))
if (reportType != null) {
switch (reportType) {
case "patient": return evaluatePatientMeasure(measure, patientId);
case "patient-list": return evaluatePatientListMeasure(measure, practitioner);
case "population": return evaluatePopulationMeasure(measure);
case "summary": return evaluatePopulationMeasure(measure);
default:
throw new IllegalArgumentException("Invalid report type " + reportType);
}
}
// default behavior
else {
if (patientId != null) return evaluatePatientMeasure(measure, patientId);
if (practitioner != null) return evaluatePatientListMeasure(measure, practitioner);
return evaluatePopulationMeasure(measure);
}
}
private void validateReport() {
if (report == null) {
throw new InternalErrorException("MeasureReport is null");
}
if (report.getEvaluatedResources() == null) {
throw new InternalErrorException("EvaluatedResources is null");
}
}
private MeasureReport evaluatePatientMeasure(Measure measure, String patientId) {
if (patientId == null) {
throw new IllegalArgumentException("Patient id must be provided for patient type measure evaluation");
}
Patient patient = ((PatientResourceProvider) provider.resolveResourceProvider("Patient")).getDao().read(new IdType(patientId));
if (patient == null) {
throw new InternalErrorException("Patient is null");
}
context.setContextValue("Patient", patientId);
report = evaluator.evaluate(context, measure, patient, measurementPeriod);
validateReport();
return report;
}
private MeasureReport evaluatePatientListMeasure(Measure measure, String practitioner) {
SearchParameterMap map = new SearchParameterMap();
map.add("general-practitioner", new ReferenceParam(practitioner));
IBundleProvider patientProvider = ((PatientResourceProvider) provider.resolveResourceProvider("Patient")).getDao().search(map);
List<IBaseResource> patientList = patientProvider.getResources(0, patientProvider.size());
if (patientList.isEmpty()) {
throw new IllegalArgumentException("No patients were found with practitioner reference " + practitioner);
}
List<Patient> patients = new ArrayList<>();
patientList.forEach(x -> patients.add((Patient) x));
// context.setContextValue("Population", patients);
report = evaluator.evaluate(context, measure, patients, measurementPeriod, MeasureReport.MeasureReportType.PATIENTLIST);
validateReport();
return report;
}
private MeasureReport evaluatePopulationMeasure(Measure measure) {
IBundleProvider patientProvider = ((PatientResourceProvider) provider.resolveResourceProvider("Patient")).getDao().search(new SearchParameterMap());
List<IBaseResource> population = patientProvider.getResources(0, patientProvider.size());
if (population.isEmpty()) {
throw new IllegalArgumentException("No patients were found in the data provider at endpoint " + provider.getEndpoint());
}
List<Patient> patients = new ArrayList<>();
population.forEach(x -> patients.add((Patient) x));
report = evaluator.evaluate(context, measure, patients, measurementPeriod, MeasureReport.MeasureReportType.SUMMARY);
validateReport();
return report;
}
@Operation(name = "$data-requirements", idempotent = true)
public org.hl7.fhir.dstu3.model.Library dataRequirements(@IdParam IdType theId,
@RequiredParam(name="startPeriod") String startPeriod,
@RequiredParam(name="endPeriod") String endPeriod)
throws InternalErrorException, FHIRException
{
Measure measure = this.getDao().read(theId);
// NOTE: This assumes there is only one library and it is the primary library for the measure.
org.hl7.fhir.dstu3.model.Library libraryResource =
getLibraryResourceProvider()
.getDao()
.read(new IdType(measure.getLibraryFirstRep().getReference()));
// TODO: what are the period params for? Library.effectivePeriod?
List<RelatedArtifact> dependencies = new ArrayList<>();
for (RelatedArtifact dependency : libraryResource.getRelatedArtifact()) {
if (dependency.getType().toCode().equals("depends-on")) {
dependencies.add(dependency);
}
}
List<Coding> typeCoding = new ArrayList<>();
typeCoding.add(new Coding().setCode("module-definition"));
org.hl7.fhir.dstu3.model.Library library =
new org.hl7.fhir.dstu3.model.Library().setType(new CodeableConcept().setCoding(typeCoding));
if (!dependencies.isEmpty()) {
library.setRelatedArtifact(dependencies);
}
return library
.setDataRequirement(libraryResource.getDataRequirement())
.setParameter(libraryResource.getParameter());
}
@Search(allowUnknownParams=true)
public IBundleProvider search(
javax.servlet.http.HttpServletRequest theServletRequest,
RequestDetails theRequestDetails,
@Description(shortDefinition="Search the contents of the resource's data using a fulltext search")
@OptionalParam(name=ca.uhn.fhir.rest.api.Constants.PARAM_CONTENT)
StringAndListParam theFtContent,
@Description(shortDefinition="Search the contents of the resource's narrative using a fulltext search")
@OptionalParam(name=ca.uhn.fhir.rest.api.Constants.PARAM_TEXT)
StringAndListParam theFtText,
@Description(shortDefinition="Search for resources which have the given tag")
@OptionalParam(name=ca.uhn.fhir.rest.api.Constants.PARAM_TAG)
TokenAndListParam theSearchForTag,
@Description(shortDefinition="Search for resources which have the given security labels")
@OptionalParam(name=ca.uhn.fhir.rest.api.Constants.PARAM_SECURITY)
TokenAndListParam theSearchForSecurity,
@Description(shortDefinition="Search for resources which have the given profile")
@OptionalParam(name=ca.uhn.fhir.rest.api.Constants.PARAM_PROFILE)
UriAndListParam theSearchForProfile,
@Description(shortDefinition="Return resources linked to by the given target")
@OptionalParam(name="_has")
HasAndListParam theHas,
@Description(shortDefinition="The ID of the resource")
@OptionalParam(name="_id")
TokenAndListParam the_id,
@Description(shortDefinition="The language of the resource")
@OptionalParam(name="_language")
StringAndListParam the_language,
@Description(shortDefinition="What resource is being referenced")
@OptionalParam(name="composed-of", targetTypes={ } )
ReferenceAndListParam theComposed_of,
@Description(shortDefinition="The measure publication date")
@OptionalParam(name="date")
DateRangeParam theDate,
@Description(shortDefinition="What resource is being referenced")
@OptionalParam(name="depends-on", targetTypes={ } )
ReferenceAndListParam theDepends_on,
@Description(shortDefinition="What resource is being referenced")
@OptionalParam(name="derived-from", targetTypes={ } )
ReferenceAndListParam theDerived_from,
@Description(shortDefinition="The description of the measure")
@OptionalParam(name="description")
StringAndListParam theDescription,
@Description(shortDefinition="The time during which the measure is intended to be in use")
@OptionalParam(name="effective")
DateRangeParam theEffective,
@Description(shortDefinition="External identifier for the measure")
@OptionalParam(name="identifier")
TokenAndListParam theIdentifier,
@Description(shortDefinition="Intended jurisdiction for the measure")
@OptionalParam(name="jurisdiction")
TokenAndListParam theJurisdiction,
@Description(shortDefinition="Computationally friendly name of the measure")
@OptionalParam(name="name")
StringAndListParam theName,
@Description(shortDefinition="What resource is being referenced")
@OptionalParam(name="predecessor", targetTypes={ } )
ReferenceAndListParam thePredecessor,
@Description(shortDefinition="Name of the publisher of the measure")
@OptionalParam(name="publisher")
StringAndListParam thePublisher,
@Description(shortDefinition="The current status of the measure")
@OptionalParam(name="status")
TokenAndListParam theStatus,
@Description(shortDefinition="What resource is being referenced")
@OptionalParam(name="successor", targetTypes={ } )
ReferenceAndListParam theSuccessor,
@Description(shortDefinition="The human-friendly name of the measure")
@OptionalParam(name="title")
StringAndListParam theTitle,
@Description(shortDefinition="Topics associated with the module")
@OptionalParam(name="topic")
TokenAndListParam theTopic,
@Description(shortDefinition="The uri that identifies the measure")
@OptionalParam(name="url")
UriAndListParam theUrl,
@Description(shortDefinition="The business version of the measure")
@OptionalParam(name="version")
TokenAndListParam theVersion,
@RawParam
Map<String, List<String>> theAdditionalRawParams,
@IncludeParam(reverse=true)
Set<Include> theRevIncludes,
@Description(shortDefinition="Only return resources which were last updated as specified by the given range")
@OptionalParam(name="_lastUpdated")
DateRangeParam theLastUpdated,
@IncludeParam(allow= {
"Measure:composed-of",
"Measure:depends-on",
"Measure:derived-from",
"Measure:predecessor",
"Measure:successor",
"Measure:composed-of",
"Measure:depends-on",
"Measure:derived-from",
"Measure:predecessor",
"Measure:successor",
"Measure:composed-of",
"Measure:depends-on",
"Measure:derived-from",
"Measure:predecessor",
"Measure:successor",
"Measure:composed-of",
"Measure:depends-on",
"Measure:derived-from",
"Measure:predecessor",
"Measure:successor",
"Measure:composed-of",
"Measure:depends-on",
"Measure:derived-from",
"Measure:predecessor",
"Measure:successor",
"*"
})
Set<Include> theIncludes,
@Sort
SortSpec theSort,
@ca.uhn.fhir.rest.annotation.Count
Integer theCount
) {
startRequest(theServletRequest);
try {
SearchParameterMap paramMap = new SearchParameterMap();
paramMap.add(ca.uhn.fhir.rest.api.Constants.PARAM_CONTENT, theFtContent);
paramMap.add(ca.uhn.fhir.rest.api.Constants.PARAM_TEXT, theFtText);
paramMap.add(ca.uhn.fhir.rest.api.Constants.PARAM_TAG, theSearchForTag);
paramMap.add(ca.uhn.fhir.rest.api.Constants.PARAM_SECURITY, theSearchForSecurity);
paramMap.add(ca.uhn.fhir.rest.api.Constants.PARAM_PROFILE, theSearchForProfile);
paramMap.add("_has", theHas);
paramMap.add("_id", the_id);
paramMap.add("_language", the_language);
paramMap.add("composed-of", theComposed_of);
paramMap.add("date", theDate);
paramMap.add("depends-on", theDepends_on);
paramMap.add("derived-from", theDerived_from);
paramMap.add("description", theDescription);
paramMap.add("effective", theEffective);
paramMap.add("identifier", theIdentifier);
paramMap.add("jurisdiction", theJurisdiction);
paramMap.add("name", theName);
paramMap.add("predecessor", thePredecessor);
paramMap.add("publisher", thePublisher);
paramMap.add("status", theStatus);
paramMap.add("successor", theSuccessor);
paramMap.add("title", theTitle);
paramMap.add("topic", theTopic);
paramMap.add("url", theUrl);
paramMap.add("version", theVersion);
paramMap.setRevIncludes(theRevIncludes);
paramMap.setLastUpdated(theLastUpdated);
paramMap.setIncludes(theIncludes);
paramMap.setSort(theSort);
paramMap.setCount(theCount);
// paramMap.setRequestDetails(theRequestDetails);
getDao().translateRawParameters(theAdditionalRawParams, paramMap);
return getDao().search(paramMap, theRequestDetails);
} finally {
endRequest(theServletRequest);
}
}
}

View File

@ -0,0 +1,295 @@
package ca.uhn.fhir.jpa.cqf.ruler.providers;
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
import ca.uhn.fhir.jpa.provider.dstu3.JpaResourceProviderDstu3;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.rest.annotation.*;
import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.param.*;
import ca.uhn.fhir.rest.server.IResourceProvider;
import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.exceptions.FHIRException;
import ca.uhn.fhir.jpa.cqf.ruler.builders.CarePlanBuilder;
import ca.uhn.fhir.jpa.cqf.ruler.builders.JavaDateBuilder;
import org.opencds.cqf.cql.runtime.DateTime;
import javax.xml.bind.JAXBException;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class FHIRPlanDefinitionResourceProvider extends JpaResourceProviderDstu3<PlanDefinition> {
private JpaDataProvider provider;
private CqlExecutionProvider executionProvider;
public FHIRPlanDefinitionResourceProvider(Collection<IResourceProvider> providers) {
this.provider = new JpaDataProvider(providers);
this.executionProvider = new CqlExecutionProvider(providers);
}
@Operation(name = "$apply", idempotent = true)
public CarePlan applyPlanDefinition(
@IdParam IdType theId,
@RequiredParam(name="patient") String patientId,
@OptionalParam(name="encounter") String encounterId,
@OptionalParam(name="practitioner") String practitionerId,
@OptionalParam(name="organization") String organizationId,
@OptionalParam(name="userType") String userType,
@OptionalParam(name="userLanguage") String userLanguage,
@OptionalParam(name="userTaskContext") String userTaskContext,
@OptionalParam(name="setting") String setting,
@OptionalParam(name="settingContext") String settingContext)
throws IOException, JAXBException, FHIRException
{
PlanDefinition planDefinition = this.getDao().read(theId);
if (planDefinition == null) {
throw new IllegalArgumentException("Couldn't find PlanDefintion " + theId);
}
CarePlanBuilder builder = new CarePlanBuilder();
builder
.buildDefinition(new Reference(planDefinition.getIdElement().getIdPart()))
.buildSubject(new Reference(patientId))
.buildStatus(CarePlan.CarePlanStatus.DRAFT);
if (encounterId != null) builder.buildContext(new Reference(encounterId));
if (practitionerId != null) builder.buildAuthor(new Reference(practitionerId));
if (organizationId != null) builder.buildAuthor(new Reference(organizationId));
if (userLanguage != null) builder.buildLanguage(userLanguage);
return resolveActions(planDefinition, builder, patientId);
}
private CarePlan resolveActions(PlanDefinition planDefinition, CarePlanBuilder builder,
String patientId) throws FHIRException
{
for (PlanDefinition.PlanDefinitionActionComponent action : planDefinition.getAction())
{
// TODO - Apply input/output dataRequirements?
if (meetsConditions(planDefinition, patientId, action)) {
return resolveDynamicValues(planDefinition, builder.build(), patientId, action);
}
}
return builder.build();
}
public Boolean meetsConditions(PlanDefinition planDefinition, String patientId,
PlanDefinition.PlanDefinitionActionComponent action)
{
for (PlanDefinition.PlanDefinitionActionConditionComponent condition: action.getCondition()) {
// TODO start
// TODO stop
if (condition.getKind() == PlanDefinition.ActionConditionKind.APPLICABILITY) {
if (!condition.getLanguage().equals("text/cql")) {
// TODO - log this
continue;
}
if (!condition.hasExpression()) {
// TODO - log this
continue;
}
String cql = condition.getExpression();
Object result = executionProvider.evaluateInContext(planDefinition, cql, patientId);
if (!(result instanceof Boolean)) {
// TODO - log this
// maybe try an int value check (i.e. 0 or 1)?
continue;
}
if (!(Boolean) result) {
return false;
}
}
}
return true;
}
private CarePlan resolveDynamicValues(PlanDefinition planDefinition, CarePlan carePlan, String patientId,
PlanDefinition.PlanDefinitionActionComponent action) throws FHIRException
{
for (PlanDefinition.PlanDefinitionActionDynamicValueComponent dynamicValue: action.getDynamicValue())
{
if (dynamicValue.hasExpression()) {
Object result =
executionProvider
.evaluateInContext(planDefinition, dynamicValue.getExpression(), patientId);
if (dynamicValue.hasPath() && dynamicValue.getPath().equals("$this"))
{
carePlan = (CarePlan) result;
}
else {
// TODO - likely need more date tranformations
if (result instanceof DateTime) {
result =
new JavaDateBuilder()
.buildFromDateTime((DateTime) result)
.build();
}
else if (result instanceof String) {
result = new StringType((String) result);
}
provider.setValue(carePlan, dynamicValue.getPath(), result);
}
}
}
return carePlan;
}
@Search(allowUnknownParams=true)
public IBundleProvider search(
javax.servlet.http.HttpServletRequest theServletRequest,
RequestDetails theRequestDetails,
@Description(shortDefinition="Search the contents of the resource's data using a fulltext search")
@OptionalParam(name=ca.uhn.fhir.rest.api.Constants.PARAM_CONTENT)
StringAndListParam theFtContent,
@Description(shortDefinition="Search the contents of the resource's narrative using a fulltext search")
@OptionalParam(name=ca.uhn.fhir.rest.api.Constants.PARAM_TEXT)
StringAndListParam theFtText,
@Description(shortDefinition="Search for resources which have the given tag")
@OptionalParam(name=ca.uhn.fhir.rest.api.Constants.PARAM_TAG)
TokenAndListParam theSearchForTag,
@Description(shortDefinition="Search for resources which have the given security labels")
@OptionalParam(name=ca.uhn.fhir.rest.api.Constants.PARAM_SECURITY)
TokenAndListParam theSearchForSecurity,
@Description(shortDefinition="Search for resources which have the given profile")
@OptionalParam(name=ca.uhn.fhir.rest.api.Constants.PARAM_PROFILE)
UriAndListParam theSearchForProfile,
@Description(shortDefinition="Return resources linked to by the given target")
@OptionalParam(name="_has")
HasAndListParam theHas,
@Description(shortDefinition="The ID of the resource")
@OptionalParam(name="_id")
TokenAndListParam the_id,
@Description(shortDefinition="The language of the resource")
@OptionalParam(name="_language")
StringAndListParam the_language,
@Description(shortDefinition="What resource is being referenced")
@OptionalParam(name="composed-of", targetTypes={ } )
ReferenceAndListParam theComposed_of,
@Description(shortDefinition="The plan definition publication date")
@OptionalParam(name="date")
DateRangeParam theDate,
@Description(shortDefinition="What resource is being referenced")
@OptionalParam(name="depends-on", targetTypes={ } )
ReferenceAndListParam theDepends_on,
@Description(shortDefinition="What resource is being referenced")
@OptionalParam(name="derived-from", targetTypes={ } )
ReferenceAndListParam theDerived_from,
@Description(shortDefinition="The description of the plan definition")
@OptionalParam(name="description")
StringAndListParam theDescription,
@Description(shortDefinition="The time during which the plan definition is intended to be in use")
@OptionalParam(name="effective")
DateRangeParam theEffective,
@Description(shortDefinition="External identifier for the plan definition")
@OptionalParam(name="identifier")
TokenAndListParam theIdentifier,
@Description(shortDefinition="Intended jurisdiction for the plan definition")
@OptionalParam(name="jurisdiction")
TokenAndListParam theJurisdiction,
@Description(shortDefinition="Computationally friendly name of the plan definition")
@OptionalParam(name="name")
StringAndListParam theName,
@Description(shortDefinition="What resource is being referenced")
@OptionalParam(name="predecessor", targetTypes={ } )
ReferenceAndListParam thePredecessor,
@Description(shortDefinition="Name of the publisher of the plan definition")
@OptionalParam(name="publisher")
StringAndListParam thePublisher,
@Description(shortDefinition="The current status of the plan definition")
@OptionalParam(name="status")
TokenAndListParam theStatus,
@Description(shortDefinition="What resource is being referenced")
@OptionalParam(name="successor", targetTypes={ } )
ReferenceAndListParam theSuccessor,
@Description(shortDefinition="The human-friendly name of the plan definition")
@OptionalParam(name="title")
StringAndListParam theTitle,
@Description(shortDefinition="Topics associated with the module")
@OptionalParam(name="topic")
TokenAndListParam theTopic,
@Description(shortDefinition="The uri that identifies the plan definition")
@OptionalParam(name="url")
UriAndListParam theUrl,
@Description(shortDefinition="The business version of the plan definition")
@OptionalParam(name="version")
TokenAndListParam theVersion,
@RawParam
Map<String, List<String>> theAdditionalRawParams,
@IncludeParam(reverse=true)
Set<Include> theRevIncludes,
@Description(shortDefinition="Only return resources which were last updated as specified by the given range")
@OptionalParam(name="_lastUpdated")
DateRangeParam theLastUpdated,
@IncludeParam(allow= {
"PlanDefinition:composed-of" , "PlanDefinition:depends-on" , "PlanDefinition:derived-from" , "PlanDefinition:predecessor" , "PlanDefinition:successor" , "PlanDefinition:composed-of" , "PlanDefinition:depends-on" , "PlanDefinition:derived-from" , "PlanDefinition:predecessor" , "PlanDefinition:successor" , "PlanDefinition:composed-of" , "PlanDefinition:depends-on" , "PlanDefinition:derived-from" , "PlanDefinition:predecessor" , "PlanDefinition:successor" , "PlanDefinition:composed-of" , "PlanDefinition:depends-on" , "PlanDefinition:derived-from" , "PlanDefinition:predecessor" , "PlanDefinition:successor" , "PlanDefinition:composed-of" , "PlanDefinition:depends-on" , "PlanDefinition:derived-from" , "PlanDefinition:predecessor" , "PlanDefinition:successor" , "*"
})
Set<Include> theIncludes,
@Sort
SortSpec theSort,
@ca.uhn.fhir.rest.annotation.Count
Integer theCount
) {
startRequest(theServletRequest);
try {
SearchParameterMap paramMap = new SearchParameterMap();
paramMap.add(ca.uhn.fhir.rest.api.Constants.PARAM_CONTENT, theFtContent);
paramMap.add(ca.uhn.fhir.rest.api.Constants.PARAM_TEXT, theFtText);
paramMap.add(ca.uhn.fhir.rest.api.Constants.PARAM_TAG, theSearchForTag);
paramMap.add(ca.uhn.fhir.rest.api.Constants.PARAM_SECURITY, theSearchForSecurity);
paramMap.add(ca.uhn.fhir.rest.api.Constants.PARAM_PROFILE, theSearchForProfile);
paramMap.add("_has", theHas);
paramMap.add("_id", the_id);
paramMap.add("_language", the_language);
paramMap.add("composed-of", theComposed_of);
paramMap.add("date", theDate);
paramMap.add("depends-on", theDepends_on);
paramMap.add("derived-from", theDerived_from);
paramMap.add("description", theDescription);
paramMap.add("effective", theEffective);
paramMap.add("identifier", theIdentifier);
paramMap.add("jurisdiction", theJurisdiction);
paramMap.add("name", theName);
paramMap.add("predecessor", thePredecessor);
paramMap.add("publisher", thePublisher);
paramMap.add("status", theStatus);
paramMap.add("successor", theSuccessor);
paramMap.add("title", theTitle);
paramMap.add("topic", theTopic);
paramMap.add("url", theUrl);
paramMap.add("version", theVersion);
paramMap.setRevIncludes(theRevIncludes);
paramMap.setLastUpdated(theLastUpdated);
paramMap.setIncludes(theIncludes);
paramMap.setSort(theSort);
paramMap.setCount(theCount);
// paramMap.setRequestDetails(theRequestDetails);
getDao().translateRawParameters(theAdditionalRawParams, paramMap);
return getDao().search(paramMap, theRequestDetails);
} finally {
endRequest(theServletRequest);
}
}
}

View File

@ -0,0 +1,127 @@
package ca.uhn.fhir.jpa.cqf.ruler.providers;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
import ca.uhn.fhir.jpa.provider.dstu3.JpaResourceProviderDstu3;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.*;
import ca.uhn.fhir.rest.server.IResourceProvider;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.opencds.cqf.cql.data.fhir.FhirDataProviderStu3;
import org.opencds.cqf.cql.runtime.Code;
import org.opencds.cqf.cql.runtime.Interval;
import org.opencds.cqf.cql.terminology.ValueSetInfo;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
/**
* Created by Christopher Schuler on 7/17/2017.
*/
public class JpaDataProvider extends FhirDataProviderStu3 {
// need these to access the dao
private HashMap<String, IResourceProvider> providers;
public JpaDataProvider(Collection<IResourceProvider> providers) {
this.providers = new HashMap<>();
for (IResourceProvider i : providers) {
this.providers.put(i.getResourceType().getSimpleName(), i);
}
// NOTE: Defaults to STU3
setPackageName("org.hl7.fhir.dstu3.model");
setFhirContext(FhirContext.forDstu3());
}
public Iterable<Object> retrieve(String context, Object contextValue, String dataType, String templateId,
String codePath, Iterable<Code> codes, String valueSet, String datePath,
String dateLowPath, String dateHighPath, Interval dateRange)
{
SearchParameterMap map = new SearchParameterMap();
map.setLastUpdated(new DateRangeParam());
if (templateId != null && !templateId.equals("")) {
// do something?
}
if (valueSet != null && valueSet.startsWith("urn:oid:")) {
valueSet = valueSet.replace("urn:oid:", "");
}
if (codePath == null && (codes != null || valueSet != null)) {
throw new IllegalArgumentException("A code path must be provided when filtering on codes or a valueset.");
}
if (dataType == null) {
throw new IllegalArgumentException("A data type (i.e. Procedure, Valueset, etc...) must be specified for clinical data retrieval");
}
if (context != null && context.equals("Patient") && contextValue != null) {
ReferenceParam patientParam = new ReferenceParam(contextValue.toString());
map.add(getPatientSearchParam(dataType), patientParam);
}
if (codePath != null && !codePath.equals("")) {
if (terminologyProvider != null && expandValueSets) {
ValueSetInfo valueSetInfo = new ValueSetInfo().withId(valueSet);
codes = terminologyProvider.expand(valueSetInfo);
}
if (codes != null) {
TokenOrListParam codeParams = new TokenOrListParam();
for (Code code : codes) {
codeParams.addOr(new TokenParam(code.getSystem(), code.getCode()));
}
map.add(convertPathToSearchParam(codePath), codeParams);
}
}
if (dateRange != null) {
DateParam low = null;
DateParam high = null;
if (dateRange.getLow() != null) {
low = new DateParam(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, convertPathToSearchParam(dateLowPath != null ? dateLowPath : datePath));
}
if (dateRange.getHigh() != null) {
high = new DateParam(ParamPrefixEnum.LESSTHAN_OR_EQUALS, convertPathToSearchParam(dateHighPath != null ? dateHighPath : datePath));
}
DateRangeParam rangeParam;
if (low == null && high != null) {
rangeParam = new DateRangeParam(high);
}
else if (high == null && low != null) {
rangeParam = new DateRangeParam(low);
}
else {
rangeParam = new DateRangeParam(high, low);
}
map.add(convertPathToSearchParam(datePath), rangeParam);
}
JpaResourceProviderDstu3<? extends IAnyResource> jpaResProvider = resolveResourceProvider(dataType);
IBundleProvider bundleProvider = jpaResProvider.getDao().search(map);
List<IBaseResource> resourceList = bundleProvider.getResources(0, 50);
return resolveResourceList(resourceList);
}
public Iterable<Object> resolveResourceList(List<IBaseResource> resourceList) {
List<Object> ret = new ArrayList<>();
for (IBaseResource res : resourceList) {
Class clazz = res.getClass();
ret.add(clazz.cast(res));
}
// ret.addAll(resourceList);
return ret;
}
public JpaResourceProviderDstu3<? extends IAnyResource> resolveResourceProvider(String datatype) {
return (JpaResourceProviderDstu3<? extends IAnyResource>) providers.get(datatype);
}
}

View File

@ -0,0 +1,58 @@
package ca.uhn.fhir.jpa.cqf.ruler.providers;
import ca.uhn.fhir.jpa.provider.dstu3.JpaResourceProviderDstu3;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import org.hl7.fhir.dstu3.model.CodeSystem;
import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.dstu3.model.ValueSet;
import org.opencds.cqf.cql.runtime.Code;
import org.opencds.cqf.cql.terminology.CodeSystemInfo;
import org.opencds.cqf.cql.terminology.TerminologyProvider;
import org.opencds.cqf.cql.terminology.ValueSetInfo;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Christopher Schuler on 7/17/2017.
*/
public class JpaTerminologyProvider implements TerminologyProvider {
private JpaResourceProviderDstu3<ValueSet> valueSetProvider;
private JpaResourceProviderDstu3<CodeSystem> codeSystemProvider;
public JpaTerminologyProvider(JpaResourceProviderDstu3<ValueSet> valueSetProvider, JpaResourceProviderDstu3<CodeSystem> codeSystemProvider) {
this.valueSetProvider = valueSetProvider;
this.codeSystemProvider = codeSystemProvider;
}
@Override
public boolean in(Code code, ValueSetInfo valueSet) throws ResourceNotFoundException {
for (Code c : expand(valueSet)) {
if (c == null) continue;
if (c.getCode().equals(code.getCode()) && c.getSystem().equals(code.getSystem())) {
return true;
}
}
return false;
}
@Override
public Iterable<Code> expand(ValueSetInfo valueSet) throws ResourceNotFoundException {
ValueSet vs = valueSetProvider.getDao().read(new IdType(valueSet.getId()));
List<Code> codes = new ArrayList<>();
for (ValueSet.ValueSetExpansionContainsComponent expansion : vs.getExpansion().getContains()) {
codes.add(new Code().withCode(expansion.getCode()).withSystem(expansion.getSystem()));
}
return codes;
}
@Override
public Code lookup(Code code, CodeSystemInfo codeSystem) throws ResourceNotFoundException {
CodeSystem cs = codeSystemProvider.getDao().read(new IdType(codeSystem.getId()));
for (CodeSystem.ConceptDefinitionComponent concept : cs.getConcept()) {
if (concept.getCode().equals(code.getCode()))
return code.withSystem(codeSystem.getId()).withDisplay(concept.getDisplay());
}
return code;
}
}

View File

@ -0,0 +1,46 @@
package ca.uhn.fhir.jpa.cqf.ruler.providers;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.narrative.CustomThymeleafNarrativeGenerator;
import org.hl7.fhir.instance.model.api.IBaseResource;
import java.io.*;
import java.nio.file.Path;
import java.nio.file.Paths;
/**
* Created by Christopher on 2/4/2017.
*/
public class NarrativeProvider {
// args[0] == relative path to json resource -> i.e. measure/cms146.json
public static void main(String[] args) {
Path pathToResources = Paths.get("src/main/resources/narratives").toAbsolutePath();
Path pathToProp = pathToResources.resolve("narrative.properties");
String propFile = "file:" + pathToProp.toString();
CustomThymeleafNarrativeGenerator gen = new CustomThymeleafNarrativeGenerator(propFile);
FhirContext ctx = FhirContext.forDstu3();
ctx.setNarrativeGenerator(gen);
// examples are here: src/main/resources/narratives/examples
if (args.length < 1) { throw new IllegalArgumentException("provide a file name..."); }
Path pathToResource = pathToResources.resolve("examples/" + args[0]);
try {
IBaseResource res = ctx.newJsonParser().parseResource(new FileReader(pathToResource.toFile()));
String resource = ctx.newXmlParser().setPrettyPrint(true)
.encodeResourceToString(ctx.newJsonParser().setPrettyPrint(true).parseResource(new FileReader(pathToResource.toFile())));
try {
PrintWriter writer = new PrintWriter(new File(pathToResources.resolve("scratch.xml").toString()), "UTF-8");
writer.println(resource);
writer.println();
writer.close();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,150 @@
package ca.uhn.fhir.jpa.cqf.ruler.servlet;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
import ca.uhn.fhir.jpa.provider.dstu3.JpaConformanceProviderDstu3;
import ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3;
import ca.uhn.fhir.jpa.rp.dstu3.ActivityDefinitionResourceProvider;
import ca.uhn.fhir.jpa.rp.dstu3.MeasureResourceProvider;
import ca.uhn.fhir.jpa.rp.dstu3.PlanDefinitionResourceProvider;
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.rest.server.interceptor.LoggingInterceptor;
import org.hl7.fhir.dstu3.model.Bundle;
import org.hl7.fhir.dstu3.model.Meta;
import ca.uhn.fhir.jpa.cqf.ruler.providers.FHIRActivityDefinitionResourceProvider;
import ca.uhn.fhir.jpa.cqf.ruler.providers.FHIRMeasureResourceProvider;
import ca.uhn.fhir.jpa.cqf.ruler.providers.FHIRPlanDefinitionResourceProvider;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.WebApplicationContext;
import javax.servlet.ServletException;
import java.util.Collection;
import java.util.List;
/**
* Created by Chris Schuler on 12/11/2016.
*/
public class BaseServlet extends RestfulServer {
@SuppressWarnings("unchecked")
@Override
protected void initialize() throws ServletException {
super.initialize();
FhirVersionEnum fhirVersion = FhirVersionEnum.DSTU3;
setFhirContext(new FhirContext(fhirVersion));
// Get the spring context from the web container (it's declared in web.xml)
WebApplicationContext myAppCtx = ContextLoaderListener.getCurrentWebApplicationContext();
String resourceProviderBeanName = "myResourceProvidersDstu3";
List<IResourceProvider> beans = myAppCtx.getBean(resourceProviderBeanName, List.class);
setResourceProviders(beans);
Object systemProvider = myAppCtx.getBean("mySystemProviderDstu3", JpaSystemProviderDstu3.class);
setPlainProviders(systemProvider);
IFhirSystemDao<Bundle, Meta> systemDao = myAppCtx.getBean("mySystemDaoDstu3", IFhirSystemDao.class);
JpaConformanceProviderDstu3 confProvider = new JpaConformanceProviderDstu3(this, systemDao,
myAppCtx.getBean(DaoConfig.class));
confProvider.setImplementationDescription("Measure and Opioid Processing Server");
setServerConformanceProvider(confProvider);
FhirContext ctx = getFhirContext();
ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator());
setDefaultPrettyPrint(true);
setDefaultResponseEncoding(EncodingEnum.JSON);
setPagingProvider(myAppCtx.getBean(DatabaseBackedPagingProvider.class));
/*
* Enable CORS
*/
// CorsConfiguration config = new CorsConfiguration();
// CorsInterceptor corsInterceptor = new CorsInterceptor(config);
// config.addAllowedHeader("Origin");
// config.addAllowedHeader("Accept");
// config.addAllowedHeader("X-Requested-With");
// config.addAllowedHeader("Content-Type");
// config.addAllowedHeader("Access-Control-Request-Method");
// config.addAllowedHeader("Access-Control-Request-Headers");
// config.addAllowedOrigin("*");
// config.addExposedHeader("Location");
// config.addExposedHeader("Content-Location");
// config.setAllowedMethods(Arrays.asList("GET","POST","PUT","DELETE","OPTIONS"));
// registerInterceptor(corsInterceptor);
/*
* Load interceptors for the server from Spring (these are defined in FhirServerConfig.java)
*/
Collection<IServerInterceptor> interceptorBeans = myAppCtx.getBeansOfType(IServerInterceptor.class).values();
for (IServerInterceptor interceptor : interceptorBeans) {
this.registerInterceptor(interceptor);
}
// Measure processing
FHIRMeasureResourceProvider measureProvider = new FHIRMeasureResourceProvider(getResourceProviders());
MeasureResourceProvider jpaMeasureProvider = (MeasureResourceProvider) getProvider("Measure");
measureProvider.setDao(jpaMeasureProvider.getDao());
measureProvider.setContext(jpaMeasureProvider.getContext());
// PlanDefinition processing
FHIRPlanDefinitionResourceProvider planDefProvider = new FHIRPlanDefinitionResourceProvider(getResourceProviders());
PlanDefinitionResourceProvider jpaPlanDefProvider =
(PlanDefinitionResourceProvider) getProvider("PlanDefinition");
planDefProvider.setDao(jpaPlanDefProvider.getDao());
planDefProvider.setContext(jpaPlanDefProvider.getContext());
// ActivityDefinition processing
FHIRActivityDefinitionResourceProvider actDefProvider = new FHIRActivityDefinitionResourceProvider(getResourceProviders());
ActivityDefinitionResourceProvider jpaActDefProvider =
(ActivityDefinitionResourceProvider) getProvider("ActivityDefinition");
actDefProvider.setDao(jpaActDefProvider.getDao());
actDefProvider.setContext(jpaActDefProvider.getContext());
try {
unregisterProvider(jpaMeasureProvider);
unregisterProvider(jpaPlanDefProvider);
unregisterProvider(jpaActDefProvider);
} catch (Exception e) {
throw new ServletException("Unable to unregister provider: " + e.getMessage());
}
registerProvider(measureProvider);
registerProvider(planDefProvider);
registerProvider(actDefProvider);
// Register the logging interceptor
LoggingInterceptor loggingInterceptor = new LoggingInterceptor();
this.registerInterceptor(loggingInterceptor);
// The SLF4j logger "test.accesslog" will receive the logging events
loggingInterceptor.setLoggerName("logging.accesslog");
// This is the format for each line. A number of substitution variables may
// be used here. See the JavaDoc for LoggingInterceptor for information on
// what is available.
loggingInterceptor.setMessageFormat("Source[${remoteAddr}] Operation[${operationType} ${idOrResourceName}] UA[${requestHeader.user-agent}] Params[${requestParameters}]");
//setServerAddressStrategy(new HardcodedServerAddressStrategy("http://mydomain.com/fhir/baseDstu2"));
//registerProvider(myAppCtx.getBean(TerminologyUploaderProviderDstu3.class));
}
public IResourceProvider getProvider(String name) {
for (IResourceProvider res : getResourceProviders()) {
if (res.getResourceType().getSimpleName().equals(name)) {
return res;
}
}
throw new IllegalArgumentException("This should never happen!");
}
}

View File

@ -0,0 +1,180 @@
package ca.uhn.fhir.jpa.cqf.ruler.servlet;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.rp.dstu3.LibraryResourceProvider;
import ca.uhn.fhir.model.primitive.IdDt;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.exceptions.FHIRException;
import ca.uhn.fhir.jpa.cqf.ruler.cds.*;
import ca.uhn.fhir.jpa.cqf.ruler.providers.FHIRPlanDefinitionResourceProvider;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
/**
* Created by Christopher Schuler on 5/1/2017.
*/
@WebServlet(name = "cds-services")
public class CdsServicesServlet extends BaseServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// validate that we are dealing with JSON
if (!request.getContentType().equals("application/json")) {
throw new ServletException(String.format("Invalid content type %s. Please use application/json.", request.getContentType()));
}
CdsHooksRequest cdsHooksRequest = new CdsHooksRequest(request.getReader());
CdsRequestProcessor processor = null;
String service = request.getPathInfo().replace("/", "");
// PlanDefinition must have the same id as the cds service
// For example, {BASE}/cds-services/cdc-opioid-guidance -> PlanDefinition ID = cds-opioid-guidance
PlanDefinition planDefinition =
((FHIRPlanDefinitionResourceProvider) getProvider("PlanDefinition"))
.getDao()
.read(new IdDt(service));
// Custom cds services
if (request.getRequestURL().toString().endsWith("cdc-opioid-guidance")) {
resolveMedicationPrescribePrefetch(cdsHooksRequest);
try {
processor = new OpioidGuidanceProcessor(cdsHooksRequest, planDefinition, (LibraryResourceProvider) getProvider("Library"));
} catch (FHIRException e) {
e.printStackTrace();
}
}
else {
// User-defined cds services
// These are limited - no user-defined data/terminology providers
switch (cdsHooksRequest.getHook()) {
case "medication-prescribe":
resolveMedicationPrescribePrefetch(cdsHooksRequest);
try {
processor = new MedicationPrescribeProcessor(cdsHooksRequest, planDefinition, (LibraryResourceProvider) getProvider("Library"));
} catch (FHIRException e) {
e.printStackTrace();
}
break;
case "order-review":
// resolveOrderReviewPrefetch(cdsHooksRequest);
// TODO - currently only works for ProcedureRequest orders
processor = new OrderReviewProcessor(cdsHooksRequest, planDefinition, (LibraryResourceProvider) getProvider("Library"));
break;
case "patient-view":
processor = new PatientViewProcessor(cdsHooksRequest, planDefinition, (LibraryResourceProvider) getProvider("Library"));
break;
}
}
if (processor == null) {
throw new ServletException("Invalid cds service");
}
response.getWriter().println(toJsonResponse(processor.process()));
}
// If the EHR did not provide the prefetch resources, fetch them
// Assuming EHR is using DSTU2 resources here...
// This is a big drag on performance.
public void resolveMedicationPrescribePrefetch(CdsHooksRequest cdsHooksRequest) {
if (cdsHooksRequest.getPrefetch().size() == 0) {
String searchUrl = String.format("MedicationOrder?patient=%s&status=active", cdsHooksRequest.getPatientId());
ca.uhn.fhir.model.dstu2.resource.Bundle postfetch = FhirContext.forDstu2()
.newRestfulGenericClient(cdsHooksRequest.getFhirServerEndpoint())
.search()
.byUrl(searchUrl)
.returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class)
.execute();
cdsHooksRequest.setPrefetch(postfetch, "medication");
}
}
// This is especially inefficient as the search must be done for each Request resource (and then converted to stu3):
// MedicationOrder -> MedicationRequest, DiagnosticOrder or DeviceUseRequest -> ProcedureRequest, SupplyRequest
public void resolveOrderReviewPrefetch(CdsHooksRequest cdsHooksRequest) {
// TODO - clean this up
if (cdsHooksRequest.getPrefetch().size() == 0) {
String searchUrl = String.format("MedicationOrder?patient=%s&encounter=%s", cdsHooksRequest.getPatientId(), cdsHooksRequest.getEncounterId());
ca.uhn.fhir.model.dstu2.resource.Bundle postfetch = FhirContext.forDstu2()
.newRestfulGenericClient(cdsHooksRequest.getFhirServerEndpoint())
.search()
.byUrl(searchUrl)
.returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class)
.execute();
cdsHooksRequest.setPrefetch(postfetch, "medication");
searchUrl = String.format("DiagnosticOrder?patient=%s&encounter=%s", cdsHooksRequest.getPatientId(), cdsHooksRequest.getEncounterId());
ca.uhn.fhir.model.dstu2.resource.Bundle diagnosticOrders = FhirContext.forDstu2()
.newRestfulGenericClient(cdsHooksRequest.getFhirServerEndpoint())
.search()
.byUrl(searchUrl)
.returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class)
.execute();
cdsHooksRequest.setPrefetch(diagnosticOrders, "diagnosticOrders");
searchUrl = String.format("DeviceUseRequest?patient=%s&encounter=%s", cdsHooksRequest.getPatientId(), cdsHooksRequest.getEncounterId());
ca.uhn.fhir.model.dstu2.resource.Bundle deviceUseRequests = FhirContext.forDstu2()
.newRestfulGenericClient(cdsHooksRequest.getFhirServerEndpoint())
.search()
.byUrl(searchUrl)
.returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class)
.execute();
cdsHooksRequest.setPrefetch(deviceUseRequests, "deviceUseRequests");
searchUrl = String.format("ProcedureRequest?patient=%s&encounter=%s", cdsHooksRequest.getPatientId(), cdsHooksRequest.getEncounterId());
ca.uhn.fhir.model.dstu2.resource.Bundle procedureRequests = FhirContext.forDstu2()
.newRestfulGenericClient(cdsHooksRequest.getFhirServerEndpoint())
.search()
.byUrl(searchUrl)
.returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class)
.execute();
cdsHooksRequest.setPrefetch(procedureRequests, "procedureRequests");
searchUrl = String.format("SupplyRequest?patient=%s&encounter=%s", cdsHooksRequest.getPatientId(), cdsHooksRequest.getEncounterId());
ca.uhn.fhir.model.dstu2.resource.Bundle supplyRequests = FhirContext.forDstu2()
.newRestfulGenericClient(cdsHooksRequest.getFhirServerEndpoint())
.search()
.byUrl(searchUrl)
.returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class)
.execute();
cdsHooksRequest.setPrefetch(supplyRequests, "supplyRequests");
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
if (!request.getRequestURL().toString().endsWith("cds-services")) {
throw new ServletException("This servlet is not configured to handle GET requests.");
}
CdsHooksHelper.DisplayDiscovery(response);
}
public String toJsonResponse(List<CdsCard> cards) {
JsonObject ret = new JsonObject();
JsonArray cardArray = new JsonArray();
for (CdsCard card : cards) {
cardArray.add(card.toJson());
}
ret.add("cards", cardArray);
Gson gson = new GsonBuilder().setPrettyPrinting().create();
return gson.toJson(ret);
}
}

View File

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns4:modelInfo name="OMTK" url="http://org.opencds/opioid-cds" targetQualifier="cdc" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:ns4="urn:hl7-org:elm-modelinfo:r1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
version="0.1.0">
<ns4:typeInfo xsi:type="ns4:ClassInfo" name="OMTK.MED_DOSE_FORM" baseType="System.Any" retrievable="true">
<ns4:element name="DOSE_FORM_RXCUI" type="System.Code"/>
<ns4:element name="DOSE_FORM_NAME" type="System.String"/>
<ns4:element name="UPDATE_DTM" type="System.DateTime"/>
</ns4:typeInfo>
<ns4:typeInfo xsi:type="ns4:ClassInfo" name="OMTK.MED_DOSE_FORM_GROUP" baseType="System.Any" retrievable="true">
<ns4:element name="DOSE_FORM_GROUP_RXCUI" type="System.Code"/>
<ns4:element name="DOSE_FORM_GROUP_NAME" type="System.String"/>
<ns4:element name="UPDATE_DTM" type="System.DateTime"/>
</ns4:typeInfo>
<ns4:typeInfo xsi:type="ns4:ClassInfo" name="OMTK.MED_DRUG" baseType="System.Any" retrievable="true">
<ns4:element name="DRUG_RXCUI" type="System.Code"/>
<ns4:element name="DRUG_TYPE" type="System.String"/>
<ns4:element name="DRUG_NAME" type="System.String"/>
<ns4:element name="UPDATE_DTM" type="System.DateTime"/>
</ns4:typeInfo>
<ns4:typeInfo xsi:type="ns4:ClassInfo" name="OMTK.MED_DRUG_DOSE_FORM" baseType="System.Any" retrievable="true">
<ns4:element name="DRUG_RXCUI" type="System.Code"/>
<ns4:element name="DOSE_FORM_RXCUI" type="System.Code"/>
<ns4:element name="UPDATE_DTM" type="System.DateTime"/>
</ns4:typeInfo>
<ns4:typeInfo xsi:type="ns4:ClassInfo" name="OMTK.MED_DRUG_DOSE_FORM_GROUP" baseType="System.Any" retrievable="true">
<ns4:element name="DRUG_RXCUI" type="System.Code"/>
<ns4:element name="DOSE_FORM_GROUP_RXCUI" type="System.Code"/>
<ns4:element name="UPDATE_DTM" type="System.DateTime"/>
</ns4:typeInfo>
<ns4:typeInfo xsi:type="ns4:ClassInfo" name="OMTK.MED_DRUG_WITH_INGREDIENT" baseType="System.Any" retrievable="true">
<ns4:element name="INGREDIENT_RXCUI" type="System.Code"/>
<ns4:element name="DRUG_RXCUI" type="System.Code"/>
<ns4:element name="UPDATE_DTM" type="System.DateTime"/>
</ns4:typeInfo>
<ns4:typeInfo xsi:type="ns4:ClassInfo" name="OMTK.MED_INGREDIENT" baseType="System.Any" retrievable="true">
<ns4:element name="INGREDIENT_RXCUI" type="System.Code"/>
<ns4:element name="INGREDIENT_NAME" type="System.String"/>
<ns4:element name="MANUALLY_ENTERED" type="System.Integer"/>
<ns4:element name="UPDATE_DTM" type="System.DateTime"/>
</ns4:typeInfo>
<ns4:typeInfo xsi:type="ns4:ClassInfo" name="OMTK.MED_INGREDIENT_FOR_SCDC" baseType="System.Any" retrievable="true">
<ns4:element name="SCDC_RXCUI" type="System.Code"/>
<ns4:element name="INGREDIENT_RXCUI" type="System.Code"/>
<ns4:element name="UPDATE_DTM" type="System.DateTime"/>
</ns4:typeInfo>
<ns4:typeInfo xsi:type="ns4:ClassInfo" name="OMTK.MED_INGREDIENT_TYPE" baseType="System.Any" retrievable="true">
<ns4:element name="INGREDIENT_RXCUI" type="System.Code"/>
<ns4:element name="INGREDIENT_TYPE" type="System.String"/>
<ns4:element name="UPDATE_DTM" type="System.DateTime"/>
</ns4:typeInfo>
<ns4:typeInfo xsi:type="ns4:ClassInfo" name="OMTK.MED_SCDC" baseType="System.Any" retrievable="true">
<ns4:element name="SCDC_RXCUI" type="System.Code"/>
<ns4:element name="SCDC_NAME" type="System.String"/>
<ns4:element name="STRENGTH" type="System.String"/>
<ns4:element name="STRENGTH_VALUE" type="System.Decimal"/>
<ns4:element name="STRENGTH_UNIT" type="System.String"/>
<ns4:element name="UPDATE_DTM" type="System.DateTime"/>
</ns4:typeInfo>
<ns4:typeInfo xsi:type="ns4:ClassInfo" name="OMTK.MED_SCDC_FOR_DRUG" baseType="System.Any" retrievable="true">
<ns4:element name="DRUG_RXCUI" type="System.Code"/>
<ns4:element name="SCDC_RXCUI" type="System.Code"/>
<ns4:element name="UPDATE_DTM" type="System.DateTime"/>
</ns4:typeInfo>
<ns4:typeInfo xsi:type="ns4:ClassInfo" name="OMTK.NON_SURGICAL_OPIOID_TO_INCLUDE" baseType="System.Any" retrievable="true">
<ns4:element name="DRUG_RXCUI" type="System.Code"/>
</ns4:typeInfo>
</ns4:modelInfo>

View File

@ -0,0 +1,34 @@
{
"resourceType": "CodeSystem",
"id": "2.16.840.1.113883.6.1",
"url": "http://loinc.org",
"status": "draft",
"content": "example",
"concept": [
{
"code": "17856-6",
"display": "Hemoglobin A1c/Hemoglobin.total in Blood by HPLC"
},
{
"code": "4548-4",
"display": "Hemoglobin A1c/Hemoglobin.total in Blood"
},
{
"code": "4549-2",
"display": "Hemoglobin A1c/Hemoglobin.total in Blood by Electrophoresis"
},
{
"code": "17856-6",
"display": "Hemoglobin A1c/Hemoglobin.total in Blood by HPLC"
},
{
"code": "4548-4",
"display": "Hemoglobin A1c/Hemoglobin.total in Blood"
},
{
"code": "4549-2",
"display": "Hemoglobin A1c/Hemoglobin.total in Blood by Electrophoresis"
}
]
}

View File

@ -0,0 +1,443 @@
{
"resourceType": "CodeSystem",
"id": "2.16.840.1.113883.6.103",
"url": "http://hl7.org/fhir/sid/icd-9-cm",
"version": "2013",
"status": "draft",
"content": "example",
"concept": [
{
"code": "250",
"display": "Diabetes mellitus without mention of complication, type II or unspecified type, not stated as uncontrolled"
},
{
"code": "250.01",
"display": "Diabetes mellitus without mention of complication, type I [juvenile type], not stated as uncontrolled"
},
{
"code": "250.02",
"display": "Diabetes mellitus without mention of complication, type II or unspecified type, uncontrolled"
},
{
"code": "250.03",
"display": "Diabetes mellitus without mention of complication, type I [juvenile type], uncontrolled"
},
{
"code": "250.1",
"display": "Diabetes with ketoacidosis, type II or unspecified type, not stated as uncontrolled"
},
{
"code": "250.11",
"display": "Diabetes with ketoacidosis, type I [juvenile type], not stated as uncontrolled"
},
{
"code": "250.12",
"display": "Diabetes with ketoacidosis, type II or unspecified type, uncontrolled"
},
{
"code": "250.13",
"display": "Diabetes with ketoacidosis, type I [juvenile type], uncontrolled"
},
{
"code": "250.2",
"display": "Diabetes with hyperosmolarity, type II or unspecified type, not stated as uncontrolled"
},
{
"code": "250.21",
"display": "Diabetes with hyperosmolarity, type I [juvenile type], not stated as uncontrolled"
},
{
"code": "250.22",
"display": "Diabetes with hyperosmolarity, type II or unspecified type, uncontrolled"
},
{
"code": "250.23",
"display": "Diabetes with hyperosmolarity, type I [juvenile type], uncontrolled"
},
{
"code": "250.3",
"display": "Diabetes with other coma, type II or unspecified type, not stated as uncontrolled"
},
{
"code": "250.31",
"display": "Diabetes with other coma, type I [juvenile type], not stated as uncontrolled"
},
{
"code": "250.32",
"display": "Diabetes with other coma, type II or unspecified type, uncontrolled"
},
{
"code": "250.33",
"display": "Diabetes with other coma, type I [juvenile type], uncontrolled"
},
{
"code": "250.4",
"display": "Diabetes with renal manifestations, type II or unspecified type, not stated as uncontrolled"
},
{
"code": "250.41",
"display": "Diabetes with renal manifestations, type I [juvenile type], not stated as uncontrolled"
},
{
"code": "250.42",
"display": "Diabetes with renal manifestations, type II or unspecified type, uncontrolled"
},
{
"code": "250.43",
"display": "Diabetes with renal manifestations, type I [juvenile type], uncontrolled"
},
{
"code": "250.5",
"display": "Diabetes with ophthalmic manifestations, type II or unspecified type, not stated as uncontrolled"
},
{
"code": "250.51",
"display": "Diabetes with ophthalmic manifestations, type I [juvenile type], not stated as uncontrolled"
},
{
"code": "250.52",
"display": "Diabetes with ophthalmic manifestations, type II or unspecified type, uncontrolled"
},
{
"code": "250.53",
"display": "Diabetes with ophthalmic manifestations, type I [juvenile type], uncontrolled"
},
{
"code": "250.6",
"display": "Diabetes with neurological manifestations, type II or unspecified type, not stated as uncontrolled"
},
{
"code": "250.61",
"display": "Diabetes with neurological manifestations, type I [juvenile type], not stated as uncontrolled"
},
{
"code": "250.62",
"display": "Diabetes with neurological manifestations, type II or unspecified type, uncontrolled"
},
{
"code": "250.63",
"display": "Diabetes with neurological manifestations, type I [juvenile type], uncontrolled"
},
{
"code": "250.7",
"display": "Diabetes with peripheral circulatory disorders, type II or unspecified type, not stated as uncontrolled"
},
{
"code": "250.71",
"display": "Diabetes with peripheral circulatory disorders, type I [juvenile type], not stated as uncontrolled"
},
{
"code": "250.72",
"display": "Diabetes with peripheral circulatory disorders, type II or unspecified type, uncontrolled"
},
{
"code": "250.73",
"display": "Diabetes with peripheral circulatory disorders, type I [juvenile type], uncontrolled"
},
{
"code": "250.8",
"display": "Diabetes with other specified manifestations, type II or unspecified type, not stated as uncontrolled"
},
{
"code": "250.81",
"display": "Diabetes with other specified manifestations, type I [juvenile type], not stated as uncontrolled"
},
{
"code": "250.82",
"display": "Diabetes with other specified manifestations, type II or unspecified type, uncontrolled"
},
{
"code": "250.83",
"display": "Diabetes with other specified manifestations, type I [juvenile type], uncontrolled"
},
{
"code": "250.9",
"display": "Diabetes with unspecified complication, type II or unspecified type, not stated as uncontrolled"
},
{
"code": "250.91",
"display": "Diabetes with unspecified complication, type I [juvenile type], not stated as uncontrolled"
},
{
"code": "250.92",
"display": "Diabetes with unspecified complication, type II or unspecified type, uncontrolled"
},
{
"code": "250.93",
"display": "Diabetes with unspecified complication, type I [juvenile type], uncontrolled"
},
{
"code": "357.2",
"display": "Polyneuropathy in diabetes"
},
{
"code": "362.01",
"display": "Background diabetic retinopathy"
},
{
"code": "362.02",
"display": "Proliferative diabetic retinopathy"
},
{
"code": "362.03",
"display": "Nonproliferative diabetic retinopathy NOS"
},
{
"code": "362.04",
"display": "Mild nonproliferative diabetic retinopathy"
},
{
"code": "362.05",
"display": "Moderate nonproliferative diabetic retinopathy"
},
{
"code": "362.06",
"display": "Severe nonproliferative diabetic retinopathy"
},
{
"code": "362.07",
"display": "Diabetic macular edema"
},
{
"code": "366.41",
"display": "Diabetic cataract"
},
{
"code": "648",
"display": "Diabetes mellitus of mother, complicating pregnancy, childbirth, or the puerperium, unspecified as to episode of care or not applicable"
},
{
"code": "648.01",
"display": "Diabetes mellitus of mother, complicating pregnancy, childbirth, or the puerperium, delivered, with or without mention of antepartum condition"
},
{
"code": "648.02",
"display": "Diabetes mellitus of mother, complicating pregnancy, childbirth, or the puerperium, delivered, with mention of postpartum complication"
},
{
"code": "648.03",
"display": "Diabetes mellitus of mother, complicating pregnancy, childbirth, or the puerperium, antepartum condition or complication"
},
{
"code": "648.04",
"display": "Diabetes mellitus of mother, complicating pregnancy, childbirth, or the puerperium, postpartum condition or complication"
},
{
"code": "250",
"display": "Diabetes mellitus without mention of complication, type II or unspecified type, not stated as uncontrolled"
},
{
"code": "250.01",
"display": "Diabetes mellitus without mention of complication, type I [juvenile type], not stated as uncontrolled"
},
{
"code": "250.02",
"display": "Diabetes mellitus without mention of complication, type II or unspecified type, uncontrolled"
},
{
"code": "250.03",
"display": "Diabetes mellitus without mention of complication, type I [juvenile type], uncontrolled"
},
{
"code": "250.1",
"display": "Diabetes with ketoacidosis, type II or unspecified type, not stated as uncontrolled"
},
{
"code": "250.11",
"display": "Diabetes with ketoacidosis, type I [juvenile type], not stated as uncontrolled"
},
{
"code": "250.12",
"display": "Diabetes with ketoacidosis, type II or unspecified type, uncontrolled"
},
{
"code": "250.13",
"display": "Diabetes with ketoacidosis, type I [juvenile type], uncontrolled"
},
{
"code": "250.2",
"display": "Diabetes with hyperosmolarity, type II or unspecified type, not stated as uncontrolled"
},
{
"code": "250.21",
"display": "Diabetes with hyperosmolarity, type I [juvenile type], not stated as uncontrolled"
},
{
"code": "250.22",
"display": "Diabetes with hyperosmolarity, type II or unspecified type, uncontrolled"
},
{
"code": "250.23",
"display": "Diabetes with hyperosmolarity, type I [juvenile type], uncontrolled"
},
{
"code": "250.3",
"display": "Diabetes with other coma, type II or unspecified type, not stated as uncontrolled"
},
{
"code": "250.31",
"display": "Diabetes with other coma, type I [juvenile type], not stated as uncontrolled"
},
{
"code": "250.32",
"display": "Diabetes with other coma, type II or unspecified type, uncontrolled"
},
{
"code": "250.33",
"display": "Diabetes with other coma, type I [juvenile type], uncontrolled"
},
{
"code": "250.4",
"display": "Diabetes with renal manifestations, type II or unspecified type, not stated as uncontrolled"
},
{
"code": "250.41",
"display": "Diabetes with renal manifestations, type I [juvenile type], not stated as uncontrolled"
},
{
"code": "250.42",
"display": "Diabetes with renal manifestations, type II or unspecified type, uncontrolled"
},
{
"code": "250.43",
"display": "Diabetes with renal manifestations, type I [juvenile type], uncontrolled"
},
{
"code": "250.5",
"display": "Diabetes with ophthalmic manifestations, type II or unspecified type, not stated as uncontrolled"
},
{
"code": "250.51",
"display": "Diabetes with ophthalmic manifestations, type I [juvenile type], not stated as uncontrolled"
},
{
"code": "250.52",
"display": "Diabetes with ophthalmic manifestations, type II or unspecified type, uncontrolled"
},
{
"code": "250.53",
"display": "Diabetes with ophthalmic manifestations, type I [juvenile type], uncontrolled"
},
{
"code": "250.6",
"display": "Diabetes with neurological manifestations, type II or unspecified type, not stated as uncontrolled"
},
{
"code": "250.61",
"display": "Diabetes with neurological manifestations, type I [juvenile type], not stated as uncontrolled"
},
{
"code": "250.62",
"display": "Diabetes with neurological manifestations, type II or unspecified type, uncontrolled"
},
{
"code": "250.63",
"display": "Diabetes with neurological manifestations, type I [juvenile type], uncontrolled"
},
{
"code": "250.7",
"display": "Diabetes with peripheral circulatory disorders, type II or unspecified type, not stated as uncontrolled"
},
{
"code": "250.71",
"display": "Diabetes with peripheral circulatory disorders, type I [juvenile type], not stated as uncontrolled"
},
{
"code": "250.72",
"display": "Diabetes with peripheral circulatory disorders, type II or unspecified type, uncontrolled"
},
{
"code": "250.73",
"display": "Diabetes with peripheral circulatory disorders, type I [juvenile type], uncontrolled"
},
{
"code": "250.8",
"display": "Diabetes with other specified manifestations, type II or unspecified type, not stated as uncontrolled"
},
{
"code": "250.81",
"display": "Diabetes with other specified manifestations, type I [juvenile type], not stated as uncontrolled"
},
{
"code": "250.82",
"display": "Diabetes with other specified manifestations, type II or unspecified type, uncontrolled"
},
{
"code": "250.83",
"display": "Diabetes with other specified manifestations, type I [juvenile type], uncontrolled"
},
{
"code": "250.9",
"display": "Diabetes with unspecified complication, type II or unspecified type, not stated as uncontrolled"
},
{
"code": "250.91",
"display": "Diabetes with unspecified complication, type I [juvenile type], not stated as uncontrolled"
},
{
"code": "250.92",
"display": "Diabetes with unspecified complication, type II or unspecified type, uncontrolled"
},
{
"code": "250.93",
"display": "Diabetes with unspecified complication, type I [juvenile type], uncontrolled"
},
{
"code": "357.2",
"display": "Polyneuropathy in diabetes"
},
{
"code": "362.01",
"display": "Background diabetic retinopathy"
},
{
"code": "362.02",
"display": "Proliferative diabetic retinopathy"
},
{
"code": "362.03",
"display": "Nonproliferative diabetic retinopathy NOS"
},
{
"code": "362.04",
"display": "Mild nonproliferative diabetic retinopathy"
},
{
"code": "362.05",
"display": "Moderate nonproliferative diabetic retinopathy"
},
{
"code": "362.06",
"display": "Severe nonproliferative diabetic retinopathy"
},
{
"code": "362.07",
"display": "Diabetic macular edema"
},
{
"code": "366.41",
"display": "Diabetic cataract"
},
{
"code": "648",
"display": "Diabetes mellitus of mother, complicating pregnancy, childbirth, or the puerperium, unspecified as to episode of care or not applicable"
},
{
"code": "648.01",
"display": "Diabetes mellitus of mother, complicating pregnancy, childbirth, or the puerperium, delivered, with or without mention of antepartum condition"
},
{
"code": "648.02",
"display": "Diabetes mellitus of mother, complicating pregnancy, childbirth, or the puerperium, delivered, with mention of postpartum complication"
},
{
"code": "648.03",
"display": "Diabetes mellitus of mother, complicating pregnancy, childbirth, or the puerperium, antepartum condition or complication"
},
{
"code": "648.04",
"display": "Diabetes mellitus of mother, complicating pregnancy, childbirth, or the puerperium, postpartum condition or complication"
}
]
}

View File

@ -0,0 +1,291 @@
{
"resourceType": "CodeSystem",
"id": "2.16.840.1.113883.6.96",
"url": "http://snomed.info/sct",
"version": "2016-09",
"status": "draft",
"content": "example",
"concept": [
{
"code": "4783006",
"display": "Maternal diabetes mellitus with hypoglycemia affecting fetus OR newborn (disorder)"
},
{
"code": "9859006",
"display": "Type 2 diabetes mellitus with acanthosis nigricans (disorder)"
},
{
"code": "23045005",
"display": "Insulin dependent diabetes mellitus type IA (disorder)"
},
{
"code": "28032008",
"display": "Insulin dependent diabetes mellitus type IB (disorder)"
},
{
"code": "44054006",
"display": "Diabetes mellitus type 2 (disorder)"
},
{
"code": "46635009",
"display": "Diabetes mellitus type 1 (disorder)"
},
{
"code": "75682002",
"display": "Diabetes mellitus caused by insulin receptor antibodies (disorder)"
},
{
"code": "76751001",
"display": "Diabetes mellitus in mother complicating pregnancy, childbirth AND/OR puerperium (disorder)"
},
{
"code": "81531005",
"display": "Diabetes mellitus type 2 in obese (disorder)"
},
{
"code": "190330002",
"display": "Type 1 diabetes mellitus with hyperosmolar coma (disorder)"
},
{
"code": "190331003",
"display": "Type 2 diabetes mellitus with hyperosmolar coma (disorder)"
},
{
"code": "190368000",
"display": "Type I diabetes mellitus with ulcer (disorder)"
},
{
"code": "190369008",
"display": "Type I diabetes mellitus with gangrene (disorder)"
},
{
"code": "190372001",
"display": "Type I diabetes mellitus maturity onset (disorder)"
},
{
"code": "190389009",
"display": "Type II diabetes mellitus with ulcer (disorder)"
},
{
"code": "190390000",
"display": "Type II diabetes mellitus with gangrene (disorder)"
},
{
"code": "199223000",
"display": "Diabetes mellitus during pregnancy, childbirth and the puerperium (disorder)"
},
{
"code": "199225007",
"display": "Diabetes mellitus during pregnancy - baby delivered (disorder)"
},
{
"code": "199226008",
"display": "Diabetes mellitus in the puerperium - baby delivered during current episode of care (disorder)"
},
{
"code": "199227004",
"display": "Diabetes mellitus during pregnancy - baby not yet delivered (disorder)"
},
{
"code": "199228009",
"display": "Diabetes mellitus in the puerperium - baby delivered during previous episode of care (disorder)"
},
{
"code": "199229001",
"display": "Pre-existing type 1 diabetes mellitus (disorder)"
},
{
"code": "199230006",
"display": "Pre-existing type 2 diabetes mellitus (disorder)"
},
{
"code": "237599002",
"display": "Insulin treated type 2 diabetes mellitus (disorder)"
},
{
"code": "237618001",
"display": "Insulin-dependent diabetes mellitus secretory diarrhea syndrome (disorder)"
},
{
"code": "237627000",
"display": "Pregnancy and type 2 diabetes mellitus (disorder)"
},
{
"code": "313435000",
"display": "Type I diabetes mellitus without complication (disorder)"
},
{
"code": "313436004",
"display": "Type II diabetes mellitus without complication (disorder)"
},
{
"code": "314771006",
"display": "Type I diabetes mellitus with hypoglycemic coma (disorder)"
},
{
"code": "314772004",
"display": "Type II diabetes mellitus with hypoglycemic coma (disorder)"
},
{
"code": "314893005",
"display": "Type I diabetes mellitus with arthropathy (disorder)"
},
{
"code": "314902007",
"display": "Type II diabetes mellitus with peripheral angiopathy (disorder)"
},
{
"code": "314903002",
"display": "Type II diabetes mellitus with arthropathy (disorder)"
},
{
"code": "314904008",
"display": "Type II diabetes mellitus with neuropathic arthropathy (disorder)"
},
{
"code": "359642000",
"display": "Diabetes mellitus type 2 in nonobese (disorder)"
},
{
"code": "4783006",
"display": "Maternal diabetes mellitus with hypoglycemia affecting fetus OR newborn (disorder)"
},
{
"code": "9859006",
"display": "Type 2 diabetes mellitus with acanthosis nigricans (disorder)"
},
{
"code": "23045005",
"display": "Insulin dependent diabetes mellitus type IA (disorder)"
},
{
"code": "28032008",
"display": "Insulin dependent diabetes mellitus type IB (disorder)"
},
{
"code": "44054006",
"display": "Diabetes mellitus type 2 (disorder)"
},
{
"code": "46635009",
"display": "Diabetes mellitus type 1 (disorder)"
},
{
"code": "75682002",
"display": "Diabetes mellitus caused by insulin receptor antibodies (disorder)"
},
{
"code": "76751001",
"display": "Diabetes mellitus in mother complicating pregnancy, childbirth AND/OR puerperium (disorder)"
},
{
"code": "81531005",
"display": "Diabetes mellitus type 2 in obese (disorder)"
},
{
"code": "190330002",
"display": "Type 1 diabetes mellitus with hyperosmolar coma (disorder)"
},
{
"code": "190331003",
"display": "Type 2 diabetes mellitus with hyperosmolar coma (disorder)"
},
{
"code": "190368000",
"display": "Type I diabetes mellitus with ulcer (disorder)"
},
{
"code": "190369008",
"display": "Type I diabetes mellitus with gangrene (disorder)"
},
{
"code": "190372001",
"display": "Type I diabetes mellitus maturity onset (disorder)"
},
{
"code": "190389009",
"display": "Type II diabetes mellitus with ulcer (disorder)"
},
{
"code": "190390000",
"display": "Type II diabetes mellitus with gangrene (disorder)"
},
{
"code": "199223000",
"display": "Diabetes mellitus during pregnancy, childbirth and the puerperium (disorder)"
},
{
"code": "199225007",
"display": "Diabetes mellitus during pregnancy - baby delivered (disorder)"
},
{
"code": "199226008",
"display": "Diabetes mellitus in the puerperium - baby delivered during current episode of care (disorder)"
},
{
"code": "199227004",
"display": "Diabetes mellitus during pregnancy - baby not yet delivered (disorder)"
},
{
"code": "199228009",
"display": "Diabetes mellitus in the puerperium - baby delivered during previous episode of care (disorder)"
},
{
"code": "199229001",
"display": "Pre-existing type 1 diabetes mellitus (disorder)"
},
{
"code": "199230006",
"display": "Pre-existing type 2 diabetes mellitus (disorder)"
},
{
"code": "237599002",
"display": "Insulin treated type 2 diabetes mellitus (disorder)"
},
{
"code": "237618001",
"display": "Insulin-dependent diabetes mellitus secretory diarrhea syndrome (disorder)"
},
{
"code": "237627000",
"display": "Pregnancy and type 2 diabetes mellitus (disorder)"
},
{
"code": "313435000",
"display": "Type I diabetes mellitus without complication (disorder)"
},
{
"code": "313436004",
"display": "Type II diabetes mellitus without complication (disorder)"
},
{
"code": "314771006",
"display": "Type I diabetes mellitus with hypoglycemic coma (disorder)"
},
{
"code": "314772004",
"display": "Type II diabetes mellitus with hypoglycemic coma (disorder)"
},
{
"code": "314893005",
"display": "Type I diabetes mellitus with arthropathy (disorder)"
},
{
"code": "314902007",
"display": "Type II diabetes mellitus with peripheral angiopathy (disorder)"
},
{
"code": "314903002",
"display": "Type II diabetes mellitus with arthropathy (disorder)"
},
{
"code": "314904008",
"display": "Type II diabetes mellitus with neuropathic arthropathy (disorder)"
},
{
"code": "359642000",
"display": "Diabetes mellitus type 2 in nonobese (disorder)"
}
]
}

View File

@ -0,0 +1,16 @@
<configuration scan="true" scanPeriod="30 seconds">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} [%file:%line] %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>

View File

@ -0,0 +1,108 @@
# Resource Loading
## Methods
### HTTP Client
There are 2 different methods of uploading resources using an HTTP client.
1. PUT or POST a single resource
PUT method is used to create a new resource with a specified ID or update an existing resource.
PUT [base]/baseDstu3/Practitioner/prac-123
```
{
"resourceType": "Practitioner",
"id": "prac-123",
"identifier": [
{
"system": "http://clinfhir.com/fhir/NamingSystem/practitioner",
"value": "z1z1kXlcn3bhaZRsg7izSA1PYZm1"
}
],
"telecom": [
{
"system": "email",
"value": "sruthi.v@collabnotes.com"
}
]
}
```
Successful response
```
{
"resourceType": "OperationOutcome",
"issue": [
{
"severity": "information",
"code": "informational",
"diagnostics": "Successfully created resource \"Practitioner/prac-123/_history/1\" in 32ms"
}
]
}
```
If the request results in an error, the "severity" will be specified as "error" and a message will be given in the "diagnostics" value field
POST method is used to create a new resource with a generated ID.
POST [base]/baseDstu3/Practitioner
```
{
"resourceType": "Practitioner",
"identifier": [
{
"system": "http://clinfhir.com/fhir/NamingSystem/practitioner",
"value": "z1z1kXlcn3bhaZRsg7izSA1PYZm1"
}
],
"telecom": [
{
"system": "email",
"value": "sruthi.v@collabnotes.com"
}
]
}
```
The response will be the same as the PUT method.
2. POST a transaction Bundle
The [transaction](http://hl7.org/implement/standards/fhir/http.html#transaction) operation loads all the resources within a transaction Bundle.
POST [base]/baseDstu3
```
{
"resourceType": "Bundle",
"id": "example-transaction",
"type": "transaction",
"entry": [
{
"resource": {
...
},
"request": {
"method": "PUT",
"url": "[base]/baseDstu3/Resource/ResourceID"
}
},
...
]
}
```
The response will be a Bundle containing the status and location for each uploaded resource if successful or an OperationOutcome if there were errors.
```
{
"resourceType": "Bundle",
"id": "...",
"type": "transaction-response",
"entry": [
{
"response": {
"status": "201 Created",
"location": "Resource/ResourceID/_history/1",
...
},
...
}
]
}
```

View File

@ -0,0 +1,99 @@
{
"resourceType": "ActivityDefinition",
"id": "citalopramPrescription",
"contained": [
{
"resourceType": "Medication",
"id": "citalopramMedication",
"code": {
"coding": [
{
"system": "http://www.nlm.nih.gov/research/umls/rxnorm",
"code": "200371"
}
],
"text": "citalopram"
},
"form": {
"coding": [
{
"system": "http://snomed.info/sct",
"code": "385055001",
"display": "Tablet dose form"
}
],
"text": "Tablet dose form"
},
"ingredient": [
{
"itemReference": {
"reference": "#citalopramSubstance"
},
"amount": {
"numerator": {
"value": 20,
"unit": "mg"
},
"denominator": {
"value": 1,
"unit": "{tbl}"
}
}
}
]
},
{
"resourceType": "Substance",
"id": "citalopramSubstance",
"code": {
"coding": [
{
"system": "http://www.nlm.nih.gov/research/umls/rxnorm",
"code": "2556"
}
],
"text": "citalopram"
}
}
],
"status": "draft",
"category": "drug",
"productReference": {
"reference": "#citalopramMedication"
},
"dosageInstruction": [
{
"text": "1 tablet oral 1 time daily",
"timing": {
"repeat": {
"frequency": 1,
"period": 1,
"periodUnit": "d"
}
},
"route": {
"coding": [
{
"code": "26643006",
"display": "Oral route (qualifier value)"
}
],
"text": "Oral route (qualifier value)"
},
"doseQuantity": {
"value": 1,
"unit": "{tbl}"
}
}
],
"dynamicValue": [
{
"path": "dispenseRequest.numberOfRepeatsAllowed",
"expression": "3"
},
{
"path": "dispenseRequest.quantity",
"expression": "30 '{tbl}'"
}
]
}

View File

@ -0,0 +1,31 @@
{
"resourceType": "ActivityDefinition",
"id": "example",
"status": "draft",
"description": "refer to primary care mental-health integrated care program for evaluation and treatment of mental health conditions now",
"category": "referral",
"code": {
"coding": [
{
"system": "http://snomed.info/sct",
"code": "306206005"
}
],
"text": "Referral to service (procedure)"
},
"timingTiming": {
"event": [
{
"extension": [
{
"url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression",
"valueString": "Now()"
}
]
}
]
},
"participantType": [
"practitioner"
]
}

View File

@ -0,0 +1,33 @@
{
"resourceType": "ActivityDefinition",
"id": "serum-zika-dengue-virus-igm",
"url": "http://example.org/ActivityDefinition/serum-zika-dengue-virus-igm",
"status": "draft",
"description": "Order Serum Zika and Dengue Virus IgM",
"relatedArtifact": [
{
"type": "documentation",
"display": "Explanation of diagnostic tests for Zika virus and which to use based on the patients clinical and exposure history.",
"url": "http://www.cdc.gov/zika/hc-providers/diagnostic.html"
}
],
"category": "diagnostic",
"code": {
"text": "Serum Zika and Dengue Virus IgM"
},
"timingTiming": {
"event": [
{
"extension": [
{
"url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression",
"valueString": "Now()"
}
]
}
]
},
"participantType": [
"practitioner"
]
}

View File

@ -0,0 +1,38 @@
{
"resourceType": "ActivityDefinition",
"id": "provide-mosquito-prevention-advice",
"url": "http://example.org/ActivityDefinition/provide-mosquito-prevention-advice",
"status": "draft",
"description": "Provide mosquito prevention advice",
"relatedArtifact": [
{
"type": "documentation",
"display": "Advice for patients about how to avoid Mosquito bites.",
"url": "http://www.cdc.gov/zika/prevention/index.html"
},
{
"type": "documentation",
"display": "Advice for patients about which mosquito repellents are effective and safe to use in pregnancy. [DEET, IF3535 and Picardin are safe during]",
"url": "https://www.epa.gov/insect-repellents/find-insect-repellent-right-you"
}
],
"category": "communication",
"code": {
"text": "Provide Mosquito Prevention Advice"
},
"timingTiming": {
"event": [
{
"extension": [
{
"url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression",
"valueString": "Now()"
}
]
}
]
},
"participantType": [
"practitioner"
]
}

View File

@ -0,0 +1,31 @@
{
"resourceType": "ActivityDefinition",
"id": "administer-zika-virus-exposure-assessment",
"url": "http://example.org/ActivityDefinition/administer-zika-virus-exposure-assessment",
"status": "draft",
"description": "Administer Zika Virus Exposure Assessment",
"category": "procedure",
"code": {
"coding": [
{
"system": "http://example.org/questionnaires",
"code": "zika-virus-exposure-assessment"
}
]
},
"timingTiming": {
"event": [
{
"extension": [
{
"url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression",
"valueString": "Now()"
}
]
}
]
},
"participantType": [
"practitioner"
]
}

View File

@ -0,0 +1,8 @@
{
"resourceType": "GuidanceResponse",
"id": "example",
"module": {
"reference": "ServiceDefinition/example"
},
"status": "success"
}

View File

@ -0,0 +1,161 @@
{
"resourceType": "Library",
"id": "library-cms146-example",
"identifier": [
{
"use": "official",
"value": "CMS146"
}
],
"version": "2.0.0",
"title": "Appropriate Testing for Children with Pharyngitis",
"type": {
"coding": [
{
"code": "logic-library"
}
]
},
"status": "draft",
"date": "2015-07-22",
"description": "Logic for CMS 146: Appropriate Testing for Children with Pharyngitis",
"relatedArtifact": [
{
"type": "depends-on",
"resource": {
"reference": "Library/library-quick-model-definition"
}
}
],
"dataRequirement": [
{
"type": "Condition",
"codeFilter": [
{
"path": "code",
"valueSetString": "Other Female Reproductive Conditions"
}
]
},
{
"type": "Patient"
},
{
"type": "Condition",
"codeFilter": [
{
"path": "category",
"valueCode": [
"diagnosis"
]
},
{
"path": "clinicalStatus",
"valueCode": [
"confirmed"
]
},
{
"path": "code",
"valueSetString": "2.16.840.1.113883.3.464.1003.102.12.1011"
}
]
},
{
"type": "Condition",
"codeFilter": [
{
"path": "category",
"valueCode": [
"diagnosis"
]
},
{
"path": "clinicalStatus",
"valueCode": [
"confirmed"
]
},
{
"path": "code",
"valueSetString": "2.16.840.1.113883.3.464.1003.102.12.1012"
}
]
},
{
"type": "Encounter",
"codeFilter": [
{
"path": "status",
"valueCode": [
"finished"
]
},
{
"path": "class",
"valueCode": [
"ambulatory"
]
},
{
"path": "type",
"valueSetString": "2.16.840.1.113883.3.464.1003.101.12.1061"
}
]
},
{
"type": "DiagnosticReport",
"codeFilter": [
{
"path": "diagnosis",
"valueSetString": "2.16.840.1.113883.3.464.1003.198.12.1012"
}
]
},
{
"type": "Medication",
"codeFilter": [
{
"path": "code",
"valueSetString": "2.16.840.1.113883.3.464.1003.196.12.1001"
}
]
},
{
"type": "MedicationRequest",
"codeFilter": [
{
"path": "status",
"valueCode": [
"active"
]
},
{
"path": "medication.code",
"valueSetString": "2.16.840.1.113883.3.464.1003.196.12.1001"
}
]
},
{
"type": "MedicationStatement",
"codeFilter": [
{
"path": "status",
"valueCode": [
"completed"
]
},
{
"path": "medication.code",
"valueSetString": "2.16.840.1.113883.3.464.1003.196.12.1001"
}
]
}
],
"content": [
{
"contentType": "text/cql",
"url": "http://cqlrepository.org/CMS146.cql"
}
]
}

View File

@ -0,0 +1,52 @@
{
"resourceType": "Library",
"id": "example",
"identifier": [
{
"use": "official",
"value": "ChalmydiaScreening_Common"
}
],
"version": "2.0.0",
"title": "Chlamydia Screening Common Library",
"type": {
"coding": [
{
"code": "logic-library"
}
]
},
"status": "draft",
"date": "2015-07-22",
"description": "Common Logic for adherence to Chlamydia Screening guidelines",
"topic": [
{
"text": "Chlamydia Screening"
}
],
"relatedArtifact": [
{
"type": "depends-on",
"resource": {
"reference": "Library/library-quick-model-definition"
}
}
],
"dataRequirement": [
{
"type": "Condition",
"codeFilter": [
{
"path": "code",
"valueSetString": "Other Female Reproductive Conditions"
}
]
}
],
"content": [
{
"contentType": "text/cql",
"url": "http://cqlrepository.org/ChlamydiaScreening_Common.cql"
}
]
}

View File

@ -0,0 +1,53 @@
{
"resourceType": "Library",
"id": "library-exclusive-breastfeeding-cds-logic",
"identifier": [
{
"use": "official",
"value": "Exclusive_Breastfeeding_CDS_Logic"
}
],
"version": "1.0.0",
"title": "Exclusive Breastfeeding CDS Logic",
"type": {
"coding": [
{
"code": "logic-library"
}
]
},
"status": "active",
"experimental": true,
"date": "2016-03-08",
"description": "Decision support logic for improving outcomes for exclusive breastmilk feeding of newborns",
"topic": [
{
"text": "Exclusive Breastfeeding"
}
],
"relatedArtifact": [
{
"type": "depends-on",
"resource": {
"reference": "Library/library-quick-model-definition"
}
}
],
"dataRequirement": [
{
"type": "Condition",
"codeFilter": [
{
"path": "code",
"valueSetString": "Single Live Birth"
}
]
}
],
"content": [
{
"contentType": "text/cql",
"url": "http://cqlrepository.org/CMS9v4_CDS.cql"
}
]
}

View File

@ -0,0 +1,53 @@
{
"resourceType": "Library",
"id": "library-exclusive-breastfeeding-cqm-logic",
"identifier": [
{
"use": "official",
"value": "Exclusive_Breastfeeding_CQM_Logic"
}
],
"version": "1.0.0",
"title": "Exclusive Breastfeeding CQM Logic",
"type": {
"coding": [
{
"code": "logic-library"
}
]
},
"status": "active",
"experimental": true,
"date": "2016-03-08",
"description": "Quality measure logic for measuring outcomes for exclusive breastmilk feeding of newborns",
"topic": [
{
"text": "Exclusive Breastfeeding"
}
],
"relatedArtifact": [
{
"type": "depends-on",
"resource": {
"reference": "Library/library-quick-model-definition"
}
}
],
"dataRequirement": [
{
"type": "Condition",
"codeFilter": [
{
"path": "code",
"valueSetString": "Single Live Birth"
}
]
}
],
"content": [
{
"contentType": "text/cql",
"url": "http://cqlrepository.org/CMS9v4_CQM.cql"
}
]
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,33 @@
{
"resourceType": "Library",
"id": "library-fhir-model-definition",
"identifier": [
{
"use": "official",
"value": "FHIR"
}
],
"version": "1.9.0",
"title": "FHIR Model Definition",
"type": {
"coding": [
{
"code": "model-definition"
}
]
},
"status": "draft",
"date": "2016-07-08",
"description": "Model definition for the FHIR Model",
"topic": [
{
"text": "FHIR"
}
],
"content": [
{
"contentType": "application/xml",
"url": "http://cqlrepository.org/fhirmodel-modelinfo.xml"
}
]
}

View File

@ -0,0 +1,33 @@
{
"resourceType": "Library",
"id": "library-quick-model-definition",
"identifier": [
{
"use": "official",
"value": "QUICK"
}
],
"version": "1.0.0",
"title": "QUICK Model Definition",
"type": {
"coding": [
{
"code": "model-definition"
}
]
},
"status": "draft",
"date": "2016-07-08",
"description": "Model definition for the QUICK Logical Model",
"topic": [
{
"text": "QUICK"
}
],
"content": [
{
"contentType": "application/xml",
"url": "http://cqlrepository.org/quick-modelinfo.xml"
}
]
}

View File

@ -0,0 +1,95 @@
{
"resourceType": "Library",
"id": "mmi-suiciderisk-orderset-logic",
"identifier": [
{
"use": "official",
"value": "SuicideRiskLogic"
}
],
"version": "1.0.0",
"title": "Suicide Risk Order Set Logic",
"type": {
"coding": [
{
"code": "logic-library"
}
]
},
"status": "draft",
"date": "2015-07-22",
"description": "Logic for Suicide Risk Order Sets",
"topic": [
{
"text": "Suicide Risk Order Set Logic"
}
],
"relatedArtifact": [
{
"type": "depends-on",
"resource": {
"reference": "Library/library-fhir-model-definition"
}
},
{
"type": "depends-on",
"resource": {
"reference": "Library/library-fhir-helpers",
"display": "FHIRHelpers"
}
},
{
"type": "depends-on",
"resource": {
"reference": "CodeSystem/npi-taxonomy"
}
},
{
"type": "depends-on",
"resource": {
"reference": "ValueSet/1.2.3.4.5",
"display": "Suicide Risk Assessment"
}
}
],
"parameter": [
{
"name": "Patient",
"use": "in",
"min": 1,
"max": "1",
"type": "Patient"
},
{
"name": "Encounter",
"use": "in",
"min": 1,
"max": "1",
"type": "Encounter"
},
{
"name": "Practitioner",
"use": "in",
"min": 1,
"max": "1",
"type": "Practitioner"
}
],
"dataRequirement": [
{
"type": "RiskAssessment",
"codeFilter": [
{
"path": "code",
"valueSetString": "Suicide Risk Assessment"
}
]
}
],
"content": [
{
"contentType": "text/cql",
"url": "library-mmi-suiciderisk-orderset-logic-content.cql"
}
]
}

View File

@ -0,0 +1,101 @@
{
"resourceType": "Measure",
"id": "measure-exclusive-breastfeeding",
"identifier": [
{
"use": "official",
"value": "exclusive-breastfeeding-measure"
}
],
"version": "1.0.0",
"title": "Exclusive Breastfeeding Measure",
"status": "active",
"date": "2015-03-08",
"description": "Exclusive breastfeeding measure of outcomes for exclusive breastmilk feeding of newborns.",
"purpose": "Exclusive breast milk feeding for the first 6 months of neonatal life can result in numerous long-term health benefits for both mother and newborn and is recommended by a number of national and international organizations. Evidence suggests that the prenatal and intrapartum period is critical for the success of exclusive (or any) breast feeding. Therefore, it is recommended that newborns are fed breast milk only from birth to discharge.",
"topic": [
{
"text": "Exclusive Breastfeeding"
}
],
"library": [
{
"reference": "Library/library-exclusive-breastfeeding-cqm-logic"
}
],
"type": [
"outcome"
],
"group": [
{
"identifier": {
"value": "Population Group 1"
},
"population": [
{
"type": "initial-population",
"identifier": {
"value": "initial-population-1-identifier"
},
"criteria": "InitialPopulation1"
},
{
"type": "denominator",
"identifier": {
"value": "denominator-1-identifier"
},
"criteria": "Denominator1"
},
{
"type": "denominator-exclusion",
"identifier": {
"value": "denominator-exclusions-1-identifier"
},
"criteria": "DenominatorExclusions1"
},
{
"type": "numerator",
"identifier": {
"value": "numerator-1-identifier"
},
"criteria": "Numerator1"
}
]
},
{
"identifier": {
"value": "Population Group 2"
},
"population": [
{
"type": "initial-population",
"identifier": {
"value": "initial-population-2-identifier"
},
"criteria": "InitialPopulation2"
},
{
"type": "denominator",
"identifier": {
"value": "denominator-2-identifier"
},
"criteria": "Denominator2"
},
{
"type": "denominator-exclusion",
"identifier": {
"value": "denominator-exclusions-2-identifier"
},
"criteria": "DenominatorExclusions2"
},
{
"type": "numerator",
"identifier": {
"value": "numerator-2-identifier"
},
"criteria": "Numerator2"
}
]
}
]
}

View File

@ -0,0 +1,112 @@
{
"resourceType": "Measure",
"id": "measure-cms146-example",
"identifier": [
{
"use": "official",
"system": "http://hl7.org/fhir/cqi/ecqm/Measure/Identifier/cms",
"value": "146"
},
{
"use": "official",
"system": "http://hl7.org/fhir/cqi/ecqm/Measure/Identifier/nqf",
"value": "0002"
}
],
"version": "1.0.0",
"title": "Appropriate Testing for Children with Pharyngitis",
"status": "active",
"experimental": true,
"description": "Percentage of children 2-18 years of age who were diagnosed with pharyngitis, ordered an antibiotic and received a group A streptococcus (strep) test for the episode.",
"purpose": "The Infectious Diseases Society of America (IDSA) \"recommends swabbing the throat and testing for GAS pharyngitis by rapid antigen detection test (RADT) and/or culture because the clinical features alone do not reliably discriminate between GAS and viral pharyngitis except when overt viral features like rhinorrhea, cough, oral ulcers, and/or hoarseness are present\"",
"topic": [
{
"coding": [
{
"system": "http://hl7.org/fhir/c80-doc-typecodes",
"code": "57024-2"
}
]
}
],
"library": [
{
"reference": "Library/library-cms146-example"
}
],
"scoring": "proportion",
"type": [
"process"
],
"group": [
{
"identifier": {
"value": "CMS146-group-1"
},
"population": [
{
"type": "initial-population",
"identifier": {
"value": "initial-population-identifier"
},
"criteria": "CMS146.InInitialPopulation"
},
{
"type": "numerator",
"identifier": {
"value": "numerator-identifier"
},
"criteria": "CMS146.InNumerator"
},
{
"type": "denominator",
"identifier": {
"value": "denominator-identifier"
},
"criteria": "CMS146.InDenominator"
},
{
"type": "denominator-exclusion",
"identifier": {
"value": "denominator-exclusions-identifier"
},
"criteria": "CMS146.InDenominatorExclusions"
}
],
"stratifier": [
{
"identifier": {
"value": "stratifier-ages-up-to-9"
},
"criteria": "CMS146.AgesUpToNine"
},
{
"identifier": {
"value": "stratifier-ages-10-plus"
},
"criteria": "CMS146.AgesTenPlus"
},
{
"identifier": {
"value": "stratifier-ages-up-to-9"
},
"path": "Patient.gender"
}
]
}
],
"supplementalData": [
{
"identifier": {
"value": "supplemental-data-gender"
},
"path": "Patient.gender"
},
{
"identifier": {
"value": "supplemental-data-deceased"
},
"path": "deceasedBoolean"
}
]
}

View File

@ -0,0 +1,285 @@
{
"resourceType": "MeasureReport",
"id": "measurereport-cms146-cat1-example",
"contained": [
{
"resourceType": "Organization",
"id": "reporter",
"name": "Good Health Hospital"
}
],
"measure": {
"reference": "Measure/CMS146"
},
"type": "individual",
"patient": {
"reference": "Patient/123"
},
"period": {
"start": "2014-01-01",
"end": "2014-03-31"
},
"status": "complete",
"reportingOrganization": {
"reference": "#reporter"
},
"group": [
{
"identifier": {
"value": "CMS146-group-1"
},
"population": [
{
"type": "initial-population",
"count": 1
},
{
"type": "numerator",
"count": 1
},
{
"type": "denominator",
"count": 1
},
{
"type": "denominator-exclusion",
"count": 0
}
],
"stratifier": [
{
"identifier": {
"value": "stratifier-ages-up-to-9"
},
"group": [
{
"value": "true",
"population": [
{
"type": "initial-population",
"count": 1
},
{
"type": "numerator",
"count": 1
},
{
"type": "denominator",
"count": 1
},
{
"type": "denominator-exclusion",
"count": 0
}
]
},
{
"value": "false",
"population": [
{
"type": "initial-population",
"count": 0
},
{
"type": "numerator",
"count": 0
},
{
"type": "denominator",
"count": 0
},
{
"type": "denominator-exclusion",
"count": 0
}
]
}
]
},
{
"identifier": {
"value": "stratifier-ages-10-plus"
},
"group": [
{
"value": "true",
"population": [
{
"type": "initial-population",
"count": 0
},
{
"type": "numerator",
"count": 0
},
{
"type": "denominator",
"count": 0
},
{
"type": "denominator-exclusion",
"count": 0
}
]
},
{
"value": "false",
"population": [
{
"type": "initial-population",
"count": 1
},
{
"type": "numerator",
"count": 1
},
{
"type": "denominator",
"count": 1
},
{
"type": "denominator-exclusion",
"count": 0
}
]
}
]
},
{
"identifier": {
"value": "stratifier-gender"
},
"group": [
{
"value": "male",
"population": [
{
"type": "initial-population",
"count": 1
},
{
"type": "numerator",
"count": 1
},
{
"type": "denominator",
"count": 1
},
{
"type": "denominator-exclusion",
"count": 0
}
]
},
{
"value": "female",
"population": [
{
"type": "initial-population",
"count": 0
},
{
"type": "numerator",
"count": 0
},
{
"type": "denominator",
"count": 0
},
{
"type": "denominator-exclusion",
"count": 0
}
]
},
{
"value": "other",
"population": [
{
"type": "initial-population",
"count": 0
},
{
"type": "numerator",
"count": 0
},
{
"type": "denominator",
"count": 0
},
{
"type": "denominator-exclusion",
"count": 0
}
]
},
{
"value": "unknown",
"population": [
{
"type": "initial-population",
"count": 0
},
{
"type": "numerator",
"count": 0
},
{
"type": "denominator",
"count": 0
},
{
"type": "denominator-exclusion",
"count": 0
}
]
}
]
}
],
"supplementalData": [
{
"identifier": {
"value": "supplemental-data-gender"
},
"group": [
{
"value": "male",
"count": 1
},
{
"value": "female",
"count": 0
},
{
"value": "other",
"count": 0
},
{
"value": "unknown",
"count": 0
}
]
},
{
"identifier": {
"value": "supplemental-data-deceased"
},
"group": [
{
"value": "true",
"count": 0
},
{
"value": "false",
"count": 1
}
]
}
]
}
],
"evaluatedResources": {
"reference": "Bundle/456"
}
}

View File

@ -0,0 +1,405 @@
{
"resourceType": "MeasureReport",
"id": "measurereport-cms146-cat2-example",
"contained": [
{
"resourceType": "Organization",
"id": "reporter",
"name": "Good Health Hospital"
}
],
"measure": {
"reference": "Measure/CMS146"
},
"type": "patient-list",
"period": {
"start": "2014-01-01",
"end": "2014-03-31"
},
"status": "complete",
"reportingOrganization": {
"reference": "#reporter"
},
"group": [
{
"identifier": {
"value": "CMS146-group-1"
},
"population": [
{
"type": "initial-population",
"count": 500,
"patients": {
"reference": "List/CMS146-initial-population"
}
},
{
"type": "numerator",
"count": 200,
"patients": {
"reference": "List/CMS146-numerator"
}
},
{
"type": "denominator",
"count": 500,
"patients": {
"reference": "List/CMS146-denominator"
}
},
{
"type": "denominator-exclusion",
"count": 100,
"patients": {
"reference": "List/CMS146-denominator-exclusions"
}
}
],
"stratifier": [
{
"identifier": {
"value": "stratifier-ages-up-to-9"
},
"group": [
{
"value": "true",
"population": [
{
"type": "initial-population",
"count": 250,
"patients": {
"reference": "List/CMS146-stratifier-ages-up-to-9-true-initial-population"
}
},
{
"type": "numerator",
"count": 100,
"patients": {
"reference": "List/CMS146-stratifier-ages-up-to-9-true-numerator"
}
},
{
"type": "denominator",
"count": 250,
"patients": {
"reference": "List/CMS146-stratifier-ages-up-to-9-true-denominator"
}
},
{
"type": "denominator-exclusion",
"count": 50,
"patients": {
"reference": "List/CMS146-stratifier-ages-up-to-9-true-denominator-exclusions"
}
}
]
},
{
"value": "false",
"population": [
{
"type": "initial-population",
"count": 250,
"patients": {
"reference": "List/CMS146-stratifier-ages-up-to-9-false-initial-population"
}
},
{
"type": "numerator",
"count": 100,
"patients": {
"reference": "List/CMS146-stratifier-ages-up-to-9-false-numerator"
}
},
{
"type": "denominator",
"count": 250,
"patients": {
"reference": "List/CMS146-stratifier-ages-up-to-9-false-denominator"
}
},
{
"type": "denominator-exclusion",
"count": 50,
"patients": {
"reference": "List/CMS146-stratifier-ages-up-to-9-false-denominator-exclusions"
}
}
]
}
]
},
{
"identifier": {
"value": "stratifier-ages-10-plus"
},
"group": [
{
"value": "true",
"population": [
{
"type": "initial-population",
"count": 250,
"patients": {
"reference": "List/CMS146-stratifier-ages-10-plus-true-initial-population"
}
},
{
"type": "numerator",
"count": 100,
"patients": {
"reference": "List/CMS146-stratifier-ages-10-plus-true-numerator"
}
},
{
"type": "denominator",
"count": 250,
"patients": {
"reference": "List/CMS146-stratifier-ages-10-plus-true-denominator"
}
},
{
"type": "denominator-exclusion",
"count": 50,
"patients": {
"reference": "List/CMS146-stratifier-ages-10-plus-true-denominator-exclusions"
}
}
]
},
{
"value": "false",
"population": [
{
"type": "initial-population",
"count": 250,
"patients": {
"reference": "List/CMS146-stratifier-ages-10-plus-false-initial-population"
}
},
{
"type": "numerator",
"count": 100,
"patients": {
"reference": "List/CMS146-stratifier-ages-10-plus-false-numerator"
}
},
{
"type": "denominator",
"count": 250,
"patients": {
"reference": "List/CMS146-stratifier-ages-10-plus-false-denominator"
}
},
{
"type": "denominator-exclusion",
"count": 50,
"patients": {
"reference": "List/CMS146-stratifier-ages-10-plus-false-denominator-exclusions"
}
}
]
}
]
},
{
"identifier": {
"value": "stratifier-gender"
},
"group": [
{
"value": "male",
"population": [
{
"type": "initial-population",
"count": 250,
"patients": {
"reference": "List/CMS146-stratifier-gender-male-initial-population"
}
},
{
"type": "numerator",
"count": 100,
"patients": {
"reference": "List/CMS146-stratifier-gender-male-numerator"
}
},
{
"type": "denominator",
"count": 250,
"patients": {
"reference": "List/CMS146-stratifier-gender-male-denominator"
}
},
{
"type": "denominator-exclusion",
"count": 50,
"patients": {
"reference": "List/CMS146-stratifier-gender-male-denominator-exclusions"
}
}
]
},
{
"value": "female",
"population": [
{
"type": "initial-population",
"count": 250,
"patients": {
"reference": "List/CMS146-stratifier-gender-female-initial-population"
}
},
{
"type": "numerator",
"count": 100,
"patients": {
"reference": "List/CMS146-stratifier-gender-female-numerator"
}
},
{
"type": "denominator",
"count": 250,
"patients": {
"reference": "List/CMS146-stratifier-gender-female-denominator"
}
},
{
"type": "denominator-exclusion",
"count": 50,
"patients": {
"reference": "List/CMS146-stratifier-gender-female-denominator-exclusions"
}
}
]
},
{
"value": "other",
"population": [
{
"type": "initial-population",
"count": 0,
"patients": {
"reference": "List/CMS146-stratifier-gender-other-initial-population"
}
},
{
"type": "numerator",
"count": 0,
"patients": {
"reference": "List/CMS146-stratifier-gender-other-numerator"
}
},
{
"type": "denominator",
"count": 0,
"patients": {
"reference": "List/CMS146-stratifier-gender-other-denominator"
}
},
{
"type": "denominator-exclusion",
"count": 0,
"patients": {
"reference": "List/CMS146-stratifier-gender-other-denominator-exclusions"
}
}
]
},
{
"value": "unknown",
"population": [
{
"type": "initial-population",
"count": 0,
"patients": {
"reference": "List/CMS146-stratifier-gender-unknown-initial-population"
}
},
{
"type": "numerator",
"count": 0,
"patients": {
"reference": "List/CMS146-stratifier-gender-unknown-numerator"
}
},
{
"type": "denominator",
"count": 0,
"patients": {
"reference": "List/CMS146-stratifier-gender-unknown-denominator"
}
},
{
"type": "denominator-exclusion",
"count": 0,
"patients": {
"reference": "List/CMS146-stratifier-gender-unknown-denominator-exclusions"
}
}
]
}
]
}
],
"supplementalData": [
{
"identifier": {
"value": "supplemental-data-gender"
},
"group": [
{
"value": "male",
"count": 250,
"patients": {
"reference": "List/CMS146-supplemental-data-gender-male"
}
},
{
"value": "female",
"count": 250,
"patients": {
"reference": "List/CMS146-supplemental-data-gender-female"
}
},
{
"value": "other",
"count": 0,
"patients": {
"reference": "List/CMS146-supplemental-data-gender-other"
}
},
{
"value": "unknown",
"count": 0,
"patients": {
"reference": "List/CMS146-supplemental-data-gender-unknown"
}
}
]
},
{
"identifier": {
"value": "supplemental-data-deceased"
},
"group": [
{
"value": "true",
"count": 0,
"patients": {
"reference": "List/CMS146-supplemental-data-deceased-true"
}
},
{
"value": "false",
"count": 500,
"patients": {
"reference": "List/CMS146-supplemental-data-deceased-false"
}
}
]
}
]
}
]
}

View File

@ -0,0 +1,279 @@
{
"resourceType": "MeasureReport",
"id": "measurereport-cms146-cat3-example",
"contained": [
{
"resourceType": "Organization",
"id": "reporter",
"name": "Good Health Hospital"
}
],
"measure": {
"reference": "Measure/CMS146"
},
"type": "summary",
"period": {
"start": "2014-01-01",
"end": "2014-03-31"
},
"status": "complete",
"reportingOrganization": {
"reference": "#reporter"
},
"group": [
{
"identifier": {
"value": "CMS146-group-1"
},
"population": [
{
"type": "initial-population",
"count": 500
},
{
"type": "numerator",
"count": 200
},
{
"type": "denominator",
"count": 500
},
{
"type": "denominator-exclusion",
"count": 100
}
],
"stratifier": [
{
"identifier": {
"value": "stratifier-ages-up-to-9"
},
"group": [
{
"value": "true",
"population": [
{
"type": "initial-population",
"count": 250
},
{
"type": "numerator",
"count": 100
},
{
"type": "denominator",
"count": 250
},
{
"type": "denominator-exclusion",
"count": 50
}
]
},
{
"value": "false",
"population": [
{
"type": "initial-population",
"count": 250
},
{
"type": "numerator",
"count": 100
},
{
"type": "denominator",
"count": 250
},
{
"type": "denominator-exclusion",
"count": 50
}
]
}
]
},
{
"identifier": {
"value": "stratifier-ages-10-plus"
},
"group": [
{
"value": "true",
"population": [
{
"type": "initial-population",
"count": 250
},
{
"type": "numerator",
"count": 100
},
{
"type": "denominator",
"count": 250
},
{
"type": "denominator-exclusion",
"count": 50
}
]
},
{
"value": "false",
"population": [
{
"type": "initial-population",
"count": 250
},
{
"type": "numerator",
"count": 100
},
{
"type": "denominator",
"count": 250
},
{
"type": "denominator-exclusion",
"count": 50
}
]
}
]
},
{
"identifier": {
"value": "stratifier-gender"
},
"group": [
{
"value": "male",
"population": [
{
"type": "initial-population",
"count": 250
},
{
"type": "numerator",
"count": 100
},
{
"type": "denominator",
"count": 250
},
{
"type": "denominator-exclusion",
"count": 50
}
]
},
{
"value": "female",
"population": [
{
"type": "initial-population",
"count": 250
},
{
"type": "numerator",
"count": 100
},
{
"type": "denominator",
"count": 250
},
{
"type": "denominator-exclusion",
"count": 50
}
]
},
{
"value": "other",
"population": [
{
"type": "initial-population",
"count": 0
},
{
"type": "numerator",
"count": 0
},
{
"type": "denominator",
"count": 0
},
{
"type": "denominator-exclusion",
"count": 0
}
]
},
{
"value": "unknown",
"population": [
{
"type": "initial-population",
"count": 0
},
{
"type": "numerator",
"count": 0
},
{
"type": "denominator",
"count": 0
},
{
"type": "denominator-exclusion",
"count": 0
}
]
}
]
}
],
"supplementalData": [
{
"identifier": {
"value": "supplemental-data-gender"
},
"group": [
{
"value": "male",
"count": 250
},
{
"value": "female",
"count": 250
},
{
"value": "other",
"count": 0
},
{
"value": "unknown",
"count": 0
}
]
},
{
"identifier": {
"value": "supplemental-data-deceased"
},
"group": [
{
"value": "true",
"count": 0
},
{
"value": "false",
"count": 500
}
]
}
]
}
]
}

View File

@ -0,0 +1,42 @@
{
"resourceType": "PlanDefinition",
"id": "chlamydia-screening-intervention",
"identifier": [
{
"use": "official",
"value": "ChlamydiaScreening_CDS_UsingCommon"
}
],
"version": "2.0.0",
"title": "Chalmydia Screening CDS Example Using Common",
"status": "draft",
"description": "Chlamydia Screening CDS Example Using Common",
"approvalDate": "2015-07-22",
"topic": [
{
"text": "Chlamydia Screeening"
}
],
"library": [
{
"reference": "Library/ChlamydiaScreening_CDS_UsingCommon"
}
],
"actionDefinition": [
{
"title": "Patient has not had chlamydia screening within the recommended timeframe...",
"condition": [
{
"kind": "applicability",
"expression": "NoScreening"
}
],
"dynamicValue": [
{
"path": "~",
"expression": "ChlamydiaScreeningRequest"
}
]
}
]
}

View File

@ -0,0 +1,82 @@
{
"resourceType": "PlanDefinition",
"id": "exclusive-breastfeeding-intervention-01",
"identifier": [
{
"use": "official",
"value": "exclusive-breastfeeding-intervention-01"
}
],
"version": "1.0.0",
"title": "Exclusive Breastfeeding Intervention-01",
"status": "active",
"date": "2015-03-08",
"description": "Exclusive breastfeeding intervention intended to improve outcomes for exclusive breastmilk feeding of newborns by ensuring that an appropriate breastfeeding readiness assessment order is created if necessary.",
"topic": [
{
"text": "Exclusive Breastfeeding"
}
],
"library": [
{
"reference": "Library/library-exclusive-breastfeeding-cds-logic"
}
],
"actionDefinition": [
{
"title": "Mother should be administered a breastfeeding readiness assessment.",
"triggerDefinition": [
{
"type": "named-event",
"eventName": "Admission"
},
{
"type": "named-event",
"eventName": "Birth"
},
{
"type": "named-event",
"eventName": "Infant Transfer to Recovery"
},
{
"type": "named-event",
"eventName": "Transfer to Post-Partum"
}
],
"condition": [
{
"kind": "applicability",
"expression": "Should Create Assessment Order"
}
],
"actionDefinition": [
{
"title": "Create the breastfeeding readiness assessment order.",
"textEquivalent": "Administer a breastfeeding readiness assessment.",
"type": {
"code": "create"
},
"dynamicValue": [
{
"path": "/",
"expression": "Create Breastfeeding Risk Assessment"
}
]
},
{
"title": "Notify the provider to sign the order.",
"textEquivalent": "A Breastfeeding Readiness Assessment is recommended, please authorize or reject the order.",
"type": {
"code": "create"
},
"dynamicValue": [
{
"path": "/",
"expression": "Communication Request to Provider"
}
]
}
]
}
]
}

View File

@ -0,0 +1,69 @@
{
"resourceType": "PlanDefinition",
"id": "exclusive-breastfeeding-intervention-02",
"identifier": [
{
"use": "official",
"value": "exclusive-breastfeeding-intervention-02"
}
],
"version": "1.0.0",
"title": "Exclusive Breastfeeding Intervention-02",
"status": "active",
"date": "2015-03-08",
"description": "Exclusive breastfeeding intervention intended to improve outcomes for exclusive breastmilk feeding of newborns by notifying the provider to sign the breastmilk feeding readiness assessment order, if necessary.",
"topic": [
{
"text": "Exclusive Breastfeeding"
}
],
"library": [
{
"reference": "Library/library-exclusive-breastfeeding-cds-logic"
}
],
"actionDefinition": [
{
"title": "Mother should be administered a breastfeeding readiness assessment.",
"triggerDefinition": [
{
"type": "named-event",
"eventName": "Admission"
},
{
"type": "named-event",
"eventName": "Birth"
},
{
"type": "named-event",
"eventName": "Infant Transfer to Recovery"
},
{
"type": "named-event",
"eventName": "Transfer to Post-Partum"
}
],
"condition": [
{
"kind": "applicability",
"expression": "Should Notify Provider to Sign Assessment Order"
}
],
"actionDefinition": [
{
"title": "Notify the provider to sign the order.",
"textEquivalent": "A Breastfeeding Readiness Assessment is recommended, please authorize or reject the order.",
"type": {
"code": "create"
},
"dynamicValue": [
{
"path": "/",
"expression": "Communication Request to Provider"
}
]
}
]
}
]
}

View File

@ -0,0 +1,82 @@
{
"resourceType": "PlanDefinition",
"id": "exclusive-breastfeeding-intervention-03",
"identifier": [
{
"use": "official",
"value": "exclusive-breastfeeding-intervention-03"
}
],
"version": "1.0.0",
"title": "Exclusive Breastfeeding Intervention-03",
"status": "active",
"date": "2015-03-08",
"description": "Exclusive breastfeeding intervention intended to improve outcomes for exclusive breastmilk feeding of newborns by notifying the charge and/or bedside nurse to perform the breastfeeding readiness assessment.",
"topic": [
{
"text": "Exclusive Breastfeeding"
}
],
"library": [
{
"reference": "Library/library-exclusive-breastfeeding-cds-logic"
}
],
"actionDefinition": [
{
"title": "Mother should be administered a breastfeeding readiness assessment.",
"triggerDefinition": [
{
"type": "named-event",
"eventName": "Admission"
},
{
"type": "named-event",
"eventName": "Birth"
},
{
"type": "named-event",
"eventName": "Infant Transfer to Recovery"
},
{
"type": "named-event",
"eventName": "Transfer to Post-Partum"
}
],
"condition": [
{
"kind": "applicability",
"expression": "Should Notify Nurse to Perform Assessment"
}
],
"actionDefinition": [
{
"title": "Notify the charge nurse to perform the assessment.",
"textEquivalent": "A Breastfeeding Readiness Assessment is recommended, please administer the assessment.",
"type": {
"code": "create"
},
"dynamicValue": [
{
"path": "/",
"expression": "Communication Request to Charge Nurse"
}
]
},
{
"title": "Notify the bedside nurse to perform the assessment.",
"textEquivalent": "A Breastfeeding Readiness Assessment is recommended, please administer the assessment.",
"type": {
"code": "create"
},
"dynamicValue": [
{
"path": "/",
"expression": "Communication Request to Bedside Nurse"
}
]
}
]
}
]
}

View File

@ -0,0 +1,69 @@
{
"resourceType": "PlanDefinition",
"id": "exclusive-breastfeeding-intervention-04",
"identifier": [
{
"use": "official",
"value": "exclusive-breastfeeding-intervention-04"
}
],
"version": "1.0.0",
"title": "Exclusive Breastfeeding Intervention-04",
"status": "active",
"date": "2015-03-08",
"description": "Exclusive breastfeeding intervention intended to improve outcomes for exclusive breastmilk feeding of newborns by creating a lactation consult for the mother if appropriate.",
"topic": [
{
"text": "Exclusive Breastfeeding"
}
],
"library": [
{
"reference": "Library/library-exclusive-breastfeeding-cds-logic"
}
],
"actionDefinition": [
{
"title": "Mother should be referred to a lactation specialist for consultation.",
"triggerDefinition": [
{
"type": "named-event",
"eventName": "Admission"
},
{
"type": "named-event",
"eventName": "Birth"
},
{
"type": "named-event",
"eventName": "Infant Transfer to Recovery"
},
{
"type": "named-event",
"eventName": "Transfer to Post-Partum"
}
],
"condition": [
{
"kind": "applicability",
"expression": "Should Create Lactation Consult"
}
],
"actionDefinition": [
{
"title": "Create a lactation consult request.",
"textEquivalent": "Create a lactation consult request",
"type": {
"code": "create"
},
"dynamicValue": [
{
"path": "/",
"expression": "Create Lactation Consult Request"
}
]
}
]
}
]
}

View File

@ -0,0 +1,217 @@
{
"resourceType": "PlanDefinition",
"id": "zika-virus-intervention",
"contained": [
{
"resourceType": "ActivityDefinition",
"id": "administer-zika-virus-exposure-assessment",
"url": "http://example.org/ActivityDefinition/administer-zika-virus-exposure-assessment",
"status": "draft",
"description": "Administer Zika Virus Exposure Assessment",
"category": "procedure",
"code": {
"coding": [
{
"system": "http://example.org/questionnaires",
"code": "zika-virus-exposure-assessment"
}
]
},
"timingTiming": {
"event": [
{
"extension": [
{
"url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression",
"valueString": "Now()"
}
]
}
]
},
"participantType": [
"practitioner"
]
},
{
"resourceType": "ActivityDefinition",
"id": "order-serum-zika-dengue-virus-igm",
"url": "http://example.org/ActivityDefinition/serum-zika-dengue-virus-igm",
"status": "draft",
"description": "Order Serum Zika and Dengue Virus IgM",
"relatedArtifact": [
{
"type": "documentation",
"display": "Explanation of diagnostic tests for Zika virus and which to use based on the patients clinical and exposure history.",
"url": "http://www.cdc.gov/zika/hc-providers/diagnostic.html"
}
],
"category": "diagnostic",
"code": {
"text": "Serum Zika and Dengue Virus IgM"
},
"timingTiming": {
"event": [
{
"extension": [
{
"url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression",
"valueString": "Now()"
}
]
}
]
},
"participantType": [
"practitioner"
]
},
{
"resourceType": "ActivityDefinition",
"id": "provide-mosquito-prevention-advice",
"text": {
"status": "generated",
"div": "<div xmlns=\"http://www.w3.org/1999/xhtml\">Provide Mosquito Prevention Advice</div>"
},
"url": "http://example.org/ActivityDefinition/provide-mosquito-prevention-advice",
"status": "draft",
"description": "Provide mosquito prevention advice",
"relatedArtifact": [
{
"type": "documentation",
"display": "Advice for patients about how to avoid Mosquito bites.",
"url": "http://www.cdc.gov/zika/prevention/index.html"
},
{
"type": "documentation",
"display": "Advice for patients about which mosquito repellents are effective and safe to use in pregnancy. [DEET, IF3535 and Picardin are safe during]",
"url": "https://www.epa.gov/insect-repellents/find-insect-repellent-right-you"
}
],
"category": "communication",
"code": {
"text": "Provide Mosquito Prevention Advice"
},
"timingTiming": {
"event": [
{
"extension": [
{
"url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression",
"valueString": "Now()"
}
]
}
]
},
"participantType": [
"practitioner"
]
}
],
"url": "http://example.org/PlanDefinition/zika-virus-intervention",
"identifier": [
{
"use": "official",
"value": "zika-virus-intervention"
}
],
"version": "1.0.0",
"title": "Example Zika Virus Intervention",
"status": "active",
"date": "2016-11-14",
"description": "Zika Virus Management intervention describing the CDC Guidelines for Zika Virus Reporting and Management.",
"topic": [
{
"text": "Zika Virus Management"
}
],
"library": [
{
"reference": "Library/zika-virus-intervention-logic"
}
],
"actionDefinition": [
{
"title": "Zika Virus Assessment",
"triggerDefinition": [
{
"type": "named-event",
"eventName": "patient-view"
}
],
"condition": [
{
"kind": "applicability",
"expression": "Is Patient Pregnant"
}
],
"actionDefinition": [
{
"condition": [
{
"kind": "applicability",
"expression": "Should Administer Zika Virus Exposure Assessment"
}
],
"activityDefinition": {
"reference": "#administer-zika-virus-exposure-assessment"
}
},
{
"condition": [
{
"kind": "applicability",
"expression": "Should Order Serum + Urine rRT-PCR Test"
}
],
"activityDefinition": {
"reference": "ActivityDefinition/order-serum-urine-rrt-pcr-test"
}
},
{
"condition": [
{
"kind": "applicability",
"expression": "Should Order Serum Zika Virus IgM + Dengue Virus IgM"
}
],
"activityDefinition": {
"reference": "#order-serum-zika-dengue-virus-igm"
}
},
{
"condition": [
{
"kind": "applicability",
"expression": "Should Consider IgM Antibody Testing"
}
],
"activityDefinition": {
"reference": "ActivityDefinition/consider-igm-antibody-testing"
}
},
{
"condition": [
{
"kind": "applicability",
"expression": "Should Provide Mosquito Prevention and Contraception Advice"
}
],
"actionDefinition": [
{
"activityDefinition": {
"reference": "#provide-mosquito-prevention-advice"
}
},
{
"activityDefinition": {
"reference": "ActivityDefinition/provide-contraception-advice"
}
}
]
}
]
}
]
}

View File

@ -0,0 +1,476 @@
{
"resourceType": "PlanDefinition",
"id": "example",
"contained": [
{
"resourceType": "ActivityDefinition",
"id": "referralToMentalHealthCare",
"status": "draft",
"description": "refer to primary care mental-health integrated care program for evaluation and treatment of mental health conditions now",
"category": "referral",
"code": {
"coding": [
{
"system": "http://snomed.info/sct",
"code": "306206005"
}
],
"text": "Referral to service (procedure)"
},
"timingTiming": {
"event": [
{
"extension": [
{
"url": "http://hl7.org/fhir/StructureDefinition/cqif-cqlExpression",
"valueString": "Now()"
}
]
}
]
},
"participantType": [
"practitioner"
]
},
{
"resourceType": "ActivityDefinition",
"id": "citalopramPrescription",
"status": "draft",
"category": "drug",
"productReference": {
"reference": "#citalopramMedication"
},
"dosageInstruction": [
{
"text": "1 tablet oral 1 time daily",
"timing": {
"repeat": {
"frequency": 1,
"period": 1,
"periodUnit": "d"
}
},
"route": {
"coding": [
{
"code": "26643006",
"display": "Oral route (qualifier value)"
}
],
"text": "Oral route (qualifier value)"
},
"doseQuantity": {
"value": 1,
"unit": "{tbl}"
}
}
],
"dynamicValue": [
{
"path": "dispenseRequest.numberOfRepeatsAllowed",
"expression": "3"
},
{
"path": "dispenseRequest.quantity",
"expression": "30 '{tbl}'"
}
]
},
{
"resourceType": "Medication",
"id": "citalopramMedication",
"code": {
"coding": [
{
"system": "http://www.nlm.nih.gov/research/umls/rxnorm",
"code": "200371"
}
],
"text": "citalopram"
},
"product": {
"form": {
"coding": [
{
"system": "http://snomed.info/sct",
"code": "385055001",
"display": "Tablet dose form"
}
],
"text": "Tablet dose form"
},
"ingredient": [
{
"itemReference": {
"reference": "#citalopramSubstance"
},
"amount": {
"numerator": {
"value": 20,
"unit": "mg"
},
"denominator": {
"value": 1,
"unit": "{tbl}"
}
}
}
]
}
},
{
"resourceType": "Substance",
"id": "citalopramSubstance",
"code": {
"coding": [
{
"system": "http://www.nlm.nih.gov/research/umls/rxnorm",
"code": "2556"
}
],
"text": "citalopram"
}
}
],
"identifier": [
{
"use": "official",
"value": "mmi:low-suicide-risk-order-set"
}
],
"version": "1.0.0",
"title": "Low Suicide Risk Order Set",
"status": "draft",
"date": "2015-08-15",
"description": "Orders to be applied to a patient characterized as low suicide risk.",
"purpose": "This order set helps ensure consistent application of appropriate orders for the care of low suicide risk patients.",
"usage": "This order set should be applied after assessing a patient for suicide risk, when the findings of that assessment indicate the patient has low suicide risk.",
"approvalDate": "2016-03-12",
"lastReviewDate": "2016-08-15",
"effectivePeriod": {
"start": "2016-01-01",
"end": "2017-12-31"
},
"useContext": [
{
"code": {
"system": "http://hl7.org/fhir/usage-context-type",
"code": "age"
},
"valueCodeableConcept": {
"coding": [
{
"system": "https://meshb.nlm.nih.gov",
"code": "D000328",
"display": "Adult"
}
]
}
},
{
"code": {
"system": "http://hl7.org/fhir/usage-context-type",
"code": "focus"
},
"valueCodeableConcept": {
"coding": [
{
"system": "http://snomed.info/sct",
"code": "87512008",
"display": "Mild major depression"
}
]
}
},
{
"code": {
"system": "http://hl7.org/fhir/usage-context-type",
"code": "focus"
},
"valueCodeableConcept": {
"coding": [
{
"system": "http://snomed.info/sct",
"code": "40379007",
"display": "Major depression, recurrent, mild"
}
]
}
},
{
"code": {
"system": "http://hl7.org/fhir/usage-context-type",
"code": "focus"
},
"valueCodeableConcept": {
"coding": [
{
"system": "http://snomed.info/sct",
"code": "394687007",
"display": "Low suicide risk"
}
]
}
},
{
"code": {
"system": "http://hl7.org/fhir/usage-context-type",
"code": "focus"
},
"valueCodeableConcept": {
"coding": [
{
"system": "http://snomed.info/sct",
"code": "225337009",
"display": "Suicide risk assessment"
}
]
}
},
{
"code": {
"system": "http://hl7.org/fhir/usage-context-type",
"code": "user"
},
"valueCodeableConcept": {
"coding": [
{
"system": "http://snomed.info/sct",
"code": "309343006",
"display": "Physician"
}
]
}
},
{
"code": {
"system": "http://hl7.org/fhir/usage-context-type",
"code": "venue"
},
"valueCodeableConcept": {
"coding": [
{
"system": "http://snomed.info/sct",
"code": "440655000",
"display": "Outpatient environment"
}
]
}
}
],
"jurisdiction": [
{
"coding": [
{
"system": "urn:iso:std:iso:3166",
"code": "US"
}
]
}
],
"topic": [
{
"text": "Suicide risk assessment"
}
],
"contributor": [
{
"type": "author",
"name": "Motive Medical Intelligence",
"contact": [
{
"telecom": [
{
"system": "phone",
"value": "415-362-4007",
"use": "work"
},
{
"system": "email",
"value": "info@motivemi.com",
"use": "work"
}
]
}
]
}
],
"publisher": "Motive Medical Intelligence",
"contact": [
{
"telecom": [
{
"system": "phone",
"value": "415-362-4007",
"use": "work"
},
{
"system": "email",
"value": "info@motivemi.com",
"use": "work"
}
]
}
],
"library": [
{
"reference": "Library/mmi-suiciderisk-orderset-logic",
"display": "SuicideRiskLogic"
}
],
"actionDefinition": [
{
"title": "Suicide Risk Assessment and Outpatient Management",
"actionDefinition": [
{
"title": "Consults and Referrals",
"groupingBehavior": "logical-group",
"selectionBehavior": "any",
"actionDefinition": [
{
"textEquivalent": "Refer to outpatient mental health program for evaluation and treatment of mental health conditions now",
"activityDefinition": {
"reference": "#referralToMentalHealthCare"
},
"dynamicValue": [
{
"path": "timing.event",
"expression": "Now()"
},
{
"path": "specialty",
"expression": "Code '261QM0850X' from SuicideRiskLogic.\"NPI Taxonomy\""
},
{
"path": "fulfillmentTime",
"expression": "SuicideRiskLogic.ReferralRequestFulfillmentTime"
},
{
"path": "patient",
"expression": "SuicideRiskLogic.Patient"
},
{
"path": "requester",
"expression": "SuicideRiskLogic.Practitioner"
},
{
"path": "reason",
"expression": "SuicideRiskLogic.RiskAssessmentScore"
},
{
"path": "supportingInformation",
"expression": "SuicideRiskLogic.RiskAssessment"
}
]
}
]
},
{
"title": "Medications",
"groupingBehavior": "logical-group",
"selectionBehavior": "at-most-one",
"actionDefinition": [
{
"title": "First-Line Antidepressants",
"documentation": [
{
"type": "justification",
"document": {
"extension": [
{
"url": "http://hl7.org/fhir/StructureDefinition/cqif-qualityOfEvidence",
"valueCodeableConcept": {
"coding": [
{
"system": "http://hl7.org/fhir/evidence-quality",
"code": "high"
}
],
"text": "High Quality"
}
}
],
"contentType": "text/html",
"url": "http://psychiatryonline.org/pb/assets/raw/sitewide/practice_guidelines/guidelines/mdd.pdf",
"title": "Practice Guideline for the Treatment of Patients with Major Depressive Disorder"
}
}
],
"groupingBehavior": "logical-group",
"selectionBehavior": "at-most-one",
"actionDefinition": [
{
"title": "Selective Serotonin Reuptake Inhibitors (Choose a mazimum of one or document reasons for exception)",
"documentation": [
{
"type": "justification",
"document": {
"contentType": "text/html",
"url": "http://dailymed.nlm.nih.gov/dailymed/drugInfo.cfm?setid=6daeb45c-451d-b135-bf8f-2d6dff4b6b01",
"title": "National Library of Medicine. DailyMed website. CITALOPRAM- citalopram hydrobromide tablet, film coated."
}
}
],
"groupingBehavior": "logical-group",
"selectionBehavior": "at-most-one",
"actionDefinition": [
{
"textEquivalent": "citalopram 20 mg tablet 1 tablet oral 1 time daily now (30 table; 3 refills)",
"activityDefinition": {
"reference": "#citalopramPrescription"
},
"dynamicValue": [
{
"path": "status",
"expression": "'draft'"
},
{
"path": "patient",
"expression": "SuicideRiskLogic.Patient"
},
{
"path": "prescriber",
"expression": "SuicideRiskLogic.Practitioner"
},
{
"path": "reasonCode",
"expression": "SuicideRiskLogic.RiskAssessmentScore"
},
{
"path": "reasonReference",
"expression": "SuicideRiskLogic.RiskAssessment"
}
]
},
{
"textEquivalent": "escitalopram 10 mg tablet 1 tablet oral 1 time daily now (30 tablet; 3 refills)"
},
{
"textEquivalent": "fluoxetine 20 mg capsule 1 capsule oral 1 time daily now (30 tablet; 3 refills)"
},
{
"textEquivalent": "paroxetine 20 mg tablet 1 tablet oral 1 time daily now (30 tablet; 3 refills)"
},
{
"textEquivalent": "sertraline 50 mg tablet 1 tablet oral 1 time daily now (30 tablet; 3 refills)"
}
]
},
{
"textEquivalent": "Dopamine Norepinephrine Reuptake Inhibitors (Choose a maximum of one or document reasons for exception)"
},
{
"textEquivalent": "Serotonin Norepinephrine Reuptake Inhibitors (Choose a maximum of one or doument reasons for exception)"
},
{
"textEquivalent": "Norepinephrine-Serotonin Modulators (Choose a maximum of one or document reasons for exception)"
}
]
}
]
}
]
}
]
}

View File

@ -0,0 +1,64 @@
{
"resourceType": "PlanDefinition",
"id": "protocol-example",
"contained": [
{
"resourceType": "ActivityDefinition",
"id": "procedure",
"status": "draft",
"description": "Extra information on activity ",
"category": "procedure",
"code": {
"coding": [
{
"system": "http://loinc.org",
"code": "39156-5",
"display": "Body mass index (BMI) [Ratio]"
}
]
},
"participantType": [
"practitioner"
]
}
],
"identifier": [
{
"system": "http://acme.org",
"value": "example-1"
}
],
"title": "Obesity Assessment Protocol",
"type": {
"coding": [
{
"code": "protocol"
}
]
},
"status": "draft",
"purpose": "Example of A medical algorithm for assessment and treatment of overweight and obesity",
"contributor": [
{
"type": "author",
"name": "National Heart, Lung, and Blood Institute http://www.nhlbi.nih.gov/health-pro/guidelines/current/obesity-guidelines/e_textbook/txgd/algorthm/algorthm.htm"
}
],
"actionDefinition": [
{
"label": "Measure BMI",
"title": "Measure, Weight, Height, Waist, Circumference; Calculate BMI",
"description": "Weight must be measured so that the BMI can be calculated. Most charts are based on weights obtained with the patient wearing undergarments and no shoes. BMI can be manually calculated (kg/[height in meters]2), but is more easily obtained from a nomogram. Waist circumference is important because evidence suggests that abdominal fat is a particularly strong determinant of cardiovascular risk in those with a BMI of 25 to 34.9 kg/m2. Increased waist circumference can also be a marker of increased risk even in persons of normal weight. The technique for measuring waist circumference is described in the text. A nutrition assessment will also help to assess the diet and physical activity habits of overweight patients",
"condition": [
{
"kind": "applicability",
"description": "The practitioner must seek to determine whether the patient has ever been overweight. While a technical definition is provided, a simple question such as 'Have you ever been overweight?' will accomplish the same goal. Questions directed towards weight history, dietary habits, physical activities, and medications may provide useful information about the origins of obesity in particular patients. For those who have not been overweight, a 2 year interval is appropriate for the reassessment of BMI. While this time span is not evidence-based, it is believed to be a reasonable compromise between the need to identify weight gain at an early stage and the need to limit the time, effort, and cost of repeated measurements.",
"expression": "Observation of Obesity or BMI Measured in Past 2 Years"
}
],
"activityDefinition": {
"reference": "#procedure"
}
}
]
}

View File

@ -0,0 +1,23 @@
{
"resourceType": "ServiceDefinition",
"id": "example",
"identifier": [
{
"use": "official",
"value": "guildeline-appropriate-ordering"
}
],
"version": "1.0.0",
"title": "Guideline Appropriate Ordering Module",
"status": "draft",
"date": "2015-07-22",
"description": "Guideline appropriate ordering is used to assess appropriateness of an order given a patient, a proposed order, and a set of clinical indications.",
"topic": [
{
"text": "Guideline Appropriate Ordering"
},
{
"text": "Appropriate Use Criteria"
}
]
}

View File

@ -0,0 +1,38 @@
{
"resourceType": "ServiceDefinition",
"id": "infobutton",
"identifier": [
{
"use": "official",
"value": "infobutton"
}
],
"version": "1.0.0",
"title": "InfoButton Module",
"status": "draft",
"date": "2016-07-05",
"description": "The InfoButton specification defines a mechanism for retrieving relevant clinical context based a particular set of search criteria..",
"topic": [
{
"text": "InfoButton Module"
},
{
"text": "InfoButton"
}
],
"dataRequirement": [
{
"type": "Patient",
"mustSupport": [
"gender",
"birthDate"
]
},
{
"type": "Condition"
},
{
"type": "MedicationStatement"
}
]
}

View File

@ -0,0 +1,447 @@
<Library xmlns="http://hl7.org/fhir">
<id value="mmi-suiciderisk-orderset-logic"/>
<text>
<status value="generated"/>
<div xmlns="http://www.w3.org/1999/xhtml">
<table class="grid dict">
<tr>
<td>
<b>Id: </b>
</td>
</tr>
<tr>
<td style="padding-left: 25px; padding-right: 25px;">Library/mmi-suiciderisk-orderset-logic</td>
</tr>
</table>
<table class="grid dict">
<tr>
<td>
<b>Identifier: </b>
</td>
</tr>
<tr>
<td style="padding-left: 25px; padding-right: 25px;">
<b>value: </b>
<span>SuicideRiskLogic</span>
</td>
</tr>
</table>
<p/>
<table class="grid dict">
<tr>
<td>
<b>Version: </b>
</td>
</tr>
<tr>
<td style="padding-left: 25px; padding-right: 25px;">1.0.0</td>
</tr>
</table>
<p/>
<table class="grid dict">
<tr>
<td>
<b>Title: </b>
</td>
</tr>
<tr>
<td style="padding-left: 25px; padding-right: 25px;">Suicide Risk Order Set Logic</td>
</tr>
</table>
<p/>
<table class="grid dict">
<tr>
<td>
<b>Type: </b>
</td>
</tr>
<tr>
<td style="padding-right: 25px;">
<span>
<span>
<span style="padding-left: 25px;">
<b>code: </b>
<span>logic-library</span>
</span>
</span>
</span>
</td>
</tr>
</table>
<p/>
<table class="grid dict">
<tr>
<td>
<b>Status: </b>
</td>
</tr>
<tr>
<td style="padding-left: 25px; padding-right: 25px;">draft</td>
</tr>
</table>
<p/>
<table class="grid dict">
<tr>
<td>
<b>Description: </b>
</td>
</tr>
<tr>
<td style="padding-left: 25px; padding-right: 25px;">Logic for Suicide Risk Order Sets</td>
</tr>
</table>
<p/>
<table class="grid dict">
<tr>
<td>
<b>Topic: </b>
</td>
</tr>
<tr>
<td style="padding-right: 25px;">
<span style="padding-left: 25px;">
<b>text: </b>
<span>Suicide Risk Order Set Logic</span>
</span>
</td>
</tr>
</table>
<p/>
<table class="grid dict">
<tr>
<td>
<b>Related: </b>
</td>
</tr>
<tr style="vertical-align: top;">
<td style="padding-left: 25px; padding-right: 25px;">
<p style="margin-bottom: 5px;">
<b>type: </b>
<span>depends-on</span>
</p>
<p style="margin-bottom: 5px;">
<b>Resource: </b>
<br/>
<span>
<span style="padding-left: 25px;">
<b>reference: </b>
<span>Library/library-fhir-model-definition</span>
</span>
</span>
</p>
</td>
</tr>
</table>
<table class="grid dict">
<tr>
<td>
<b>Related: </b>
</td>
</tr>
<tr style="vertical-align: top;">
<td style="padding-left: 25px; padding-right: 25px;">
<p style="margin-bottom: 5px;">
<b>type: </b>
<span>depends-on</span>
</p>
<p style="margin-bottom: 5px;">
<b>Resource: </b>
<br/>
<span>
<span style="padding-left: 25px;">
<b>reference: </b>
<span>Library/library-fhir-helpers</span>
<br/>
</span>
<span style="padding-left: 25px;">
<b>display: </b>
<span>FHIRHelpers</span>
</span>
</span>
</p>
</td>
</tr>
</table>
<table class="grid dict">
<tr>
<td>
<b>Related: </b>
</td>
</tr>
<tr style="vertical-align: top;">
<td style="padding-left: 25px; padding-right: 25px;">
<p style="margin-bottom: 5px;">
<b>type: </b>
<span>depends-on</span>
</p>
<p style="margin-bottom: 5px;">
<b>Resource: </b>
<br/>
<span>
<span style="padding-left: 25px;">
<b>reference: </b>
<span>CodeSystem/npi-taxonomy</span>
</span>
</span>
</p>
</td>
</tr>
</table>
<table class="grid dict">
<tr>
<td>
<b>Related: </b>
</td>
</tr>
<tr style="vertical-align: top;">
<td style="padding-left: 25px; padding-right: 25px;">
<p style="margin-bottom: 5px;">
<b>type: </b>
<span>depends-on</span>
</p>
<p style="margin-bottom: 5px;">
<b>Resource: </b>
<br/>
<span>
<span style="padding-left: 25px;">
<b>reference: </b>
<span>ValueSet/1.2.3.4.5</span>
<br/>
</span>
<span style="padding-left: 25px;">
<b>display: </b>
<span>Suicide Risk Assessment</span>
</span>
</span>
</p>
</td>
</tr>
</table>
<table class="grid dict">
<tr>
<td>
<b>Parameter: </b>
</td>
</tr>
<tr style="vertical-align: top;">
<td style="padding-left: 25px; padding-right: 25px;">
<span>
<b>name: </b>
<span>Patient</span>
<br/>
</span>
<b>use: </b>
<span>in</span>
<br/>
<span>
<b>minimum cardinality: </b>
<span>1</span>
<br/>
</span>
<span>
<b>maximum cardinality: </b>
<span>1</span>
<br/>
</span>
<b>type: </b>
<span>Patient</span>
<br/>
<p style="margin-bottom: 5px;"/>
</td>
</tr>
</table>
<table class="grid dict">
<tr>
<td>
<b>Parameter: </b>
</td>
</tr>
<tr style="vertical-align: top;">
<td style="padding-left: 25px; padding-right: 25px;">
<span>
<b>name: </b>
<span>Encounter</span>
<br/>
</span>
<b>use: </b>
<span>in</span>
<br/>
<span>
<b>minimum cardinality: </b>
<span>1</span>
<br/>
</span>
<span>
<b>maximum cardinality: </b>
<span>1</span>
<br/>
</span>
<b>type: </b>
<span>Encounter</span>
<br/>
<p style="margin-bottom: 5px;"/>
</td>
</tr>
</table>
<table class="grid dict">
<tr>
<td>
<b>Parameter: </b>
</td>
</tr>
<tr style="vertical-align: top;">
<td style="padding-left: 25px; padding-right: 25px;">
<span>
<b>name: </b>
<span>Practitioner</span>
<br/>
</span>
<b>use: </b>
<span>in</span>
<br/>
<span>
<b>minimum cardinality: </b>
<span>1</span>
<br/>
</span>
<span>
<b>maximum cardinality: </b>
<span>1</span>
<br/>
</span>
<b>type: </b>
<span>Practitioner</span>
<br/>
<p style="margin-bottom: 5px;"/>
</td>
</tr>
</table>
<table class="grid dict">
<tr>
<td>
<b>Data Requirements: </b>
</td>
</tr>
<tr>
<td style="padding-left: 25px; padding-right: 25px;">
<div>
<p style="margin-bottom: 5px;">
<b>type: </b>
<span>RiskAssessment</span>
</p>
<p style="margin-bottom: 5px;">
<b>code filter:</b>
<br/>
<span style="padding-left: 25px;">
<b>path: </b>
<span>code</span>
</span>
<br/>
<span style="padding-left: 25px;">
<b>valueset: </b>
<span>Suicide Risk Assessment</span>
</span>
</p>
</div>
</td>
</tr>
</table>
<table class="grid dict">
<tr>
<td>
<b>Content: </b>
</td>
</tr>
<tr>
<td style="padding-left: 25px; padding-right: 25px;">
<p style="margin-bottom: 5px;">
<b>type: </b>
<span>text/cql</span>
</p>
<p style="margin-bottom: 5px;">
<b>url: </b>
<span>library-mmi-suiciderisk-orderset-logic-content.cql</span>
</p>
</td>
</tr>
</table>
<p/>
</div>
</text>
<identifier>
<use value="official"/>
<value value="SuicideRiskLogic"/>
</identifier>
<version value="1.0.0"/>
<title value="Suicide Risk Order Set Logic"/>
<type>
<coding>
<code value="logic-library"/>
</coding>
</type>
<status value="draft"/>
<date value="2015-07-22"/>
<description value="Logic for Suicide Risk Order Sets"/>
<topic>
<text value="Suicide Risk Order Set Logic"/>
</topic>
<relatedArtifact>
<type value="depends-on"/>
<resource>
<reference value="Library/library-fhir-model-definition"/>
</resource>
</relatedArtifact>
<relatedArtifact>
<type value="depends-on"/>
<resource>
<reference value="Library/library-fhir-helpers"/>
<display value="FHIRHelpers"/>
</resource>
</relatedArtifact>
<relatedArtifact>
<type value="depends-on"/>
<resource>
<reference value="CodeSystem/npi-taxonomy"/>
</resource>
</relatedArtifact>
<relatedArtifact>
<type value="depends-on"/>
<resource>
<reference value="ValueSet/1.2.3.4.5"/>
<display value="Suicide Risk Assessment"/>
</resource>
</relatedArtifact>
<parameter>
<name value="Patient"/>
<use value="in"/>
<min value="1"/>
<max value="1"/>
<type value="Patient"/>
</parameter>
<parameter>
<name value="Encounter"/>
<use value="in"/>
<min value="1"/>
<max value="1"/>
<type value="Encounter"/>
</parameter>
<parameter>
<name value="Practitioner"/>
<use value="in"/>
<min value="1"/>
<max value="1"/>
<type value="Practitioner"/>
</parameter>
<dataRequirement>
<type value="RiskAssessment"/>
<codeFilter>
<path value="code"/>
<valueSetString value="Suicide Risk Assessment"/>
</codeFilter>
</dataRequirement>
<content>
<contentType value="text/cql"/>
<url value="library-mmi-suiciderisk-orderset-logic-content.cql"/>
</content>
</Library>

View File

@ -0,0 +1,100 @@
<th:block th:fragment="actionDefFrag(actions, contained, num)">
<span th:each="res : ${actions}" th:with="counter=${num}*25">
<b>Step: </b>
<br/>
<span th:style="'padding-left: ' + ${#strings.concat(counter, 'px')} + ';'" th:if="${res.hasLabel()}">
<b>name: </b> <span th:narrative="${res.label}"></span>
<br/>
</span>
<span th:style="'padding-left: ' + ${#strings.concat(counter, 'px')} + ';'" th:if="${res.hasTitle()}">
<b>title: </b> <span th:narrative="${res.title}"></span>
<br/>
</span>
<span th:style="'padding-left: ' + ${#strings.concat(counter, 'px')} + ';'" th:if="${res.hasDescription()}">
<b>description: </b> <span th:narrative="${res.description}"></span>
<br/>
</span>
<span th:style="'padding-left: ' + ${#strings.concat(counter, 'px')} + ';'" th:if="${res.hasTextEquivalent()}">
<b>text: </b> <span th:narrative="${res.textEquivalent}"></span>
<br/>
</span>
<span th:style="'padding-left: ' + ${#strings.concat(counter, 'px')} + ';'" th:each="con : ${res.condition}">
<b>condition: </b>
<br/>
<span th:style="'padding-left: ' + ${#strings.concat(counter+25, 'px')} + ';'">
<b>type: </b>
<span th:narrative="${con.kind.toCode()}"></span>
<br/>
</span>
<span th:style="'padding-left: ' + ${#strings.concat(counter+25, 'px')} + ';'" th:if="${con.hasDescription()}">
<b>description: </b>
<span th:narrative="${con.description}"></span>
<br/>
</span>
<span th:style="'padding-left: ' + ${#strings.concat(counter+25, 'px')} + ';'" th:if="${con.hasExpression()}">
<b>expression: </b>
<span th:narrative="${con.expression}"></span>
<br/>
</span>
</span>
<span th:style="'padding-left: ' + ${#strings.concat(counter, 'px')} + ';'" th:if="${res.hasActivityDefinition()}">
<b>condition: </b>
<br/>
<span th:style="'padding-left: ' + ${#strings.concat(counter+25, 'px')} + ';'" th:if="${res.activityDefinition.hasReference()}">
<b>reference: </b>
<br/>
<span th:style="'padding-left: ' + ${#strings.concat(counter+50, 'px')} + ';'" th:narrative="${res.activityDefinition.reference}"></span>
<br/>
<span th:style="'padding-left: ' + ${#strings.concat(counter+50, 'px')} + ';'" th:each="inline : ${contained}">
<span th:if="${res.activityDefinition.reference.contains(inline.id)}">
<span th:style="'padding-left: ' + ${#strings.concat(counter+75, 'px')} + ';'" th:if="${inline.hasName()}">
<b>name: </b>
<span th:narrative="${inline.name}"></span>
<br/>
</span>
<span th:style="'padding-left: ' + ${#strings.concat(counter+75, 'px')} + ';'" th:if="${inline.hasTitle()}">
<b>title: </b>
<span th:narrative="${inline.title}"></span>
<br/>
</span>
<span th:style="'padding-left: ' + ${#strings.concat(counter+75, 'px')} + ';'" th:if="${inline.hasDescription()}">
<b>description: </b>
<span th:narrative="${inline.description}"></span>
<br/>
</span>
<span th:style="'padding-left: ' + ${#strings.concat(counter+75, 'px')} + ';'" th:if="${inline.hasPurpose()}">
<b>purpose: </b>
<span th:narrative="${inline.purpose}"></span>
<br/>
</span>
<span th:style="'padding-left: ' + ${#strings.concat(counter+75, 'px')} + ';'" th:if="${inline.hasUsage()}">
<b>usage: </b>
<span th:narrative="${inline.usage}"></span>
<br/>
</span>
<span th:style="'padding-left: ' + ${#strings.concat(counter+75, 'px')} + ';'" th:each="related : ${inline.relatedArtifact}">
<b>related:</b>
<br/>
<span th:style="'padding-left: ' + ${#strings.concat(counter+100, 'px')} + ';'" >
<b>type: </b>
<span th:narrative="${related.type.toCode()}"></span>
<br/>
</span>
<span th:style="'padding-left: ' + ${#strings.concat(counter+100, 'px')} + ';'" th:if="${related.hasDisplay()}">
<b>display: </b>
<span th:narrative="${related.display}"></span>
<br/>
</span>
</span>
<span th:style="'padding-left: ' + ${#strings.concat(counter+75, 'px')} + ';'" th:if="${inline.hasCategory()}">
<b>category: </b>
<span th:narrative="${inline.category.toCode()}"></span>
<br/>
</span>
</span>
</span>
</span>
</span>
<span th:style="'padding-left: ' + ${#strings.concat(counter, 'px')} + ';'" th:insert="actiondefcomponent :: actionDefFrag(${res.actionDefinition}, ${contained}, ${num + 1})"></span>
</span>
</th:block>

View File

@ -0,0 +1,87 @@
<th:block th:fragment="actionDefFrag(actions, contained)">
<tr th:each="res : ${actions}">
<td><b>Step: </b></td>
<tr th:if="${res.hasLabel()}">
<tr style="padding-left: 25px;"><b>name: </b></tr>
<tr th:narrative="${res.label}"></tr>
</tr>
<tr th:if="${res.hasTitle()}">
<tr style="padding-left: 25px;"><b>title: </b></tr>
<tr th:narrative="${res.title}"></tr>
</tr>
<tr th:if="${res.hasDescription()}">
<tr style="padding-left: 25px;"><b>description: </b></tr>
<tr th:narrative="${res.description}"></tr>
</tr>
<tr th:if="${res.hasTextEquivalent()}">
<tr style="padding-left: 25px;"><b>text: </b></tr>
<tr th:narrative="${res.textEquivalent}"></tr>
</tr>
<!-- Conditions -->
<tr style="padding-left: 25px;" th:each="con : ${res.condition}">
<tr><td><b>condition: </b></td></tr>
<tr style="padding-left: 25px;">
<td><b>type: </b></td>
<td th:narrative="${con.kind.toCode()}"></td>
</tr>
<tr style="padding-left: 25px;" th:if="${con.hasDescription()}">
<td><b>description: </b></td>
<td th:narrative="${con.description}"></td>
</tr>
<tr style="padding-left: 25px;" th:if="${con.hasExpression()}">
<td><b>expression: </b></td>
<td th:narrative="${con.expression}"></td>
</tr>
</tr>
<!-- Activity Definitions -->
<td style="padding-left: 25px;" th:if="${res.hasActivityDefinition()}">
<b>condition: </b>
<tr style="padding-left: 25px;" th:if="${res.activityDefinition.hasReference()}">
<b>reference: </b>
<td th:narrative="${res.activityDefinition.reference}"></td>
<td th:each="inline : ${contained}">
<tr style="padding-left: 25px;" th:if="${res.activityDefinition.reference.contains(inline.id)}">
<tr th:if="${inline.hasName()}">
<b>name: </b>
<td th:narrative="${inline.name}"></td>
</tr>
<tr th:if="${inline.hasTitle()}">
<b>title: </b>
<td th:narrative="${inline.title}"></td>
</tr>
<tr th:if="${inline.hasDescription()}">
<b>description: </b>
<td th:narrative="${inline.description}"></td>
</tr>
<tr th:if="${inline.hasPurpose()}">
<b>purpose: </b>
<td th:narrative="${inline.purpose}"></td>
</tr>
<tr th:if="${inline.hasUsage()}">
<b>usage: </b>
<td th:narrative="${inline.usage}"></td>
</tr>
<tr th:each="related : ${inline.relatedArtifact}">
<b>related:</b>
<tr style="padding-left: 25px;">
<b>type: </b>
<td th:narrative="${related.type.toCode()}"></td>
</tr>
<tr style="padding-left: 25px;" th:if="${related.hasDisplay()}">
<b>display: </b>
<td th:narrative="${related.display}"></td>
</tr>
</tr>
<tr th:if="${inline.hasCategory()}">
<b>category: </b>
<td th:narrative="${inline.category.toCode()}"></td>
</tr>
</tr>
</td>
</tr>
</td>
<td style="padding-left: 25px;" th:include="actiondefcomponent :: actionDefFrag(${res.actionDefinition}, ${contained})"></td>
</tr>
</th:block>

View File

@ -0,0 +1,236 @@
<div xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<table th:if="${resource.hasId()}" class="grid dict">
<tr>
<td><b>Id: </b></td>
</tr>
<tr>
<td style="padding-left: 25px; padding-right: 25px;" th:narrative="${resource.id}"></td>
</tr>
</table>
<p th:if="${resource.hasId() and not resource.hasIdentifier()}"></p>
<table th:each="ident : ${resource.identifier}" class="grid dict">
<tr>
<td><br/><b>Identifier: </b></td>
</tr>
<tr th:if="${ident.hasSystem()}">
<td style="padding-left: 25px; padding-right: 25px;">
<b>system: </b> <span th:narrative="${ident.system}"></span>
</td>
</tr>
<tr th:if="${ident.hasValue()}">
<td style="padding-left: 25px; padding-right: 25px;">
<b>value: </b> <span th:narrative="${ident.value}"></span>
</td>
</tr>
</table>
<p th:if="${resource.hasIdentifier()}"></p>
<table th:if="${resource.hasTitle()}" class="grid dict">
<tr>
<td><b>Title: </b></td>
</tr>
<tr>
<td style="padding-left: 25px; padding-right: 25px;" th:narrative="${resource.title}"></td>
</tr>
</table>
<p th:if="${resource.hasTitle()}"></p>
<table class="grid dict">
<tr>
<td><b>Status: </b></td>
</tr>
<tr>
<td style="padding-left: 25px; padding-right: 25px;" th:narrative="${resource.status.toCode()}"></td>
</tr>
</table>
<p></p>
<table th:if="${resource.hasDescription()}" class="grid dict">
<tr>
<td><b>Description: </b></td>
</tr>
<tr>
<td style="padding-left: 25px; padding-right: 25px;" th:narrative="${resource.description}"></td>
</tr>
</table>
<p th:if="${resource.hasDescription()}"></p>
<table th:if="${resource.hasPurpose()}" class="grid dict">
<tr>
<td><b>Purpose: </b></td>
</tr>
<tr>
<td style="padding-left: 25px; padding-right: 25px;" th:narrative="${resource.purpose}"></td>
</tr>
</table>
<p th:if="${resource.hasPurpose()}"></p>
<table th:if="${resource.hasUsage()}" class="grid dict">
<tr>
<td><b>Usage: </b></td>
</tr>
<tr>
<td style="padding-left: 25px; padding-right: 25px;" th:narrative="${resource.usage}"></td>
</tr>
</table>
<p th:if="${resource.hasUsage()}"></p>
<table th:each="context : ${resource.useContext}" class="grid dict">
<tr>
<td><b>Use Context: </b></td>
</tr>
<tr>
<td style="padding-left: 25px; padding-right: 25px;" th:narrative="${context}"></td>
</tr>
</table>
<p th:if="${resource.hasUseContext()}"></p>
<table th:each="topics : ${resource.topic}" class="grid dict">
<tr>
<td><b>Topic: </b></td>
</tr>
<tr>
<td style="padding-right: 25px;" th:narrative="${topics}"></td>
</tr>
</table>
<p th:if="${resource.hasTopic()}"></p>
<table th:each="cont : ${resource.contributor}" class="grid dict">
<tr>
<td><b>Contributor: </b></td>
</tr>
<tr>
<td style="padding-left: 25px; padding-right: 25px;"><b><span th:narrative="${cont.type.toCode()}"></span>: </b></td>
</tr>
<tr>
<td style="padding-left: 50px; padding-right: 25px;" th:narrative="${cont.name}"></td>
</tr>
</table>
<p th:if="${resource.hasContributor()}"></p>
<table th:each="related : ${resource.relatedArtifact}" class="grid dict">
<tr>
<td><b>Related: </b></td>
</tr>
<tr style="vertical-align: top;">
<td style="padding-left: 25px; padding-right: 25px;" th:narrative="${related}"></td>
</tr>
</table>
<p th:if="${resource.hasRelatedArtifact()}"></p>
<table th:each="lib : ${resource.library}" class="grid dict">
<tr>
<td><b>Library: </b></td>
</tr>
<tr th:if="${lib.hasReference()}">
<td style="padding-left: 25px; padding-right: 25px;">
<b>reference: </b>
<span th:narrative="${lib.reference}"></span>
</td>
</tr>
<tr th:if="${lib.hasIdentifier()}">
<td style="padding-left: 25px; padding-right: 25px;">
<b>identifier: </b>
<span th:narrative="${lib.identifier}"></span>
</td>
</tr>
<tr th:if="${lib.hasDisplay()}">
<td style="padding-left: 25px; padding-right: 25px;">
<b>display: </b>
<span th:narrative="${lib.display}"></span>
</td>
</tr>
</table>
<p th:if="${resource.hasLibrary()}"></p>
<table th:if="${resource.hasCategory()}" class="grid dict">
<tr>
<td><b>Category: </b></td>
</tr>
<tr>
<td style="padding-left: 25px; padding-right: 25px;" th:narrative="${resource.category.toCode()}"></td>
</tr>
</table>
<p th:if="${resource.hasCategory()}"></p>
<table th:if="${resource.hasCode()}" class="grid dict">
<tr>
<td><b>Code: </b></td>
</tr>
<tr>
<td style="padding-right: 25px;" th:narrative="${resource.code}"></td>
</tr>
</table>
<p th:if="${resource.hasCode()}"></p>
<table th:if="${resource.hasTimingCodeableConcept()}" class="grid dict">
<tr>
<td><b>Timing: </b></td>
</tr>
<tr>
<td style="padding-left: 25px; padding-right: 25px;" th:narrative="${resource.timingCodeableConcept}"></td>
</tr>
</table>
<p th:if="${resource.hasTimingCodeableConcept()}"></p>
<table th:if="${resource.hasLocation()}" class="grid dict">
<tr>
<td><b>Location: </b></td>
</tr>
<tr>
<td style="padding-left: 25px; padding-right: 25px;" th:narrative="${resource.location}"></td>
</tr>
</table>
<p th:if="${resource.hasLocation()}"></p>
<table th:each="participant : ${resource.participantType}" class="grid dict">
<tr>
<td><b>Participant: </b></td>
</tr>
<tr style="vertical-align: top;">
<td style="padding-left: 25px; padding-right: 25px;" th:narrative="${participant.getValue().toCode()}"></td>
</tr>
</table>
<p th:if="${resource.hasParticipantType()}"></p>
<table th:if="${resource.hasProductCodeableConcept()}" class="grid dict">
<tr>
<td><b>Product Concept: </b></td>
</tr>
<tr>
<td style="padding-left: 25px; padding-right: 25px;" th:narrative="${resource.productCodeableConcept}"></td>
</tr>
</table>
<p th:if="${resource.hasProductCodeableConcept()}"></p>
<table th:if="${resource.hasQuantity()}" class="grid dict">
<tr>
<td><b>Quantity: </b></td>
</tr>
<tr>
<td style="padding-left: 25px; padding-right: 25px;" th:narrative="${resource.quantity}"></td>
</tr>
</table>
<p th:if="${resource.hasQuantity()}"></p>
</div>

View File

@ -0,0 +1,18 @@
<th:block th:if="${not resource.empty}">
<p style="margin-bottom: 5px;" th:if="${resource.hasTitle()}">
<b>title: </b>
<span th:narrative="${resource.title}"></span>
</p>
<p style="margin-bottom: 5px;" th:if="${resource.hasContentType()}">
<b>type: </b>
<span th:narrative="${resource.contentType}"></span>
</p>
<p style="margin-bottom: 5px;" th:if="${resource.hasLanguage()}">
<b>language: </b>
<span th:narrative="${resource.language}"></span>
</p>
<p style="margin-bottom: 5px;" th:if="${resource.hasUrl()}">
<b>url: </b>
<span th:narrative="${resource.url}"></span>
</p>
</th:block>

View File

@ -0,0 +1,10 @@
<th:block th:if="${not resource.empty}">
<span style="padding-left: 25px;" th:if="${resource.hasText()}">
<b>text: </b>
<span th:text="${resource.text}"></span>
</span>
<span th:each="codings : ${resource.coding}">
<br th:if="${resource.hasText()}" />
<span th:narrative="${codings}"></span>
</span>
</th:block>

View File

@ -0,0 +1,16 @@
<th:block th:if="${not resource.empty}">
<span style="padding-left: 25px;" th:if="${resource.hasSystem()}">
<b>system: </b>
<span th:text="${resource.system}"></span>
<br th:if="${resource.hasCode() or resource.hasDisplay()}" />
</span>
<span style="padding-left: 25px;" th:if="${resource.hasCode()}">
<b>code: </b>
<span th:text="${resource.code}"></span>
<br th:if="${resource.hasDisplay()}" />
</span>
<span style="padding-left: 25px;" th:if="${resource.hasDisplay()}">
<b>display: </b>
<span th:text="${resource.display}"></span>
</span>
</th:block>

View File

@ -0,0 +1,88 @@
<div xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<p style="margin-bottom: 5px;" th:if="${resource.hasId()}">
<b>id: </b>
<span th:narrative="${resource.id}"></span>
</p>
<p style="margin-bottom: 5px;">
<b>type: </b>
<span th:narrative="${resource.type}"></span>
</p>
<p style="margin-bottom: 5px;" th:each="pro : ${resource.profile}">
<b>profile: </b>
<span th:narrative="${pro}"></span>
</p>
<p style="margin-bottom: 5px;" th:each="must : ${resource.mustSupport}">
<b>must support: </b>
<span th:narrative="${must}"></span>
</p>
<p style="margin-bottom: 5px;" th:each="cFilter : ${resource.codeFilter}">
<b>code filter:</b>
<br/>
<span style="padding-left: 25px;">
<b>path: </b>
<span th:narrative="${cFilter.path}"></span>
</span>
<br th:if="${cFilter.hasValueSetStringType() or cFilter.hasValueSetReference()}" />
<span style="padding-left: 25px;" th:if="${cFilter.hasValueSetStringType()}">
<b>valueset: </b>
<span th:narrative="${cFilter.valueSetStringType}"></span>
</span>
<span style="padding-left: 25px;" th:if="${cFilter.hasValueSetReference()}">
<b>valueset: </b>
<span th:narrative="${cFilter.valueSetReference}"></span>
</span>
<br th:if="${cFilter.hasValueCode()}" />
<span style="padding-left: 25px;" th:each="codes : ${cFilter.valueCode}">
<b>code:</b>
<span th:narrative="${codes}"></span>
</span>
<br th:if="${cFilter.hasValueCoding()}" />
<span style="padding-left: 25px;" th:each="codings : ${cFilter.valueCoding}">
<br />
<b>coding:</b>
<span style="padding-left: 25px;" th:narrative="${codings}"></span>
</span>
<br th:if="${cFilter.hasValueCodeableConcept()}" />
<span style="padding-left: 25px;" th:each="concepts : ${cFilter.valueCodeableConcept}">
<b>concept:</b>
<span style="padding-left: 25px;" th:narrative="${concepts}"></span>
</span>
</p>
<p style="margin-bottom: 5px;" th:each="dFilter : ${resource.dateFilter}">
<b>date filter:</b>
<span style="padding-left: 25px;">
<b>path: </b>
<span th:narrative="${dFilter.path}"></span>
</span>
<br/>
<span th:if="${dFilter.hasValueDateTime()}">
<b>date time: </b>
<span th:narrative="${dFilter.valueDateTime}"></span>
</span>
<span th:if="${dFilter.hasValuePeriod()}">
<b>date period: </b>
<span style="padding-left: 25px;" th:if="${dFilter.valuePeriod.hasStart()}">
<br/>
<b>start: </b>
<span th:narrative="${dFilter.valuePeriod.start}"></span>
</span>
<span style="padding-left: 25px;" th:if="${dFilter.valuePeriod.hasEnd()}">
<b>end: </b>
<span th:narrative="${dFilter.valuePeriod.end}"></span>
</span>
</span>
<span th:if="${dFilter.hasValueDuration()}">
<b>date duration: </b>
<br/>
<span style="padding-left: 25px;">
<b>duration: </b>
<span th:narrative="${dFilter.valueDuration}"></span>
</span>
</span>
</p>
</div>

View File

@ -0,0 +1 @@
<th:block th:text="${resource.toString()}" />

View File

@ -0,0 +1,36 @@
<div xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<!-- INVALID - NEEDS WORK -->
<div th:if="${resource.hasId()}">
<b>id: </b>
<span th:narrative="${resource.id}"></span>
</div>
<br th:if="${resource.hasId()}" />
<div th:if="${resource.hasRequestId()}">
<b>request id: </b>
<span th:narrative="${resource.requestId}"></span>
</div>
<br th:if="${resource.hasRequestId()}" />
<div th:each="ident : ${resource.identifier}">
<span th:narrative="${ident}"></span>
</div>
<br th:if="${resource.hasIdentifier()}" />
<div>
<b>reference to knowledge module: </b>
<div style="padding-left: 25px;" th:narrative="${resource.module}"></div>
</div>
<br />
<div>
<b>status: </b>
<span th:narrative="${resource.status.toCode()}"></span>
</div>
<br />
</div>

View File

@ -0,0 +1,12 @@
<th:block th:if="${not resource.empty}">
<b>Identifier:</b>
<br/>
<span style="padding-left: 25px;" th:if="${resource.hasSystem()}">
<b>system: </b> <span th:narrative="${resource.system}"></span>
<br/>
</span>
<span style="padding-left: 25px;" th:if="${resource.hasValue()}">
<b>value: </b> <span th:narrative="${resource.value}"></span>
<br/>
</span>
</th:block>

View File

@ -0,0 +1 @@
<th:block th:text="${resource.toString()}" />

Some files were not shown because too many files have changed in this diff Show More