From b9b58204e925a845f79efe458e15df896b35cd01 Mon Sep 17 00:00:00 2001 From: Juan Carlos Namendi Pineda Date: Fri, 29 Dec 2023 20:18:16 +0700 Subject: [PATCH] update --- .../circe/check/checkers/Comparisons.java | 45 +++++++------- .../checkers/ExitCriteriaDaysOffsetCheck.java | 25 +++++--- .../checkers/FirstTimeInHistoryCheck.java | 7 ++- .../circe/check/checkers/RangeCheck.java | 26 ++++++--- .../check/checkers/TimePatternCheck.java | 25 +++++--- .../CohortExpressionQueryBuilder.java | 58 ++++++++++++------- .../cohortdefinition/CollapseSettings.java | 4 +- .../cohortdefinition/DateOffsetStrategy.java | 5 +- .../circe/cohortdefinition/IntervalUnit.java | 1 - .../ConditionOccurrenceSqlBuilder.java | 10 +--- .../printfriendly/cohortExpression.ftl | 4 ++ .../printfriendly/criteriaTypes.ftl | 49 +++++++++++++--- .../printfriendly/endStrategyTypes.ftl | 7 ++- .../printfriendly/inputTypes.ftl | 36 +++++++++--- .../sql/dateOffsetStrategy.sql | 23 ++++++-- .../cohortdefinition/sql/generateCohort.sql | 10 +++- 16 files changed, 228 insertions(+), 107 deletions(-) diff --git a/src/main/java/org/ohdsi/circe/check/checkers/Comparisons.java b/src/main/java/org/ohdsi/circe/check/checkers/Comparisons.java index 82ffe189..697b746e 100644 --- a/src/main/java/org/ohdsi/circe/check/checkers/Comparisons.java +++ b/src/main/java/org/ohdsi/circe/check/checkers/Comparisons.java @@ -20,9 +20,7 @@ import java.time.LocalDate; import java.time.format.DateTimeParseException; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; +import java.util.*; import java.util.function.Predicate; import java.util.stream.Collectors; import org.apache.commons.lang3.builder.EqualsBuilder; @@ -31,6 +29,12 @@ public class Comparisons { + private static final Map TIME_UNIT_CONVERSION = new HashMap<>(); + static { + TIME_UNIT_CONVERSION.put(IntervalUnit.HOUR.getName(), 60 * 60); + TIME_UNIT_CONVERSION.put(IntervalUnit.MINUTE.getName(), 60); + } + public static Boolean startIsGreaterThanEnd(NumericRange r) { return Objects.nonNull(r.value) && Objects.nonNull(r.extent) && r.value.intValue() > r.extent.intValue(); @@ -101,31 +105,26 @@ public static Predicate compare(Concept source) { .append(concept.vocabularyId, source.vocabularyId) .build(); } - public static int compareTo(ObservationFilter filter, Window window) { - int range1 = (filter.postDays + filter.priorDays) * 24 * 60 * 60; - int range2Start = getTimeInSeconds(window.start); - int range2End = getTimeInSeconds(window.end); + int range1 , range2Start , range2End; + if(Objects.nonNull(window.start) && Objects.nonNull(window.start.days)){ + range1 = filter.postDays + filter.priorDays; + range2Start = window.start.coeff * window.start.days; + range2End = Objects.nonNull(window.end) && Objects.nonNull(window.end.days) ? window.end.coeff * window.end.days : 0; + }else { + range1 = (filter.postDays + filter.priorDays) * 24 * 60 * 60; + range2Start = getTimeInSeconds(window.start); + range2End = getTimeInSeconds(window.end); + } return range1 - (range2End - range2Start); } - private static int getTimeInSeconds(Window.Endpoint endpoint) { - if (Objects.isNull(endpoint)) { - return 0; + return Optional.ofNullable(endpoint) + .map(ep -> { + int convertRate = TIME_UNIT_CONVERSION.getOrDefault(ep.timeUnit, 1); + return Objects.nonNull(ep.timeUnitValue) ? ep.coeff * ep.timeUnitValue * convertRate : 0; + }).orElse(0); } - int convertRate; - if (endpoint.timeUnit.equals(IntervalUnit.DAY.getName())) { - convertRate = 24 * 60 * 60; - } else if (endpoint.timeUnit.equals(IntervalUnit.HOUR.getName())) { - convertRate = 60 * 60; - } else if (endpoint.timeUnit.equals(IntervalUnit.MINUTE.getName())) { - convertRate = 60; - } else convertRate = 1; - return Objects.nonNull(endpoint.timeUnitValue) - ? endpoint.coeff * endpoint.timeUnitValue * convertRate - : 0; - } - public static boolean compare(Criteria c1, Criteria c2) { diff --git a/src/main/java/org/ohdsi/circe/check/checkers/ExitCriteriaDaysOffsetCheck.java b/src/main/java/org/ohdsi/circe/check/checkers/ExitCriteriaDaysOffsetCheck.java index 1264be55..3d814885 100644 --- a/src/main/java/org/ohdsi/circe/check/checkers/ExitCriteriaDaysOffsetCheck.java +++ b/src/main/java/org/ohdsi/circe/check/checkers/ExitCriteriaDaysOffsetCheck.java @@ -18,14 +18,17 @@ package org.ohdsi.circe.check.checkers; -import static org.ohdsi.circe.check.operations.Operations.match; -import static org.ohdsi.circe.cohortdefinition.DateOffsetStrategy.DateField.StartDate; - -import java.util.Objects; import org.ohdsi.circe.check.WarningSeverity; import org.ohdsi.circe.cohortdefinition.CohortExpression; import org.ohdsi.circe.cohortdefinition.DateOffsetStrategy; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +import static org.ohdsi.circe.check.operations.Operations.match; +import static org.ohdsi.circe.cohortdefinition.DateOffsetStrategy.DateField.StartDate; + public class ExitCriteriaDaysOffsetCheck extends BaseCheck { private static final String DAYS_OFFSET_WARNING = "Cohort Exit criteria: %ss offset from start date should be greater than 0"; @@ -40,9 +43,15 @@ protected WarningSeverity defineSeverity() { protected void check(CohortExpression expression, WarningReporter reporter) { match(expression.endStrategy) - .isA(DateOffsetStrategy.class) - .then(s -> match((DateOffsetStrategy)s) - .when(dateOffsetStrategy -> Objects.equals(StartDate, dateOffsetStrategy.dateField) && 0 == dateOffsetStrategy.offsetUnitValue) - .then(dateOffsetStrategy -> reporter.add(String.format(DAYS_OFFSET_WARNING, dateOffsetStrategy.offsetUnit)))); + .isA(DateOffsetStrategy.class) + .then(s -> match((DateOffsetStrategy)s) + .when(dateOffsetStrategy -> Objects.equals(StartDate, dateOffsetStrategy.dateField) && (0 == dateOffsetStrategy.offset || 0 == dateOffsetStrategy.offsetUnitValue)) + .then(dateOffsetStrategy -> { + if(0 == dateOffsetStrategy.offset){ + reporter.add(String.format(DAYS_OFFSET_WARNING), "Days"); + }else { + reporter.add(String.format(DAYS_OFFSET_WARNING, dateOffsetStrategy.offsetUnit)); + } + })); } } diff --git a/src/main/java/org/ohdsi/circe/check/checkers/FirstTimeInHistoryCheck.java b/src/main/java/org/ohdsi/circe/check/checkers/FirstTimeInHistoryCheck.java index 95f2109f..3c6db7c9 100644 --- a/src/main/java/org/ohdsi/circe/check/checkers/FirstTimeInHistoryCheck.java +++ b/src/main/java/org/ohdsi/circe/check/checkers/FirstTimeInHistoryCheck.java @@ -43,8 +43,11 @@ protected void checkCriteria(CorelatedCriteria criteria, String groupName, Warni Execution addWarning = () -> reporter.add(WARNING, name); match(criteria) .when(c -> c.startWindow != null && ((c.startWindow.start != null - && c.startWindow.start.timeUnitValue != null) || (c.startWindow.end != null - && c.startWindow.end.timeUnitValue != null))) + && c.startWindow.start.days != null) || (c.startWindow.end != null + && c.startWindow.end.days != null)) + || ((c.startWindow.start.timeUnitValue != null) + && (c.startWindow.end != null) + && c.startWindow.end.timeUnitValue != null)) .then(cc -> match(cc.criteria) .isA(ConditionEra.class) .then(c -> match((ConditionEra)c) diff --git a/src/main/java/org/ohdsi/circe/check/checkers/RangeCheck.java b/src/main/java/org/ohdsi/circe/check/checkers/RangeCheck.java index 6b927308..01d4cdbd 100644 --- a/src/main/java/org/ohdsi/circe/check/checkers/RangeCheck.java +++ b/src/main/java/org/ohdsi/circe/check/checkers/RangeCheck.java @@ -25,6 +25,7 @@ import org.ohdsi.circe.cohortdefinition.Window; import java.util.Objects; +import java.util.Optional; public class RangeCheck extends BaseValueCheck { private static final String NEGATIVE_VALUE_ERROR = "Time window in criteria \"%s\" has negative value %d at %s"; @@ -52,15 +53,24 @@ protected void checkInclusionRules(final CohortExpression expression, WarningRep } private void checkWindow(Window window, WarningReporter reporter, String name) { + Optional.ofNullable(window) + .map(w -> checkEndpoint(w, name, "start")) + .ifPresent(reporter::add); - if (Objects.nonNull(window)) { - if (Objects.nonNull(window.start) && Objects.nonNull(window.start.timeUnitValue) && window.start.timeUnitValue < 0) { - reporter.add(NEGATIVE_VALUE_ERROR, name, window.start.timeUnitValue, "start"); - } - if (Objects.nonNull(window.end) && Objects.nonNull(window.end.timeUnitValue) && window.end.timeUnitValue < 0) { - reporter.add(NEGATIVE_VALUE_ERROR, name, window.end.timeUnitValue, "end"); - } - } + Optional.ofNullable(window) + .map(w -> checkEndpoint(w, name, "end")) + .ifPresent(reporter::add); + } + + private String checkEndpoint(Window window, String name, String endpointType) { + boolean hasValid = Objects.nonNull(window.start) && Objects.nonNull(window.start.days < 0 ? window.start.days : window.start.timeUnitValue) && window.start.days < 0 ? window.start.days < 0 : window.start.timeUnitValue < 0; + return Optional.of(window) + .filter(w -> hasValid) + .map(w -> String.format(NEGATIVE_VALUE_ERROR, name, getEndpointValue(w.start), endpointType)) + .orElse(null); + } + private Object getEndpointValue(Window.Endpoint endpoint) { + return Objects.nonNull(endpoint.days) ? endpoint.days : endpoint.timeUnitValue; } private void checkObservationFilter(ObservationFilter filter, WarningReporter reporter, String name) { diff --git a/src/main/java/org/ohdsi/circe/check/checkers/TimePatternCheck.java b/src/main/java/org/ohdsi/circe/check/checkers/TimePatternCheck.java index 2a3c5407..561bf9b7 100644 --- a/src/main/java/org/ohdsi/circe/check/checkers/TimePatternCheck.java +++ b/src/main/java/org/ohdsi/circe/check/checkers/TimePatternCheck.java @@ -18,10 +18,7 @@ package org.ohdsi.circe.check.checkers; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Objects; +import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; import org.ohdsi.circe.check.WarningSeverity; @@ -83,17 +80,29 @@ private String formatTimeWindow(TimeWindowInfo ti) { } private String formatDays(Window.Endpoint endpoint) { - return Objects.nonNull(endpoint.timeUnitValue) ? String.valueOf(endpoint.timeUnitValue) : "all"; + return Objects.nonNull(endpoint.days) ? String.valueOf(endpoint.days) : + Objects.nonNull(endpoint.timeUnitValue) ? String.valueOf(endpoint.timeUnitValue) : "all"; + } private String formatCoeff(Window.Endpoint endpoint) { return endpoint.coeff < 0 ? "before " : "after "; } + private Integer startDays(Window window) { - return Objects.nonNull(window) && Objects.nonNull(window.start) ? - (Objects.nonNull(window.start.timeUnitValue) ? window.start.timeUnitValue : 0) * window.start.coeff : 0; - } + return Optional.ofNullable(window) + .map(w -> w.start) + .map(start -> { + int coefficient = Optional.ofNullable(start.coeff).orElse(0); + return Optional.ofNullable(start.days) + .map(days -> days * coefficient) + .orElseGet(() -> Optional.ofNullable(start.timeUnitValue) + .map(timeUnitValue -> timeUnitValue * coefficient) + .orElse(0)); + }) + .orElse(0); +} class TimeWindowInfo { private String name; diff --git a/src/main/java/org/ohdsi/circe/cohortdefinition/CohortExpressionQueryBuilder.java b/src/main/java/org/ohdsi/circe/cohortdefinition/CohortExpressionQueryBuilder.java index 50071030..690d3675 100644 --- a/src/main/java/org/ohdsi/circe/cohortdefinition/CohortExpressionQueryBuilder.java +++ b/src/main/java/org/ohdsi/circe/cohortdefinition/CohortExpressionQueryBuilder.java @@ -20,9 +20,8 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.ObjectMapper; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; + +import java.util.*; import java.util.stream.Collectors; import java.util.stream.IntStream; import org.apache.commons.lang3.StringUtils; @@ -351,10 +350,12 @@ public String buildExpressionQuery(CohortExpression expression, BuildExpressionQ resultSql = StringUtils.replace(resultSql, "@cohort_end_unions", StringUtils.join(endDateSelects, "\nUNION ALL\n")); -// resultSql = StringUtils.replace(resultSql, "@eraconstructorpad", Integer.toString(expression.collapseSettings.eraPad)); - resultSql = StringUtils.replace(resultSql, "@era_pad_unit", expression.collapseSettings.eraPadUnit); - resultSql = StringUtils.replace(resultSql, "@era_pad", Integer.toString(expression.collapseSettings.eraPadUnitValue)); - + if(!StringUtils.isEmpty(Integer.toString(expression.collapseSettings.eraPad)) && expression.collapseSettings.eraPad != 0){ + resultSql = StringUtils.replace(resultSql, "@eraconstructorpad", Integer.toString(expression.collapseSettings.eraPad)); + }else{ + resultSql = StringUtils.replace(resultSql, "@era_pad_unit", expression.collapseSettings.eraPadUnit); + resultSql = StringUtils.replace(resultSql, "@era_pad", Integer.toString(expression.collapseSettings.eraPadUnitValue)); + } resultSql = StringUtils.replace(resultSql, "@inclusionRuleTable", getInclusionRuleTableSql(expression)); resultSql = StringUtils.replace(resultSql, "@inclusionImpactAnalysisByEventQuery", getInclusionAnalysisQuery("#qualified_events", 0)); resultSql = StringUtils.replace(resultSql, "@inclusionImpactAnalysisByPersonQuery", getInclusionAnalysisQuery("#best_events", 1)); @@ -548,20 +549,25 @@ public String getWindowedCriteriaQuery(String sqlTemplate, WindowedCriteria crit Window startWindow = criteria.startWindow; String startIndexDateExpression = (startWindow.useIndexEnd != null && startWindow.useIndexEnd) ? "P.END_DATE" : "P.START_DATE"; String startEventDateExpression = (startWindow.useEventEnd != null && startWindow.useEventEnd) ? "A.END_DATE" : "A.START_DATE"; - if (startWindow.start.timeUnitValue != null) { - startExpression = String.format("DATEADD(%s,%d,%s)", startWindow.start.timeUnit, startWindow.start.coeff * startWindow.start.timeUnitValue, startIndexDateExpression); + if (startWindow.start.days != null) { + startExpression = String.format("DATEADD(day,%d,%s)", startWindow.start.coeff * startWindow.start.days, startIndexDateExpression); + } else if (startWindow.start.timeUnitValue != null) { + startExpression = String.format("DATEADD(%s,%d,%s)", startWindow.start.timeUnit, startWindow.start.coeff * startWindow.start.timeUnitValue, startIndexDateExpression); } else { - startExpression = checkObservationPeriod ? (startWindow.start.coeff == -1 ? "P.OP_START_DATE" : "P.OP_END_DATE") : null; + startExpression = checkObservationPeriod ? (startWindow.start.coeff == -1 ? "P.OP_START_DATE" : "P.OP_END_DATE") : null; } if (startExpression != null) { clauses.add(String.format("%s >= %s", startEventDateExpression, startExpression)); } - if (startWindow.end.timeUnitValue != null) { - endExpression = String.format("DATEADD(%s,%d,%s)", startWindow.start.timeUnit, startWindow.end.coeff * startWindow.end.timeUnitValue, startIndexDateExpression); + if (startWindow.end.days != null) { + endExpression = String.format("DATEADD(day,%d,%s)", startWindow.end.coeff * startWindow.end.days, startIndexDateExpression); + } + else if (startWindow.end.timeUnitValue != null) { + endExpression = String.format("DATEADD(%s,%d,%s)", startWindow.start.timeUnit, startWindow.end.coeff * startWindow.end.timeUnitValue, startIndexDateExpression); } else { - endExpression = checkObservationPeriod ? (startWindow.end.coeff == -1 ? "P.OP_START_DATE" : "P.OP_END_DATE") : null; + endExpression = checkObservationPeriod ? (startWindow.end.coeff == -1 ? "P.OP_START_DATE" : "P.OP_END_DATE") : null; } if (endExpression != null) { @@ -575,20 +581,24 @@ public String getWindowedCriteriaQuery(String sqlTemplate, WindowedCriteria crit String endIndexDateExpression = (endWindow.useIndexEnd != null && endWindow.useIndexEnd) ? "P.END_DATE" : "P.START_DATE"; // for backwards compatability, having a null endWindow.useIndexEnd means they SHOULD use the index end date. String endEventDateExpression = (endWindow.useEventEnd == null || endWindow.useEventEnd) ? "A.END_DATE" : "A.START_DATE"; - if (endWindow.start.timeUnitValue != null) { - startExpression = String.format("DATEADD(%s,%d,%s)", endWindow.start.timeUnit, endWindow.start.coeff * endWindow.start.timeUnitValue, endIndexDateExpression); + if (endWindow.start.days != null) { + startExpression = String.format("DATEADD(day,%d,%s)", endWindow.start.coeff * endWindow.start.days, endIndexDateExpression); + } else if (endWindow.start.timeUnitValue != null) { + startExpression = String.format("DATEADD(%s,%d,%s)", endWindow.start.timeUnit, endWindow.start.coeff * endWindow.start.timeUnitValue, endIndexDateExpression); } else { - startExpression = checkObservationPeriod ? (endWindow.start.coeff == -1 ? "P.OP_START_DATE" : "P.OP_END_DATE") : null; + startExpression = checkObservationPeriod ? (endWindow.start.coeff == -1 ? "P.OP_START_DATE" : "P.OP_END_DATE") : null; } if (startExpression != null) { clauses.add(String.format("%s >= %s", endEventDateExpression, startExpression)); } - if (endWindow.end.timeUnitValue != null) { - endExpression = String.format("DATEADD(%s,%d,%s)", endWindow.start.timeUnit, endWindow.end.coeff * endWindow.end.timeUnitValue, endIndexDateExpression); + if (endWindow.end.days != null) { + endExpression = String.format("DATEADD(day,%d,%s)", endWindow.end.coeff * endWindow.end.days, endIndexDateExpression); + } else if (endWindow.end.timeUnitValue != null) { + endExpression = String.format("DATEADD(%s,%d,%s)", endWindow.start.timeUnit, endWindow.end.coeff * endWindow.end.timeUnitValue, endIndexDateExpression); } else { - endExpression = checkObservationPeriod ? (endWindow.end.coeff == -1 ? "P.OP_START_DATE" : "P.OP_END_DATE") : null; + endExpression = checkObservationPeriod ? (endWindow.end.coeff == -1 ? "P.OP_START_DATE" : "P.OP_END_DATE") : null; } if (endExpression != null) { @@ -765,10 +775,13 @@ private String getDateFieldForOffsetStrategy(DateOffsetStrategy.DateField dateFi @Override public String getStrategySql(DateOffsetStrategy strat, String eventTable) { String strategySql = StringUtils.replace(DATE_OFFSET_STRATEGY_TEMPLATE, "@eventTable", eventTable); - strategySql = StringUtils.replace(strategySql, "@offsetUnitValue", Integer.toString(strat.offsetUnitValue)); - strategySql = StringUtils.replace(strategySql, "@offsetUnit", strat.offsetUnit); + if(strat.offset != 0){ + strategySql = StringUtils.replace(strategySql, "@offset", Integer.toString(strat.offset)); + }else { + strategySql = StringUtils.replace(strategySql, "@offsetUnitValue", Integer.toString(strat.offsetUnitValue)); + strategySql = StringUtils.replace(strategySql, "@offsetUnit", strat.offsetUnit); + } strategySql = StringUtils.replace(strategySql, "@dateField", getDateFieldForOffsetStrategy(strat.dateField)); - return strategySql; } @@ -793,4 +806,5 @@ public String getStrategySql(CustomEraStrategy strat, String eventTable) { } // + } diff --git a/src/main/java/org/ohdsi/circe/cohortdefinition/CollapseSettings.java b/src/main/java/org/ohdsi/circe/cohortdefinition/CollapseSettings.java index 0850fb71..26170865 100644 --- a/src/main/java/org/ohdsi/circe/cohortdefinition/CollapseSettings.java +++ b/src/main/java/org/ohdsi/circe/cohortdefinition/CollapseSettings.java @@ -24,8 +24,10 @@ public class CollapseSettings { @JsonProperty("CollapseType") public CollapseType collapseType = CollapseType.ERA; + @JsonProperty("EraPad") + public int eraPad = 0; @JsonProperty("EraPadUnit") - public String eraPadUnit = IntervalUnit.DAY.getName(); + public String eraPadUnit; @JsonProperty("EraPadUnitValue") public int eraPadUnitValue = 0; diff --git a/src/main/java/org/ohdsi/circe/cohortdefinition/DateOffsetStrategy.java b/src/main/java/org/ohdsi/circe/cohortdefinition/DateOffsetStrategy.java index 8f610267..f2d0ed6b 100644 --- a/src/main/java/org/ohdsi/circe/cohortdefinition/DateOffsetStrategy.java +++ b/src/main/java/org/ohdsi/circe/cohortdefinition/DateOffsetStrategy.java @@ -30,14 +30,15 @@ public enum DateField { StartDate, EndDate } - + @JsonProperty("Offset") + public int offset = 0; @JsonProperty("DateField") public DateField dateField = DateField.StartDate; @JsonProperty("OffsetUnitValue") public int offsetUnitValue = 0; @JsonProperty("OffsetUnit") - public String offsetUnit = IntervalUnit.DAY.getName(); + public String offsetUnit; @Override public String accept(IGetEndStrategySqlDispatcher dispatcher, String eventTable) { diff --git a/src/main/java/org/ohdsi/circe/cohortdefinition/IntervalUnit.java b/src/main/java/org/ohdsi/circe/cohortdefinition/IntervalUnit.java index e55b58e7..2a40b9cb 100644 --- a/src/main/java/org/ohdsi/circe/cohortdefinition/IntervalUnit.java +++ b/src/main/java/org/ohdsi/circe/cohortdefinition/IntervalUnit.java @@ -3,7 +3,6 @@ import jdk.nashorn.internal.objects.annotations.Getter; public enum IntervalUnit { - DAY("day"), HOUR("hour"), MINUTE("minute"), SECOND("second"); diff --git a/src/main/java/org/ohdsi/circe/cohortdefinition/builders/ConditionOccurrenceSqlBuilder.java b/src/main/java/org/ohdsi/circe/cohortdefinition/builders/ConditionOccurrenceSqlBuilder.java index 35defc8a..8ca6c2a5 100644 --- a/src/main/java/org/ohdsi/circe/cohortdefinition/builders/ConditionOccurrenceSqlBuilder.java +++ b/src/main/java/org/ohdsi/circe/cohortdefinition/builders/ConditionOccurrenceSqlBuilder.java @@ -4,12 +4,9 @@ import org.ohdsi.circe.cohortdefinition.ConditionOccurrence; import org.ohdsi.circe.helper.ResourceHelper; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Optional; -import java.util.Set; +import java.text.SimpleDateFormat; +import java.util.*; + import org.ohdsi.circe.cohortdefinition.DateAdjustment; import static org.ohdsi.circe.cohortdefinition.builders.BuilderUtils.buildDateRangeClause; @@ -65,7 +62,6 @@ protected String embedCodesetClause(String query, T criteria) { @Override protected String embedOrdinalExpression(String query, T criteria, List whereClauses) { - // first if (criteria.first != null && criteria.first == true) { whereClauses.add("C.ordinal = 1"); diff --git a/src/main/resources/resources/cohortdefinition/printfriendly/cohortExpression.ftl b/src/main/resources/resources/cohortdefinition/printfriendly/cohortExpression.ftl index c79088f3..5a2f6e12 100644 --- a/src/main/resources/resources/cohortdefinition/printfriendly/cohortExpression.ftl +++ b/src/main/resources/resources/cohortdefinition/printfriendly/cohortExpression.ftl @@ -64,7 +64,11 @@ ${cc?counter}. <@ct.Criteria c=cc/> ### Cohort Eras +<#if collapseSettings.eraPad?has_content && collapseSettings.eraPad != 0> +Entry events will be combined into cohort eras if they are within ${collapseSettings.eraPad} days of each other. +<#else > Entry events will be combined into cohort eras if they are within ${collapseSettings.eraPadUnitValue} ${collapseSettings.eraPadUnit}s of each other. + <#-- main template: end --> diff --git a/src/main/resources/resources/cohortdefinition/printfriendly/criteriaTypes.ftl b/src/main/resources/resources/cohortdefinition/printfriendly/criteriaTypes.ftl index 3dc2ee4a..29d2bf84 100644 --- a/src/main/resources/resources/cohortdefinition/printfriendly/criteriaTypes.ftl +++ b/src/main/resources/resources/cohortdefinition/printfriendly/criteriaTypes.ftl @@ -279,15 +279,46 @@ group.demographicCriteriaList?size == 1><@DemographicCriteria c=group.demographi <#macro CountCriteria countCriteria level=0 indexLabel="cohort entry">having <#if countCriteria.occurrence.type == 0 && countCriteria.occurrence.count == 0>no<#else>${inputTypes.getCountType(countCriteria)} ${countCriteria.occurrence.count}<#-- --><#if countCriteria.occurrence.isDistinct> distinct <@utils.countColumn countCriteria.occurrence.countColumn!"DOMAIN_CONCEPT" /> from <@Criteria c=countCriteria.criteria level=level isPlural=(countCriteria.occurrence.count != 1) countCriteria=countCriteria indexLabel=indexLabel /> -<#macro WindowCriteria countCriteria indexLabel="cohort entry" level=0><#local windowParts=[] restrictParts=[]><#if -countCriteria.startWindow.start.timeUnitValue?? || countCriteria.startWindow.end.timeUnitValue??><#local temp><@inputTypes.Window countCriteria.startWindow indexLabel /><#local windowParts+=[temp]><#if -countCriteria.endWindow?? && (countCriteria.endWindow.start.timeUnitValue?? || countCriteria.endWindow.end.timeUnitValue??)> -<#local temp><@inputTypes.Window countCriteria.endWindow indexLabel /><#local windowParts+=[temp]> -<#if countCriteria.restrictVisit!false><#local temp>at same visit as ${indexLabel}<#local restrictParts+=[temp]> -<#if countCriteria.ignoreObservationPeriod!false><#local temp>allow events outside observation period<#local restrictParts+=[temp]><#if -windowParts?size gt 0>${windowParts?join(" and ")}<#if restrictParts?size gt 0>; <#if -restrictParts?size gt 0>${restrictParts?join(" and ")} - +<#macro WindowCriteria countCriteria indexLabel="cohort entry" level=0><#local windowParts=[] restrictParts=[]> + <#if countCriteria.startWindow.start.days?? || countCriteria.startWindow.end.days??> + <#local temp> + <@inputTypes.Window countCriteria.startWindow indexLabel /> + + <#local windowParts+=[temp]> + + <#if countCriteria.endWindow?? && (countCriteria.endWindow.start.days?? || countCriteria.endWindow.end.days??)> +<#local temp> + <@inputTypes.Window countCriteria.endWindow indexLabel /> + + <#local windowParts+=[temp]> + + <#if countCriteria.startWindow.start.timeUnitValue?? || countCriteria.startWindow.end.timeUnitValue??> + <#local temp> + <@inputTypes.Window countCriteria.startWindow indexLabel /> + + <#local windowParts+=[temp]> + + <#if countCriteria.endWindow?? && (countCriteria.endWindow.start.timeUnitValue?? || countCriteria.endWindow.end.timeUnitValue??)> + <#local temp> + <@inputTypes.Window countCriteria.endWindow indexLabel /> + + <#local windowParts+=[temp]> + +<#if countCriteria.restrictVisit!false> + <#local temp>at same visit as ${indexLabel} + + <#local restrictParts+=[temp]> + +<#if countCriteria.ignoreObservationPeriod!false> + <#local temp>allow events outside observation period + <#local restrictParts+=[temp]> + + <#if windowParts?size gt 0>${windowParts?join(" and ")} + <#if restrictParts?size gt 0>; + + <#if restrictParts?size gt 0>${restrictParts?join(" and ")} + + <#-- Demographic Criteria --> <#macro DemographicCriteria c level=0 indexLabel = "cohort entry"><#local attrs = []><#local temp><@AgeGenderCriteria ageAtStart=c.age!{} gender=c.gender!{} /><#if temp?has_content><#local attrs+=[temp]><#local diff --git a/src/main/resources/resources/cohortdefinition/printfriendly/endStrategyTypes.ftl b/src/main/resources/resources/cohortdefinition/printfriendly/endStrategyTypes.ftl index a71c1bf4..15e16a34 100644 --- a/src/main/resources/resources/cohortdefinition/printfriendly/endStrategyTypes.ftl +++ b/src/main/resources/resources/cohortdefinition/printfriendly/endStrategyTypes.ftl @@ -24,7 +24,12 @@ The person exits the cohort at the end of continuous observation. <#assign dateOffsetFieldOptions = [{"id": "StartDate", "name": "start date"}, {"id": "EndDate", "name": "end date"}]> <#macro DateOffsetStrategy s> -The cohort end date will be offset from index event's ${utils.optionName(dateOffsetFieldOptions, s.dateField)} plus <@utils.formatValue s.offsetUnitValue s.offsetUnit/>. + <#if s.offset?? && s.offset == "day" > + The cohort end date will be offset from index event's ${utils.optionName(dateOffsetFieldOptions, s.dateField)} plus <@utils.formatValue s.offset "day"/>. + <#else > + The cohort end date will be offset from index event's ${utils.optionName(dateOffsetFieldOptions, s.dateField)} plus <@utils.formatValue s.offsetUnitValue s.offsetUnit/>. + + <#macro CustomEraStrategy s> diff --git a/src/main/resources/resources/cohortdefinition/printfriendly/inputTypes.ftl b/src/main/resources/resources/cohortdefinition/printfriendly/inputTypes.ftl index d26b4cf1..3ab55c30 100644 --- a/src/main/resources/resources/cohortdefinition/printfriendly/inputTypes.ftl +++ b/src/main/resources/resources/cohortdefinition/printfriendly/inputTypes.ftl @@ -89,15 +89,37 @@ END Note!!!! <#function whichIndexPart useEnd><#if useEnd><#return "end date"><#else><#return "start date"> <#function temporalDirection coeff><#if coeff lt 0><#return "before"><#else><#return "after"> -<#macro Window w indexLabel="cohort entry">${whichEventPart(w.useEventEnd!false)} <#-- ---><#if !w.start.timeUnitValue?? && w.end.timeUnitValue == 0 && w.start.coeff == -1 >anytime on or before ${indexLabel} ${whichIndexPart(w.useIndexEnd!false)}<#-- ---><#elseif (w.end.timeUnitValue!0) == 1 && w.start.coeff == -1 && w.end.coeff == -1><#if w.start.timeUnitValue??>in the ${w.start.timeUnitValue} ${w.start.timeUnit}s<#else>anytime prior to ${indexLabel} ${whichIndexPart(w.useIndexEnd!false)}<#-- ---><#elseif !w.start.timeUnitValue?? && (w.end.timeUnitValue!0) gt 1 && w.start.coeff == -1>anytime up to ${w.end.timeUnitValue} ${w.end.timeUnit}s ${temporalDirection(w.end.coeff)} ${indexLabel} ${whichIndexPart(w.useIndexEnd!false)}<#-- ---><#elseif !w.end.timeUnitValue?? && (w.start.timeUnitValue!0) gt 0 && w.end.coeff ==1> ${w.start.timeUnitValue} ${w.start.timeUnit}s ${temporalDirection(w.start.coeff)} ${indexLabel} ${whichIndexPart(w.useIndexEnd!false)}<#-- ---><#else>between ${w.start.timeUnitValue!"all"} ${w.start.timeUnit}s ${temporalDirection(w.start.coeff)} and ${w.end.timeUnitValue!"all"} ${w.end.timeUnit}s ${temporalDirection(w.end.coeff)} ${indexLabel} ${whichIndexPart(w.useIndexEnd!false)} +<#macro Window w indexLabel="cohort entry">${whichEventPart(w.useEventEnd!false)} +<#----> +<#if !w.start.days?? && w.end.days == 0 && w.start.coeff == -1 > + anytime on or before ${indexLabel} + ${whichIndexPart(w.useIndexEnd!false)} +<#----> +<#elseif (w.end.days!0) == 1 && w.start.coeff == -1 && w.end.coeff == -1> +<#if w.start.days??>in the ${w.start.days} days +<#else>anytime prior to ${indexLabel} ${whichIndexPart(w.useIndexEnd!false)} +<#----> +<#elseif !w.start.days?? && (w.end.days!0) gt 1 && w.start.coeff == -1>anytime up to ${w.end.days} days ${temporalDirection(w.end.coeff)} ${indexLabel} ${whichIndexPart(w.useIndexEnd!false)} +<#----> +<#elseif !w.end.days?? && (w.start.days!0) gt 0 && w.end.coeff ==1> ${w.start.days} days ${temporalDirection(w.start.coeff)} ${indexLabel} ${whichIndexPart(w.useIndexEnd!false)} +<#----> +<#else>between ${w.start.days!"all"} days ${temporalDirection(w.start.coeff)} and ${w.end.days!"all"} days ${temporalDirection(w.end.coeff)} ${indexLabel} ${whichIndexPart(w.useIndexEnd!false)} +<#----> +<#if !w.start.timeUnitValue?? && w.end.timeUnitValue == 0 && w.start.coeff == -1 >anytime on or before ${indexLabel} ${whichIndexPart(w.useIndexEnd!false)} +<#----> +<#elseif (w.end.timeUnitValue!0) == 1 && w.start.coeff == -1 && w.end.coeff == -1><#if w.start.timeUnitValue??>in the ${w.start.timeUnitValue} ${w.start.timeUnit}s<#else>anytime prior to ${indexLabel} ${whichIndexPart(w.useIndexEnd!false)} +<#----> +<#elseif !w.start.timeUnitValue?? && (w.end.timeUnitValue!0) gt 1 && w.start.coeff == -1>anytime up to ${w.end.timeUnitValue} ${w.end.timeUnit}s ${temporalDirection(w.end.coeff)} ${indexLabel} ${whichIndexPart(w.useIndexEnd!false)} +<#----> +<#elseif !w.end.timeUnitValue?? && (w.start.timeUnitValue!0) gt 0 && w.end.coeff ==1> ${w.start.timeUnitValue} ${w.start.timeUnit}s ${temporalDirection(w.start.coeff)} ${indexLabel} ${whichIndexPart(w.useIndexEnd!false)} +<#----> +<#else>between ${w.start.timeUnitValue!"all"} ${w.start.timeUnit}s ${temporalDirection(w.start.coeff)} and ${w.end.timeUnitValue!"all"} ${w.end.timeUnit}s ${temporalDirection(w.end.coeff)} ${indexLabel} ${whichIndexPart(w.useIndexEnd!false)} + <#-- User Defined Period --> <#macro UserDefinedPeriod p><#if p.startDate?has_content>a user defiend start date of ${utils.formatDate(p.startDate)}<#if p.endDate?has_content> and<#if -p.endDate?has_content><#if !p.startDate?has_content>a user defined end date of ${utils.formatDate(p.endDate)} +p.endDate?has_content><#if !p.startDate?has_content>a user defined end date of ${utils.formatDate(p.endDate)} + + diff --git a/src/main/resources/resources/cohortdefinition/sql/dateOffsetStrategy.sql b/src/main/resources/resources/cohortdefinition/sql/dateOffsetStrategy.sql index b79289dd..cac7c35f 100644 --- a/src/main/resources/resources/cohortdefinition/sql/dateOffsetStrategy.sql +++ b/src/main/resources/resources/cohortdefinition/sql/dateOffsetStrategy.sql @@ -1,9 +1,20 @@ -- date offset strategy -select event_id, - person_id, - case - when DATEADD(@offsetUnit, @offsetUnitValue, @dateField) > op_end_date then op_end_date - else DATEADD(@offsetUnit, @offsetUnitValue, @dateField) end as end_date +SELECT + event_id, + person_id, + CASE + WHEN @offset IS NOT NULL + THEN + CASE + WHEN DATEADD(@offsetUnit, @offsetUnitValue, @dateField) > op_end_date THEN op_end_date + ELSE DATEADD(@offsetUnit, @offsetUnitValue, @dateField) + END + ELSE + CASE + WHEN DATEADD(day, @offset, @dateField) > op_end_date THEN op_end_date + ELSE DATEADD(day, @offset, @dateField) + END + END AS end_date INTO #strategy_ends -from @eventTable; +FROM @eventTable; diff --git a/src/main/resources/resources/cohortdefinition/sql/generateCohort.sql b/src/main/resources/resources/cohortdefinition/sql/generateCohort.sql index 0a873c67..8fad4578 100644 --- a/src/main/resources/resources/cohortdefinition/sql/generateCohort.sql +++ b/src/main/resources/resources/cohortdefinition/sql/generateCohort.sql @@ -62,8 +62,14 @@ from ( --cteEnds FROM #cohort_rows c JOIN ( -- cteEndDates SELECT - person_id - , DATEADD(@era_pad_unit, -1 * @era_pad, event_date) as end_date + person_id, + CASE + WHEN + @eraconstructorpad IS NOT NULL + THEN + DATEADD(day, -1 * @eraconstructorpad, event_date) + ELSE + DATEADD(@era_pad_unit, -1 * @era_pad, event_date) END AS end_date FROM ( SELECT