Skip to content

Commit

Permalink
#401 - Update sq2cql and validate time restrictions (#402)
Browse files Browse the repository at this point in the history
* #401 - Update sq2cql and validate time restrictions

- update sq2cql to 0.6.0
- check if timerestrictions are correct (before date is after afterdate)
- add new error code for invalid timerestrictin
  • Loading branch information
michael-82 authored Nov 28, 2024
1 parent 5e401f3 commit 6233eb8
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 2 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@
<dependency>
<groupId>de.medizininformatik-initiative</groupId>
<artifactId>sq2cql</artifactId>
<version>0.5.0</version>
<version>0.6.0</version>
</dependency>

<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -86,6 +89,10 @@ private void annotateCriteria(List<MutableCriterion> 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());
Expand All @@ -103,6 +110,11 @@ private boolean containsInvalidCriteria(List<Criterion> 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());
Expand All @@ -115,4 +127,12 @@ private boolean containsInvalidCriteria(List<Criterion> 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()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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();
}
}

0 comments on commit 6233eb8

Please sign in to comment.