diff --git a/docker/aktin-client/.env b/docker/aktin-client/.env
index c830d48..3e53bc2 100644
--- a/docker/aktin-client/.env
+++ b/docker/aktin-client/.env
@@ -22,3 +22,4 @@ LOG_LEVEL_FEASIBILITY=FINE
HARD_RATE_LIMIT_NREQUESTS=2
HARD_RATE_LIMIT_RESET_MINUTES=1
OBFUSCATOR_EPSILON=0.28
+CQL_SOCKET_TIMEOUT_MS=60000
diff --git a/docker/aktin-client/sysproc.properties b/docker/aktin-client/sysproc.properties
index d600ce6..8198a2e 100644
--- a/docker/aktin-client/sysproc.properties
+++ b/docker/aktin-client/sysproc.properties
@@ -21,6 +21,7 @@ process.args=${PROCESS_ARGS}
plugin.feasibility.cql.fhirbaseurl=${CQL_FHIR_BASE_URL}
plugin.feasibility.cql.fhiruser=${CQL_FHIR_USER}
plugin.feasibility.cql.fhirpw=${CQL_FHIR_PW}
+plugin.feasibility.cql.sockettimeout=${CQL_SOCKET_TIMEOUT_MS}
plugin.feasibility.flare.url=${FLARE_URL}
plugin.feasibility.flare.user=${FLARE_USER}
plugin.feasibility.flare.pw=${FLARE_PW}
diff --git a/pom.xml b/pom.xml
index 5bba687..3a9bd27 100644
--- a/pom.xml
+++ b/pom.xml
@@ -112,6 +112,18 @@
2.14.1
+
+ com.fasterxml.jackson.dataformat
+ jackson-dataformat-xml
+ 2.14.1
+
+
+
+ com.google.guava
+ guava
+ 32.1.2-jre
+
+
diff --git a/src/main/java/feasibility/FeasibilityExecutionPlugin.java b/src/main/java/feasibility/FeasibilityExecutionPlugin.java
index fe1755f..7e2450a 100644
--- a/src/main/java/feasibility/FeasibilityExecutionPlugin.java
+++ b/src/main/java/feasibility/FeasibilityExecutionPlugin.java
@@ -48,12 +48,15 @@ protected void loadConfig(Properties properties) throws IOException{
String fhirUserCql = properties.getProperty("plugin.feasibility.cql.fhiruser");
String fhirPwCql = properties.getProperty("plugin.feasibility.cql.fhirpw");
+ int socketTimeout = Integer.valueOf(properties.getProperty("plugin.feasibility.cql.sockettimeout"));
if(! fhirUserCql.equals("") && ! fhirPwCql.equals("")) {
IClientInterceptor authInterceptor = new BasicAuthInterceptor(fhirUserCql, fhirPwCql);
genFhirClient.registerInterceptor(authInterceptor);
}
+ fhirContext.getRestfulClientFactory().setSocketTimeout(socketTimeout);
+
this.cqlExecutor = new CqlExecutor(
new FhirConnector(fhirContext.newRestfulGenericClient(fhirBaseUrl)),
new FhirHelper(fhirContext)
diff --git a/src/main/java/feasibilityValidator/SqValidator.java b/src/main/java/feasibilityValidator/SqValidator.java
index 4971aed..777becc 100644
--- a/src/main/java/feasibilityValidator/SqValidator.java
+++ b/src/main/java/feasibilityValidator/SqValidator.java
@@ -1,6 +1,5 @@
package feasibilityValidator;
-import feasibility.CqlExecutor;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
diff --git a/src/main/resources/feasibilityValidator/sq-schema.json b/src/main/resources/feasibilityValidator/sq-schema.json
index 9ba3e2d..396c8c3 100644
--- a/src/main/resources/feasibilityValidator/sq-schema.json
+++ b/src/main/resources/feasibilityValidator/sq-schema.json
@@ -1,167 +1,389 @@
{
- "$schema": "http://to_be_decided.com/draft-2/schema#",
-
+ "$schema": "http://medizininformatik-initiative/v3/StructuredQuery/schema#",
"definitions": {
-
"termCode": {
"type": "object",
"description": "The termCode defines a concept based on a coding system (i.e. LOINC). The triplet of code, system and version identify the concept.",
"properties": {
- "code": { "type": "string" },
- "system": { "type": "string"},
- "version": { "type": "string"},
- "display": { "type": "string"}
+ "code": {
+ "type": "string"
+ },
+ "system": {
+ "type": "string"
+ },
+ "version": {
+ "type": "string"
+ },
+ "display": {
+ "type": "string"
+ }
},
- "required": ["code", "system", "display"],
+ "required": [
+ "code",
+ "system",
+ "display"
+ ]
},
-
"criterion": {
"type": "object",
"properties": {
+ "context": {
+ "$ref": "#/definitions/termCode"
+ },
"termCodes": {
"type": "array",
"minItems": 1,
- "items": { "$ref": "#/definitions/termCode" }
+ "items": {
+ "$ref": "#/definitions/termCode"
+ }
+ },
+ "valueFilter": {
+ "$ref": "#/definitions/valueFilter"
},
- "valueFilter": { "$ref": "#/definitions/valueFilter"},
- "attributeFilters": { "$ref": "#/definitions/attributeFilters"},
- "timeRestriction": { "$ref": "#/definitions/timeRestriction"}
+ "attributeFilters": {
+ "$ref": "#/definitions/attributeFilters"
+ },
+ "timeRestriction": {
+ "$ref": "#/definitions/timeRestriction"
+ }
},
- "required": ["termCodes"]
+ "required": [
+ "termCodes",
+ "context"
+ ]
},
-
"timeRestriction": {
"type": "object",
"description": "TimeRestirction specify the interval within the critiera has to be fullfilled. An intersection of the criterias interval with the interval defined in this timeRestriction is sufficient",
"properties": {
- "beforeDate": { "type": "string", "format": "date"},
- "afterDate": { "type": "string", "format": "date"}
+ "beforeDate": {
+ "type": "string",
+ "format": "date"
+ },
+ "afterDate": {
+ "type": "string",
+ "format": "date"
+ }
},
- "anyOf": [ {"required": ["beforeDate"]}, {"required": ["afterDate"]}]
+ "anyOf": [
+ {
+ "required": [
+ "beforeDate"
+ ]
+ },
+ {
+ "required": [
+ "afterDate"
+ ]
+ }
+ ]
},
-
"unit": {
"type": "object",
"title": "UCUM Unit",
"description": "The unit is a ucum unit (https://ucum.org/trac)",
"properties": {
- "code": { "type": "string" },
- "display": {"type": "string"}
+ "code": {
+ "type": "string"
+ },
+ "display": {
+ "type": "string"
+ }
},
- "required": ["code", "display"]
+ "required": [
+ "code",
+ "display"
+ ]
},
-
"attributeFilters": {
"type": "array",
- "items": {"$ref": "#/definitions/attributeFilter"}
+ "items": {
+ "$ref": "#/definitions/attributeFilter"
+ }
},
-
"valueFilter": {
"type": "object",
"properties": {
- "type": { "enum": ["concept", "quantity-comparator", "quantity-range"]}
+ "type": {
+ "enum": [
+ "concept",
+ "quantity-comparator",
+ "quantity-range"
+ ]
+ }
},
- "required": ["type"],
+ "required": [
+ "type"
+ ],
"allOf": [
{
"if": {
- "properties": {"type": {"const": "concept"}}
+ "properties": {
+ "type": {
+ "const": "concept"
+ }
+ }
},
"then": {
"properties": {
- "type": {"const": "concept"},
+ "type": {
+ "const": "concept"
+ },
"selectedConcepts": {
"type": "array",
"minItems": 1,
- "items": {"$ref": "#/definitions/termCode"}
+ "items": {
+ "$ref": "#/definitions/termCode"
+ }
}
},
- "required": ["type", "selectedConcepts"]
+ "required": [
+ "type",
+ "selectedConcepts"
+ ]
}
},
{
"if": {
- "properties": {"type": {"const": "quantity-comperator"}}
+ "properties": {
+ "type": {
+ "const": "quantity-comperator"
+ }
+ }
},
"then": {
"properties": {
- "type": {"const": "quantity-comperator"},
- "comparator": { "enum": ["gt", "ge", "lt", "le", "eq", "ne"] },
- "value": { "type": "number" },
- "unit" : {"$ref": "#/definitions/unit" }
+ "type": {
+ "const": "quantity-comperator"
+ },
+ "comparator": {
+ "enum": [
+ "gt",
+ "ge",
+ "lt",
+ "le",
+ "eq",
+ "ne"
+ ]
+ },
+ "value": {
+ "type": "number"
+ },
+ "unit": {
+ "$ref": "#/definitions/unit"
+ }
},
- "required": ["type", "comparator", "value"]
+ "required": [
+ "type",
+ "comparator",
+ "value"
+ ]
}
},
{
- "if" : {
- "properties": {"type": {"const": "quantity-range"}}
+ "if": {
+ "properties": {
+ "type": {
+ "const": "quantity-range"
+ }
+ }
},
- "then" : {
+ "then": {
"properties": {
- "type": {"const": "quantity-range"},
- "minValue": { "type": "number" },
- "maxValue": { "type": "number" },
- "unit" : {"$ref": "#/definitions/unit" }
+ "type": {
+ "const": "quantity-range"
+ },
+ "minValue": {
+ "type": "number"
+ },
+ "maxValue": {
+ "type": "number"
+ },
+ "unit": {
+ "$ref": "#/definitions/unit"
+ }
},
- "required": ["type", "minValue", "maxValue"]
+ "required": [
+ "type",
+ "minValue",
+ "maxValue"
+ ]
}
}
]
},
-
"attributeFilter": {
"type": "object",
"properties": {
- "type": { "enum": ["concept", "quantity-comparator", "quantity-range"]}
+ "type": {
+ "enum": [
+ "concept",
+ "quantity-comparator",
+ "quantity-range",
+ "reference"
+ ]
+ }
},
- "required": ["type"],
+ "required": [
+ "type"
+ ],
"allOf": [
{
"if": {
- "properties": {"type": {"const": "concept"}}
+ "properties": {
+ "type": {
+ "const": "concept"
+ }
+ }
},
"then": {
"properties": {
- "attributeCode": {"$ref": "#/definitions/termCode"},
- "type": {"const": "concept"},
+ "attributeCode": {
+ "$ref": "#/definitions/termCode"
+ },
+ "type": {
+ "const": "concept"
+ },
"selectedConcepts": {
"type": "array",
"minItems": 1,
- "items": {"$ref": "#/definitions/termCode"}
+ "items": {
+ "$ref": "#/definitions/termCode"
+ }
}
},
- "required": ["type", "selectedConcepts", "attributeCode"]
+ "required": [
+ "type",
+ "selectedConcepts",
+ "attributeCode"
+ ]
}
},
{
"if": {
- "properties": {"type": {"const": "quantity-comperator"}}
+ "properties": {
+ "type": {
+ "const": "quantity-comperator"
+ }
+ }
},
"then": {
"properties": {
- "attributeCode": {"$ref": "#/definitions/termCode"},
- "type": {"const": "quantity-comperator"},
- "comparator": { "enum": ["gt", "ge", "lt", "le", "eq", "ne"] },
- "value": { "type": "number" },
- "unit" : {"$ref": "#/definitions/unit" }
+ "attributeCode": {
+ "$ref": "#/definitions/termCode"
+ },
+ "type": {
+ "const": "quantity-comperator"
+ },
+ "comparator": {
+ "enum": [
+ "gt",
+ "ge",
+ "lt",
+ "le",
+ "eq",
+ "ne"
+ ]
+ },
+ "value": {
+ "type": "number"
+ },
+ "unit": {
+ "$ref": "#/definitions/unit"
+ }
},
- "required": ["type", "comparator", "value", "attributeCode"]
+ "required": [
+ "type",
+ "comparator",
+ "value",
+ "attributeCode"
+ ]
}
},
{
- "if" : {
- "properties": {"type": {"const": "quantity-range"}}
+ "if": {
+ "properties": {
+ "type": {
+ "const": "quantity-range"
+ }
+ }
},
- "then" : {
+ "then": {
"properties": {
- "attributeCode": {"$ref": "#/definitions/termCode"},
- "type": {"const": "quantity-range"},
- "minValue": { "type": "number" },
- "maxValue": { "type": "number" },
- "unit" : {"$ref": "#/definitions/unit" }
+ "attributeCode": {
+ "$ref": "#/definitions/termCode"
+ },
+ "type": {
+ "const": "quantity-range"
+ },
+ "minValue": {
+ "type": "number"
+ },
+ "maxValue": {
+ "type": "number"
+ },
+ "unit": {
+ "$ref": "#/definitions/unit"
+ }
},
- "required": ["type", "minValue", "maxValue", "attributeCode"]
+ "required": [
+ "type",
+ "minValue",
+ "maxValue",
+ "attributeCode"
+ ]
+ }
+ },
+ {
+ "if": {
+ "properties": {
+ "type": {
+ "const": "reference"
+ }
+ }
+ },
+ "then": {
+ "properties": {
+ "attributeCode": {
+ "$ref": "#/definitions/termCode"
+ },
+ "type": {
+ "const": "reference"
+ },
+ "criteria": {
+ "type": "array",
+ "minItems": 1,
+ "items": {
+ "allOf": [
+ {
+ "properties": {
+ "attributeFilters": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/attributeFilter",
+ "not": {
+ "properties": {
+ "type": {
+ "const": "reference"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "$ref": "#/definitions/criterion"
+ }
+ ]
+ }
+ }
+ },
+ "required": [
+ "type",
+ "criteria",
+ "attributeCode"
+ ]
}
}
]
@@ -172,7 +394,10 @@
"description": "Within a query the inclusion and exclusion criteria are conjuncted with AND NOT",
"type": "object",
"properties": {
- "version": {"type": "string", "format": "uri"},
+ "version": {
+ "type": "string",
+ "format": "uri"
+ },
"inclusionCriteria": {
"type": "array",
"minItems": 1,
@@ -181,7 +406,9 @@
"type": "array",
"minItems": 1,
"description": "All elements within the array are conjuncted with an OR operator",
- "items": { "$ref": "#/definitions/criterion" }
+ "items": {
+ "$ref": "#/definitions/criterion"
+ }
}
},
"exclusionCriteria": {
@@ -192,10 +419,17 @@
"type": "array",
"minItems": 1,
"description": "All elements within the array are conjuncted with an AND operator",
- "items": { "$ref": "#/definitions/criterion" }
+ "items": {
+ "$ref": "#/definitions/criterion"
+ }
}
},
- "display" : {"type": "string"}
+ "display": {
+ "type": "string"
+ }
},
- "required": ["version", "inclusionCriteria" ]
-}
\ No newline at end of file
+ "required": [
+ "version",
+ "inclusionCriteria"
+ ]
+}v3
\ No newline at end of file
diff --git a/src/test/resources/example-sq.json b/src/test/resources/example-sq.json
index 6284165..c62cb23 100644
--- a/src/test/resources/example-sq.json
+++ b/src/test/resources/example-sq.json
@@ -3,19 +3,26 @@
"inclusionCriteria": [
[
{
+ "attributeFilters": [],
"termCodes": [
{
"code": "263495000",
- "system": "http://snomed.info/sct",
- "display": "Geschlecht"
+ "display": "Geschlecht",
+ "system": "http://snomed.info/sct"
}
],
+ "context": {
+ "code": "Patient",
+ "display": "Patient",
+ "system": "fdpg.mii.cds",
+ "version": "1.0.0"
+ },
"valueFilter": {
"selectedConcepts": [
{
- "code": "female",
- "system": "http://hl7.org/fhir/administrative-gender",
- "display": "Female"
+ "code": "male",
+ "display": "Male",
+ "system": "http://hl7.org/fhir/administrative-gender"
}
],
"type": "concept"
diff --git a/src/test/resources/sysproc.properties b/src/test/resources/sysproc.properties
index ceb99b1..886568a 100644
--- a/src/test/resources/sysproc.properties
+++ b/src/test/resources/sysproc.properties
@@ -9,7 +9,7 @@ properties.mapvars=env
# e.g. broker.endpoint.uri=http://${HOST}:${PORT}/broker/
-#roker.request.mediatype=text/cql
+#broker.request.mediatype=text/cql
broker.request.mediatype=application/sq+json
broker.result.mediatype=application/json
broker.endpoint.uri=http://localhost:8082/broker/
@@ -35,6 +35,7 @@ client.executor.threads=1
plugin.feasibility.cql.fhirbaseurl=http://localhost:8081/fhir
plugin.feasibility.cql.fhiruser=
plugin.feasibility.cql.fhirpw=
+plugin.feasibility.cql.sockettimeout=60000
plugin.feasibility.flare.url=http://localhost:8084/query/execute
plugin.feasibility.flare.user=
plugin.feasibility.flare.pw=