diff --git a/pom.xml b/pom.xml
index da9a155b..71513e90 100644
--- a/pom.xml
+++ b/pom.xml
@@ -213,7 +213,7 @@
de.medizininformatik-initiative
sq2cql
- 0.5.0
+ 0.6.0
diff --git a/src/main/java/de/numcodex/feasibility_gui_backend/query/api/status/ValidationIssue.java b/src/main/java/de/numcodex/feasibility_gui_backend/query/api/status/ValidationIssue.java
index f42212a1..6692323c 100644
--- a/src/main/java/de/numcodex/feasibility_gui_backend/query/api/status/ValidationIssue.java
+++ b/src/main/java/de/numcodex/feasibility_gui_backend/query/api/status/ValidationIssue.java
@@ -7,7 +7,8 @@
@Slf4j
@JsonSerialize(using = ValidationIssueSerializer.class)
public enum ValidationIssue {
- TERMCODE_CONTEXT_COMBINATION_INVALID(20001, "The combination of context and termcode(s) is not found.");
+ TERMCODE_CONTEXT_COMBINATION_INVALID(20001, "The combination of context and termcode(s) is not found."),
+ TIMERESTRICTION_INVALID(20002, "The TimeRestriction is invalid. 'beforeDate' must not be before 'afterDate'");
private static final ValidationIssue[] VALUES;
diff --git a/src/main/java/de/numcodex/feasibility_gui_backend/terminology/validation/StructuredQueryValidation.java b/src/main/java/de/numcodex/feasibility_gui_backend/terminology/validation/StructuredQueryValidation.java
index a1cf677b..d688580b 100644
--- a/src/main/java/de/numcodex/feasibility_gui_backend/terminology/validation/StructuredQueryValidation.java
+++ b/src/main/java/de/numcodex/feasibility_gui_backend/terminology/validation/StructuredQueryValidation.java
@@ -5,8 +5,11 @@
import de.numcodex.feasibility_gui_backend.common.api.TermCode;
import de.numcodex.feasibility_gui_backend.query.api.MutableStructuredQuery;
import de.numcodex.feasibility_gui_backend.query.api.StructuredQuery;
+import de.numcodex.feasibility_gui_backend.query.api.TimeRestriction;
import de.numcodex.feasibility_gui_backend.query.api.status.ValidationIssue;
import de.numcodex.feasibility_gui_backend.terminology.TerminologyService;
+
+import java.time.LocalDate;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
@@ -86,6 +89,10 @@ private void annotateCriteria(List criteria, boolean skipValid
criterion.setValidationIssues(List.of(ValidationIssue.TERMCODE_CONTEXT_COMBINATION_INVALID));
continue;
}
+ if (isTimeRestrictionInvalid(criterion.getTimeRestriction())) {
+ criterion.setValidationIssues(List.of(ValidationIssue.TIMERESTRICTION_INVALID));
+ continue;
+ }
for (TermCode termCode : criterion.getTermCodes()) {
if (terminologyService.isExistingTermCode(termCode.system(), termCode.code())) {
log.trace("termcode ok: {} - {}", termCode.system(), termCode.code());
@@ -103,6 +110,11 @@ private boolean containsInvalidCriteria(List criteria) {
if (criterion.context() == null) {
return true;
}
+ if (isTimeRestrictionInvalid(criterion.timeRestriction())) {
+ log.debug("TimeRestriction invalid. 'beforeDate' ({}) must be after 'afterDate' ({}) but is not",
+ criterion.timeRestriction().beforeDate(), criterion.timeRestriction().afterDate());
+ return true;
+ }
for (TermCode termCode : criterion.termCodes()) {
if (terminologyService.isExistingTermCode(termCode.system(), termCode.code())) {
log.trace("termcode ok: {} - {}", termCode.system(), termCode.code());
@@ -115,4 +127,12 @@ private boolean containsInvalidCriteria(List criteria) {
}
return false;
}
+
+ private boolean isTimeRestrictionInvalid(TimeRestriction timeRestriction) {
+ // If no timeRestriction is set, it is not invalid
+ if (timeRestriction == null) {
+ return false;
+ }
+ return LocalDate.parse(timeRestriction.beforeDate()).isBefore(LocalDate.parse(timeRestriction.afterDate()));
+ }
}
diff --git a/src/test/java/de/numcodex/feasibility_gui_backend/terminology/validation/StructuredQueryValidationTest.java b/src/test/java/de/numcodex/feasibility_gui_backend/terminology/validation/StructuredQueryValidationTest.java
index aaddc12a..e4ab71b5 100644
--- a/src/test/java/de/numcodex/feasibility_gui_backend/terminology/validation/StructuredQueryValidationTest.java
+++ b/src/test/java/de/numcodex/feasibility_gui_backend/terminology/validation/StructuredQueryValidationTest.java
@@ -3,6 +3,7 @@
import de.numcodex.feasibility_gui_backend.common.api.Criterion;
import de.numcodex.feasibility_gui_backend.common.api.TermCode;
import de.numcodex.feasibility_gui_backend.query.api.StructuredQuery;
+import de.numcodex.feasibility_gui_backend.query.api.TimeRestriction;
import de.numcodex.feasibility_gui_backend.terminology.TerminologyService;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.BeforeEach;
@@ -62,6 +63,13 @@ void testIsValid_falseOnMissingContext() {
assertFalse(isValid);
}
+ @Test
+ void testIsValid_falseOnInvalidTimeRestriction() {
+ var isValid = structuredQueryValidation.isValid(createStructuredQueryWithInvalidTimeRestriction());
+
+ assertFalse(isValid);
+ }
+
@ParameterizedTest
@CsvSource({"true,true", "true,false", "false,true", "false,false"})
void testAnnotateStructuredQuery_emptyIssuesOnValidCriteriaOrSkippedValidation(String withExclusionCriteriaString, String skipValidationString) {
@@ -106,6 +114,18 @@ void testAnnotateStructuredQuery_nonEmptyIssuesOnMissingContext(boolean skipVali
}
}
+ @ParameterizedTest
+ @ValueSource(booleans = {true, false})
+ void testAnnotateStructuredQuery_nonEmptyIssuesOnInvalidTimeRestriction(boolean skipValidation) {
+ var annotatedStructuredQuery = structuredQueryValidation.annotateStructuredQuery(createStructuredQueryWithInvalidTimeRestriction(), skipValidation);
+
+ if (skipValidation) {
+ assertTrue(annotatedStructuredQuery.inclusionCriteria().get(0).get(0).validationIssues().isEmpty());
+ } else {
+ assertFalse(annotatedStructuredQuery.inclusionCriteria().get(0).get(0).validationIssues().isEmpty());
+ }
+ }
+
@NotNull
private static StructuredQuery createValidStructuredQuery(boolean withExclusionCriteria) {
var context = TermCode.builder()
@@ -150,4 +170,35 @@ private static StructuredQuery createStructuredQueryWithoutContext() {
.display("foo")
.build();
}
+
+ @NotNull
+ private static StructuredQuery createStructuredQueryWithInvalidTimeRestriction() {
+ var context = TermCode.builder()
+ .code("Laboruntersuchung")
+ .system("fdpg.mii.cds")
+ .display("Laboruntersuchung")
+ .version("1.0.0")
+ .build();
+ var termCode = TermCode.builder()
+ .code("19113-0")
+ .system("http://loinc.org")
+ .display("IgE")
+ .build();
+ var timeRestriction = TimeRestriction.builder()
+ .afterDate("1998-05-09")
+ .beforeDate("1991-06-15")
+ .build();
+ var criterion = Criterion.builder()
+ .termCodes(List.of(termCode))
+ .context(context)
+ .attributeFilters(List.of())
+ .timeRestriction(timeRestriction)
+ .build();
+ return StructuredQuery.builder()
+ .version(URI.create("http://to_be_decided.com/draft-2/schema#"))
+ .inclusionCriteria(List.of(List.of(criterion)))
+ .exclusionCriteria(List.of())
+ .display("foo")
+ .build();
+ }
}