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 bf2404e3..0af3a208 100644 --- a/src/main/java/org/ohdsi/circe/check/checkers/Comparisons.java +++ b/src/main/java/org/ohdsi/circe/check/checkers/Comparisons.java @@ -102,18 +102,48 @@ public static Predicate compare(Concept source) { .build(); } - public static int compareTo(ObservationFilter filter, Window window) { - - int range1 = filter.postDays + filter.priorDays; - int range2Start = 0, range2End = 0; - if (Objects.nonNull(window.start) && Objects.nonNull(window.start.days)) { - range2Start = window.start.coeff * window.start.days; - } - if (Objects.nonNull(window.end) && Objects.nonNull(window.end.days)) { - range2End = window.end.coeff * window.end.days; - } - return range1 - (range2End - range2Start); + /** + * If timeUnit is equal to values such as Hours, Minutes, Seconds, the values will be converted to seconds + * Otherwise it will return to the previous logic. + * @param filter + * @param window + * @return + */ + public static int compareTo(ObservationFilter filter, Window window) { + int range1, range2Start = 0, range2End = 0; + if (Objects.nonNull(window.start) && Objects.nonNull(window.start.timeUnit) && !IntervalUnit.DAY.getName().equals(window.start.timeUnit)) { + range1 = (filter.postDays + filter.priorDays) * 24 * 60 * 60; + range2Start = getTimeInSeconds(window.start); + range2End = getTimeInSeconds(window.end); + } else { + range1 = filter.postDays + filter.priorDays; + if (Objects.nonNull(window.start) && Objects.nonNull(window.start.days)) { + range2Start = window.start.coeff * window.start.days; + } + if (Objects.nonNull(window.end) && Objects.nonNull(window.end.days)) { + range2End = window.end.coeff * window.end.days; + } + } + return range1 - (range2End - range2Start); + } + + /** + * @return Convert values to seconds. + */ + private static int getTimeInSeconds(Window.Endpoint endpoint) { + if (Objects.isNull(endpoint)) { + return 0; } + int convertRate; + if (IntervalUnit.HOUR.getName().equals(endpoint.timeUnit)) { + convertRate = 60 * 60; + } else if (IntervalUnit.MINUTE.getName().equals(endpoint.timeUnit)) { + 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 b8e83804..2b009ddb 100644 --- a/src/main/java/org/ohdsi/circe/check/checkers/ExitCriteriaDaysOffsetCheck.java +++ b/src/main/java/org/ohdsi/circe/check/checkers/ExitCriteriaDaysOffsetCheck.java @@ -28,7 +28,7 @@ public class ExitCriteriaDaysOffsetCheck extends BaseCheck { - private static final String DAYS_OFFSET_WARNING = "Cohort Exit criteria: Days offset from start date should be greater than 0"; + private static final String DAYS_OFFSET_WARNING = "Cohort Exit criteria: %ss offset from start date should be greater than 0"; @Override protected WarningSeverity defineSeverity() { @@ -40,9 +40,9 @@ 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.offset) - .then(() -> reporter.add(DAYS_OFFSET_WARNING))); + .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)))); } } 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 8280bcd7..89e437eb 100644 --- a/src/main/java/org/ohdsi/circe/check/checkers/FirstTimeInHistoryCheck.java +++ b/src/main/java/org/ohdsi/circe/check/checkers/FirstTimeInHistoryCheck.java @@ -42,9 +42,10 @@ protected void checkCriteria(CorelatedCriteria criteria, String groupName, Warni String name = CriteriaNameHelper.getCriteriaName(criteria.criteria) + " at " + groupName; Execution addWarning = () -> reporter.add(WARNING, name); match(criteria) - .when(c -> c.startWindow != null && ((c.startWindow.start != null - && c.startWindow.start.days != null) || (c.startWindow.end != null - && c.startWindow.end.days != null))) + .when(c -> c.startWindow != null && ((c.startWindow.start != null && c.startWindow.start.days != null) + || (c.startWindow.end != null && c.startWindow.end.days != null)) + || c.startWindow != null && (( c.startWindow.start != 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 e3b2a8eb..8a117e5b 100644 --- a/src/main/java/org/ohdsi/circe/check/checkers/RangeCheck.java +++ b/src/main/java/org/ohdsi/circe/check/checkers/RangeCheck.java @@ -52,17 +52,23 @@ protected void checkInclusionRules(final CohortExpression expression, WarningRep } private void checkWindow(Window window, WarningReporter reporter, String name) { + if (Objects.isNull(window)) { + return; + } + checkAndReportIfNegative(window.start, reporter, name, "start"); + checkAndReportIfNegative(window.end, reporter, name, "end"); - if (Objects.nonNull(window)) { - if (Objects.nonNull(window.start) && Objects.nonNull(window.start.days) && window.start.days < 0) { - reporter.add(NEGATIVE_VALUE_ERROR, name, window.start.days, "start"); - } - if (Objects.nonNull(window.end) && Objects.nonNull(window.end.days) && window.end.days < 0) { - reporter.add(NEGATIVE_VALUE_ERROR, name, window.end.days, "end"); - } + } + + private void checkAndReportIfNegative(Window.Endpoint windowDetails, WarningReporter reporter, String name, String type) { + if (Objects.nonNull(windowDetails) && Objects.nonNull(windowDetails.days) && windowDetails.days < 0) { + reporter.add(NEGATIVE_VALUE_ERROR, name, windowDetails.days, type); + } else if (Objects.nonNull(windowDetails) && Objects.nonNull(windowDetails.timeUnitValue) && windowDetails.timeUnitValue < 0) { + reporter.add(NEGATIVE_VALUE_ERROR, name, windowDetails.timeUnitValue, type); } } + private void checkObservationFilter(ObservationFilter filter, WarningReporter reporter, String name) { if (Objects.nonNull(filter)) { 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 a01c75f6..ffe2caba 100644 --- a/src/main/java/org/ohdsi/circe/check/checkers/TimePatternCheck.java +++ b/src/main/java/org/ohdsi/circe/check/checkers/TimePatternCheck.java @@ -22,6 +22,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.function.Function; import java.util.stream.Collectors; import org.ohdsi.circe.check.WarningSeverity; @@ -83,16 +84,28 @@ private String formatTimeWindow(TimeWindowInfo ti) { } private String formatDays(Window.Endpoint endpoint) { - return Objects.nonNull(endpoint.days) ? String.valueOf(endpoint.days) : "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.days) ? window.start.days : 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 { diff --git a/src/main/java/org/ohdsi/circe/cohortdefinition/CohortExpressionQueryBuilder.java b/src/main/java/org/ohdsi/circe/cohortdefinition/CohortExpressionQueryBuilder.java index bf65f8ab..948536a0 100644 --- a/src/main/java/org/ohdsi/circe/cohortdefinition/CohortExpressionQueryBuilder.java +++ b/src/main/java/org/ohdsi/circe/cohortdefinition/CohortExpressionQueryBuilder.java @@ -351,8 +351,13 @@ 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)); - + if (!StringUtils.isEmpty(Integer.toString(expression.collapseSettings.eraPad)) && (expression.collapseSettings.eraPadUnit == null || IntervalUnit.DAY.getName().equals(expression.collapseSettings.eraPadUnit))) { + resultSql = StringUtils.replace(resultSql, "@eraconstructorpad", Integer.toString(expression.collapseSettings.eraPad)); + resultSql = StringUtils.replace(resultSql, "@era_pad_unit", expression.collapseSettings.eraPadUnit); + } else { + resultSql = StringUtils.replace(resultSql, "@eraconstructorpad", Integer.toString(expression.collapseSettings.eraPadUnitValue)); + resultSql = StringUtils.replace(resultSql, "@era_pad_unit", expression.collapseSettings.eraPadUnit); + } 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)); @@ -546,8 +551,10 @@ 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.days != null) { + if (startWindow.start.days != null && (startWindow.start.timeUnit == null || startWindow.start.timeUnit.equals(IntervalUnit.DAY.getName()))) { 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; } @@ -556,9 +563,12 @@ public String getWindowedCriteriaQuery(String sqlTemplate, WindowedCriteria crit clauses.add(String.format("%s >= %s", startEventDateExpression, startExpression)); } - if (startWindow.end.days != null) { + if (startWindow.end.days != null && (startWindow.end.timeUnit == null || IntervalUnit.DAY.getName().equals(startWindow.end.timeUnit))) { endExpression = String.format("DATEADD(day,%d,%s)", startWindow.end.coeff * startWindow.end.days, startIndexDateExpression); - } else { + }else if(startWindow.end.timeUnitValue != null){ + endExpression = String.format("DATEADD(%s,%d,%s)", startWindow.end.timeUnit, startWindow.end.coeff * startWindow.end.timeUnitValue, startIndexDateExpression); + } + else { endExpression = checkObservationPeriod ? (startWindow.end.coeff == -1 ? "P.OP_START_DATE" : "P.OP_END_DATE") : null; } @@ -573,9 +583,12 @@ 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.days != null) { + if (endWindow.start.days != null && (endWindow.start.timeUnit == null || IntervalUnit.DAY.getName().equals(endWindow.start.timeUnit))) { startExpression = String.format("DATEADD(day,%d,%s)", endWindow.start.coeff * endWindow.start.days, endIndexDateExpression); - } else { + }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; } @@ -583,9 +596,13 @@ public String getWindowedCriteriaQuery(String sqlTemplate, WindowedCriteria crit clauses.add(String.format("%s >= %s", endEventDateExpression, startExpression)); } - if (endWindow.end.days != null) { + if (endWindow.end.days != null && (endWindow.end.timeUnit == null || IntervalUnit.DAY.getName().equals(endWindow.end.timeUnit))) { endExpression = String.format("DATEADD(day,%d,%s)", endWindow.end.coeff * endWindow.end.days, endIndexDateExpression); - } else { + } + else if(endWindow.end.timeUnitValue != null){ + endExpression = String.format("DATEADD(%s,%d,%s)", endWindow.end.timeUnit, endWindow.end.coeff * endWindow.end.timeUnitValue, endIndexDateExpression); + } + else { endExpression = checkObservationPeriod ? (endWindow.end.coeff == -1 ? "P.OP_START_DATE" : "P.OP_END_DATE") : null; } @@ -763,7 +780,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, "@offset", Integer.toString(strat.offset)); + if (strat.offsetUnit == null || IntervalUnit.DAY.getName().equals(strat.offsetUnit)) { + strategySql = StringUtils.replace(strategySql, "@offsetUnitValue", Integer.toString(strat.offset)); + strategySql = StringUtils.replace(strategySql, "@offsetUnit", "day"); + } 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; @@ -777,13 +800,33 @@ public String getStrategySql(CustomEraStrategy strat, String eventTable) { } String drugExposureEndDateExpression = DEFAULT_DRUG_EXPOSURE_END_DATE_EXPRESSION; - if (strat.daysSupplyOverride != null) { + if (strat.daysSupplyOverride != null && IntervalUnit.DAY.getName().equals(strat.gapUnit)) { drugExposureEndDateExpression = String.format("DATEADD(day,%d,DRUG_EXPOSURE_START_DATE)", strat.daysSupplyOverride); + }else if(strat.daysSupplyOverride != null && IntervalUnit.HOUR.getName().equals(strat.gapUnit)){ + drugExposureEndDateExpression = String.format("DATEADD(hour,%d,DRUG_EXPOSURE_START_DATE)", strat.daysSupplyOverride); + }else if(strat.daysSupplyOverride != null && IntervalUnit.MINUTE.getName().equals(strat.gapUnit)){ + drugExposureEndDateExpression = String.format("DATEADD(minute,%d,DRUG_EXPOSURE_START_DATE)", strat.daysSupplyOverride); + }else if(strat.daysSupplyOverride != null && IntervalUnit.SECOND.getName().equals(strat.gapUnit)){ + drugExposureEndDateExpression = String.format("DATEADD(second,%d,DRUG_EXPOSURE_START_DATE)", strat.daysSupplyOverride); } String strategySql = StringUtils.replace(CUSTOM_ERA_STRATEGY_TEMPLATE, "@eventTable", eventTable); strategySql = StringUtils.replace(strategySql, "@drugCodesetId", strat.drugCodesetId.toString()); - strategySql = StringUtils.replace(strategySql, "@gapDays", Integer.toString(strat.gapDays)); - strategySql = StringUtils.replace(strategySql, "@offset", Integer.toString(strat.offset)); + if (IntervalUnit.DAY.getName().equals(strat.gapUnit)) { + strategySql = StringUtils.replace(strategySql, "@gapUnitValue", Integer.toString(strat.gapDays)); + strategySql = StringUtils.replace(strategySql, "@gapUnit", "day"); + }else { + strategySql = StringUtils.replace(strategySql, "@gapUnitValue", Integer.toString(strat.gapUnitValue)); + strategySql = StringUtils.replace(strategySql, "@gapUnit", strat.gapUnit); + + } + if(IntervalUnit.DAY.getName().equals(strat.offsetUnit) || strat.offsetUnit == null){ + strategySql = StringUtils.replace(strategySql, "@offsetUnitValue", Integer.toString(strat.offset)); + strategySql = StringUtils.replace(strategySql, "@offsetUnit", "day"); + }else { + strategySql = StringUtils.replace(strategySql, "@offsetUnitValue", Integer.toString(strat.offsetUnitValue)); + strategySql = StringUtils.replace(strategySql, "@offsetUnit", strat.offsetUnit); + } + strategySql = StringUtils.replace(strategySql, "@drugExposureEndDateExpression", drugExposureEndDateExpression); return strategySql; diff --git a/src/main/java/org/ohdsi/circe/cohortdefinition/CollapseSettings.java b/src/main/java/org/ohdsi/circe/cohortdefinition/CollapseSettings.java index 39db0e79..8456b30a 100644 --- a/src/main/java/org/ohdsi/circe/cohortdefinition/CollapseSettings.java +++ b/src/main/java/org/ohdsi/circe/cohortdefinition/CollapseSettings.java @@ -18,8 +18,10 @@ */ package org.ohdsi.circe.cohortdefinition; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; +@JsonIgnoreProperties(ignoreUnknown = true) public class CollapseSettings { @JsonProperty("CollapseType") @@ -27,5 +29,11 @@ public class CollapseSettings { @JsonProperty("EraPad") public int eraPad = 0; - + + @JsonProperty("EraPadUnit") + public String eraPadUnit = IntervalUnit.DAY.getName(); + + @JsonProperty("EraPadUnitValue") + public int eraPadUnitValue = 0; + } diff --git a/src/main/java/org/ohdsi/circe/cohortdefinition/Criteria.java b/src/main/java/org/ohdsi/circe/cohortdefinition/Criteria.java index dc64997b..9179bd83 100644 --- a/src/main/java/org/ohdsi/circe/cohortdefinition/Criteria.java +++ b/src/main/java/org/ohdsi/circe/cohortdefinition/Criteria.java @@ -22,6 +22,7 @@ import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeInfo.As; + import org.ohdsi.circe.cohortdefinition.builders.BuilderOptions; /** @@ -60,5 +61,14 @@ public String accept(IGetCriteriaSqlDispatcher dispatcher) { @JsonProperty("DateAdjustment") public DateAdjustment dateAdjustment; + + /** + * This is a marker for the proper table columns definition while constructing the SELECT part of the result SQL + * in the corresponding SQL builder to reflect the values being defined in the associated WindowCriteria + * + * The DAY value should be set only if all the WindowCriteria's Window.Endpoint values are DAY + */ + @JsonProperty("IntervalUnit") + public String intervalUnit; } diff --git a/src/main/java/org/ohdsi/circe/cohortdefinition/CustomEraStrategy.java b/src/main/java/org/ohdsi/circe/cohortdefinition/CustomEraStrategy.java index f585e73d..aa307330 100644 --- a/src/main/java/org/ohdsi/circe/cohortdefinition/CustomEraStrategy.java +++ b/src/main/java/org/ohdsi/circe/cohortdefinition/CustomEraStrategy.java @@ -31,9 +31,17 @@ public class CustomEraStrategy extends EndStrategy { @JsonProperty("GapDays") public int gapDays = 0; + @JsonProperty("GapUnit") + public String gapUnit = "day"; + @JsonProperty("GapUnitValue") + public int gapUnitValue = 0; @JsonProperty("Offset") public int offset = 0; + @JsonProperty("OffsetUnit") + public String offsetUnit = "day"; + @JsonProperty("OffsetUnitValue") + public int offsetUnitValue = 0; @JsonProperty("DaysSupplyOverride") public Integer daysSupplyOverride = null; diff --git a/src/main/java/org/ohdsi/circe/cohortdefinition/DateOffsetStrategy.java b/src/main/java/org/ohdsi/circe/cohortdefinition/DateOffsetStrategy.java index 0ed6b8a8..6917a596 100644 --- a/src/main/java/org/ohdsi/circe/cohortdefinition/DateOffsetStrategy.java +++ b/src/main/java/org/ohdsi/circe/cohortdefinition/DateOffsetStrategy.java @@ -18,25 +18,32 @@ */ package org.ohdsi.circe.cohortdefinition; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; /** * * @author Chris Knoll */ +@JsonIgnoreProperties(ignoreUnknown = true) public class DateOffsetStrategy extends EndStrategy { public enum DateField { StartDate, EndDate } - + @JsonProperty("DateField") public DateField dateField = DateField.StartDate; @JsonProperty("Offset") public int offset = 0; - + + @JsonProperty("OffsetUnitValue") + public int offsetUnitValue = 0; + @JsonProperty("OffsetUnit") + public String offsetUnit = "day"; + @Override public String accept(IGetEndStrategySqlDispatcher dispatcher, String eventTable) { return dispatcher.getStrategySql(this, eventTable); diff --git a/src/main/java/org/ohdsi/circe/cohortdefinition/IntervalUnit.java b/src/main/java/org/ohdsi/circe/cohortdefinition/IntervalUnit.java new file mode 100644 index 00000000..18da37c2 --- /dev/null +++ b/src/main/java/org/ohdsi/circe/cohortdefinition/IntervalUnit.java @@ -0,0 +1,18 @@ +package org.ohdsi.circe.cohortdefinition; + +public enum IntervalUnit { + DAY("day"), + HOUR("hour"), + MINUTE("minute"), + SECOND("second"); + + private final String name; + + IntervalUnit(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/src/main/java/org/ohdsi/circe/cohortdefinition/ObservationFilter.java b/src/main/java/org/ohdsi/circe/cohortdefinition/ObservationFilter.java index 8be10161..53e514bb 100644 --- a/src/main/java/org/ohdsi/circe/cohortdefinition/ObservationFilter.java +++ b/src/main/java/org/ohdsi/circe/cohortdefinition/ObservationFilter.java @@ -27,6 +27,6 @@ public class ObservationFilter { @JsonProperty("PriorDays") public int priorDays; - @JsonProperty("PostDays") + @JsonProperty("PostDays") public int postDays; } diff --git a/src/main/java/org/ohdsi/circe/cohortdefinition/Window.java b/src/main/java/org/ohdsi/circe/cohortdefinition/Window.java index c1a2563b..928b160f 100644 --- a/src/main/java/org/ohdsi/circe/cohortdefinition/Window.java +++ b/src/main/java/org/ohdsi/circe/cohortdefinition/Window.java @@ -18,32 +18,39 @@ */ package org.ohdsi.circe.cohortdefinition; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; /** - * * @author cknoll1 */ +@JsonIgnoreProperties(ignoreUnknown = true) public class Window { + @JsonIgnoreProperties(ignoreUnknown = true) public static class Endpoint { @JsonProperty("Days") public Integer days; + @JsonProperty("TimeUnitValue") + public Integer timeUnitValue; + @JsonProperty("TimeUnit") + public String timeUnit = "day"; + @JsonProperty("Coeff") public int coeff; } - + @JsonProperty("Start") - public Endpoint start; + public Endpoint start; @JsonProperty("End") - public Endpoint end; + public Endpoint end; @JsonProperty("UseIndexEnd") public Boolean useIndexEnd; @JsonProperty("UseEventEnd") public Boolean useEventEnd; - + } diff --git a/src/main/java/org/ohdsi/circe/cohortdefinition/builders/ConditionEraSqlBuilder.java b/src/main/java/org/ohdsi/circe/cohortdefinition/builders/ConditionEraSqlBuilder.java index 4f6183f3..3fa1d738 100644 --- a/src/main/java/org/ohdsi/circe/cohortdefinition/builders/ConditionEraSqlBuilder.java +++ b/src/main/java/org/ohdsi/circe/cohortdefinition/builders/ConditionEraSqlBuilder.java @@ -35,15 +35,21 @@ protected String getQueryTemplate() { return CONDITION_ERA_TEMPLATE; } + /** + * Add params timeIntervalUnit to check for hours/minutes/seconds situation + * @param column + * @param timeIntervalUnit + * @return + */ @Override - protected String getTableColumnForCriteriaColumn(CriteriaColumn column) { + protected String getTableColumnForCriteriaColumn(CriteriaColumn column, String timeIntervalUnit) { switch (column) { case DOMAIN_CONCEPT: return "C.condition_concept_id"; case ERA_OCCURRENCES: return "C.condition_occurrence_count"; case DURATION: - return "(DATEDIFF(d,C.start_date, C.end_date))"; + return String.format("DATEDIFF(%s,c.start_date, c.end_date)", StringUtils.isEmpty(timeIntervalUnit) ? "d" : timeIntervalUnit); default: throw new IllegalArgumentException("Invalid CriteriaColumn for Condition Era:" + column.toString()); } 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 92880b74..1d3c98c6 100644 --- a/src/main/java/org/ohdsi/circe/cohortdefinition/builders/ConditionOccurrenceSqlBuilder.java +++ b/src/main/java/org/ohdsi/circe/cohortdefinition/builders/ConditionOccurrenceSqlBuilder.java @@ -2,6 +2,7 @@ import org.apache.commons.lang3.StringUtils; import org.ohdsi.circe.cohortdefinition.ConditionOccurrence; +import org.ohdsi.circe.cohortdefinition.IntervalUnit; import org.ohdsi.circe.helper.ResourceHelper; import java.util.ArrayList; @@ -39,13 +40,19 @@ protected String getQueryTemplate() { return CONDITION_OCCURRENCE_TEMPLATE; } + /** + * Add params timeIntervalUnit to check for hours/minutes/seconds situation + * @param column + * @param timeIntervalUnit + * @return + */ @Override - protected String getTableColumnForCriteriaColumn(CriteriaColumn column) { + protected String getTableColumnForCriteriaColumn(CriteriaColumn column, String timeIntervalUnit) { switch (column) { case DOMAIN_CONCEPT: return "C.condition_concept_id"; case DURATION: - return "(DATEDIFF(d,C.start_date, C.end_date))"; + return String.format("DATEDIFF(%s,c.start_date, c.end_date)", StringUtils.isEmpty(timeIntervalUnit) ? "d" : timeIntervalUnit); default: throw new IllegalArgumentException("Invalid CriteriaColumn for Condition Occurrence:" + column.toString()); } @@ -98,11 +105,17 @@ protected List resolveSelectClauses(T criteria) { } // dateAdjustment or default start/end dates if (criteria.dateAdjustment != null) { - selectCols.add(BuilderUtils.getDateAdjustmentExpression(criteria.dateAdjustment, - criteria.dateAdjustment.startWith == DateAdjustment.DateType.START_DATE ? "co.condition_start_date" : "COALESCE(co.condition_end_date, DATEADD(day,1,co.condition_start_date))", - criteria.dateAdjustment.endWith == DateAdjustment.DateType.START_DATE ? "co.condition_start_date" : "COALESCE(co.condition_end_date, DATEADD(day,1,co.condition_start_date))")); + selectCols.add(BuilderUtils.getDateAdjustmentExpression(criteria.dateAdjustment, + criteria.dateAdjustment.startWith == DateAdjustment.DateType.START_DATE ? "co.condition_start_date" : "COALESCE(co.condition_end_date, DATEADD(day,1,co.condition_start_date))", + criteria.dateAdjustment.endWith == DateAdjustment.DateType.START_DATE ? "co.condition_start_date" : "COALESCE(co.condition_end_date, DATEADD(day,1,co.condition_start_date))")); } else { - selectCols.add("co.condition_start_date as start_date, COALESCE(co.condition_end_date, DATEADD(day,1,co.condition_start_date)) as end_date"); + if (criteria.intervalUnit == null || IntervalUnit.DAY.getName().equals(criteria.intervalUnit)) { + selectCols.add("co.condition_start_date as start_date, COALESCE(co.condition_end_date, DATEADD(day,1,co.condition_start_date)) as end_date"); + } + else { + // if any specific business logic is necessary if condition_end_datetime is empty it should be added accordingly as for the 'day' case + selectCols.add("co.condition_start_datetime as start_date, co.condition_end_datetime as end_date"); + } } return selectCols; } diff --git a/src/main/java/org/ohdsi/circe/cohortdefinition/builders/CriteriaSqlBuilder.java b/src/main/java/org/ohdsi/circe/cohortdefinition/builders/CriteriaSqlBuilder.java index 32dd1051..9a3b6620 100644 --- a/src/main/java/org/ohdsi/circe/cohortdefinition/builders/CriteriaSqlBuilder.java +++ b/src/main/java/org/ohdsi/circe/cohortdefinition/builders/CriteriaSqlBuilder.java @@ -35,7 +35,7 @@ public String getCriteriaSql(T criteria, BuilderOptions options) { .filter((column) -> !this.getDefaultColumns().contains(column)) .collect(Collectors.toList()); if (filteredColumns.size() > 0) { - query = StringUtils.replace(query, "@additionalColumns", ", " + this.getAdditionalColumns(filteredColumns)); + query = StringUtils.replace(query, "@additionalColumns", ", " + this.getAdditionalColumnsWithTimeInterval(filteredColumns, criteria.intervalUnit)); } else { query = StringUtils.replace(query, "@additionalColumns", ""); } @@ -46,16 +46,24 @@ public String getCriteriaSql(T criteria, BuilderOptions options) { return query; } - protected abstract String getTableColumnForCriteriaColumn(CriteriaColumn column); + protected abstract String getTableColumnForCriteriaColumn(CriteriaColumn column, String timeIntervalUnit); protected String getAdditionalColumns(List columns) { String cols = String.join(", ", columns.stream() .map((column) -> { - return String.format("%s as %s", getTableColumnForCriteriaColumn(column), column.columnName()); + return String.format("%s as %s", getTableColumnForCriteriaColumn(column, null), column.columnName()); }).collect(Collectors.toList())); return cols; } + protected String getAdditionalColumnsWithTimeInterval(List columns, String timeIntervalUnit) { + String cols = String.join(", ", columns.stream() + .map((column) -> { + return String.format("%s as %s", getTableColumnForCriteriaColumn(column, timeIntervalUnit), column.columnName()); + }).collect(Collectors.toList())); + return cols; + } + protected abstract Set getDefaultColumns(); protected String embedSelectClauses(String query, List selectClauses) { diff --git a/src/main/java/org/ohdsi/circe/cohortdefinition/builders/DeathSqlBuilder.java b/src/main/java/org/ohdsi/circe/cohortdefinition/builders/DeathSqlBuilder.java index c79fc122..23852594 100644 --- a/src/main/java/org/ohdsi/circe/cohortdefinition/builders/DeathSqlBuilder.java +++ b/src/main/java/org/ohdsi/circe/cohortdefinition/builders/DeathSqlBuilder.java @@ -2,6 +2,7 @@ import org.apache.commons.lang3.StringUtils; import org.ohdsi.circe.cohortdefinition.Death; +import org.ohdsi.circe.cohortdefinition.IntervalUnit; import org.ohdsi.circe.helper.ResourceHelper; import java.util.ArrayList; @@ -9,6 +10,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; + import org.ohdsi.circe.cohortdefinition.DateAdjustment; import static org.ohdsi.circe.cohortdefinition.builders.BuilderUtils.buildDateRangeClause; @@ -38,7 +40,7 @@ protected String getQueryTemplate() { } @Override - protected String getTableColumnForCriteriaColumn(CriteriaColumn column) { + protected String getTableColumnForCriteriaColumn(CriteriaColumn column, String timeIntervalUnit) { switch (column) { case DOMAIN_CONCEPT: return "coalesce(C.cause_concept_id,0)"; @@ -78,7 +80,13 @@ protected List resolveSelectClauses(T criteria) { if (criteria.dateAdjustment != null) { selectCols.add(BuilderUtils.getDateAdjustmentExpression(criteria.dateAdjustment, "d.death_date", "DATEADD(day,1,d.death_date)")); } else { - selectCols.add("d.death_date as start_date, DATEADD(day,1,d.death_date) as end_date"); + if (criteria.intervalUnit == null || IntervalUnit.DAY.getName().equals(criteria.intervalUnit)) { + selectCols.add("d.death_date as start_date, DATEADD(day,1,d.death_date) as end_date"); + } + else { + // if any specific business logic is necessary if death_datetime is empty it should be added accordingly + selectCols.add("d.death_datetime as start_date, d.death_datetime as end_date"); + } } return selectCols; } diff --git a/src/main/java/org/ohdsi/circe/cohortdefinition/builders/DeviceExposureSqlBuilder.java b/src/main/java/org/ohdsi/circe/cohortdefinition/builders/DeviceExposureSqlBuilder.java index 586c15e7..f5eb74f8 100644 --- a/src/main/java/org/ohdsi/circe/cohortdefinition/builders/DeviceExposureSqlBuilder.java +++ b/src/main/java/org/ohdsi/circe/cohortdefinition/builders/DeviceExposureSqlBuilder.java @@ -2,6 +2,7 @@ import org.apache.commons.lang3.StringUtils; import org.ohdsi.circe.cohortdefinition.DeviceExposure; +import org.ohdsi.circe.cohortdefinition.IntervalUnit; import org.ohdsi.circe.helper.ResourceHelper; import java.util.ArrayList; @@ -10,6 +11,7 @@ import java.util.List; import java.util.Map; import java.util.Set; + import org.ohdsi.circe.cohortdefinition.DateAdjustment; import static org.ohdsi.circe.cohortdefinition.builders.BuilderUtils.buildDateRangeClause; @@ -41,14 +43,15 @@ protected String getQueryTemplate() { } @Override - protected String getTableColumnForCriteriaColumn(CriteriaColumn column) { + protected String getTableColumnForCriteriaColumn(CriteriaColumn column, String timeIntervalUnit) { switch (column) { case DOMAIN_CONCEPT: return "C.device_concept_id"; case QUANTITY: return "C.quantity"; case DURATION: - return "DATEDIFF(d,c.start_date, c.end_date)"; + return String.format("DATEDIFF(%s,c.start_date, c.end_date)", StringUtils.isEmpty(timeIntervalUnit) ? "d" : timeIntervalUnit); +// return "DATEDIFF(d,c.start_date, c.end_date)"; default: throw new IllegalArgumentException("Invalid CriteriaColumn for Device Exposure:" + column.toString()); } @@ -102,7 +105,13 @@ protected List resolveSelectClauses(T criteria) { criteria.dateAdjustment.startWith == DateAdjustment.DateType.START_DATE ? "de.device_exposure_start_date" : "COALESCE(de.device_exposure_end_date, DATEADD(day,1,de.device_exposure_start_date))", criteria.dateAdjustment.endWith == DateAdjustment.DateType.START_DATE ? "de.device_exposure_start_date" : "COALESCE(de.device_exposure_end_date, DATEADD(day,1,de.device_exposure_start_date))")); } else { - selectCols.add("de.device_exposure_start_date as start_date, COALESCE(de.device_exposure_end_date, DATEADD(day,1,de.device_exposure_start_date)) as end_date"); + if (criteria.intervalUnit == null || IntervalUnit.DAY.getName().equals(criteria.intervalUnit)) { + selectCols.add("de.device_exposure_start_date as start_date, COALESCE(de.device_exposure_end_date, DATEADD(day,1,de.device_exposure_start_date)) as end_date"); + } + else { + // if any specific business logic is necessary if device_exposure_end_datetime is empty it should be added accordingly as for the 'day' case + selectCols.add("de.device_exposure_start_datetime as start_date, de.device_exposure_end_datetime as end_date"); + } } return selectCols; } diff --git a/src/main/java/org/ohdsi/circe/cohortdefinition/builders/DoseEraSqlBuilder.java b/src/main/java/org/ohdsi/circe/cohortdefinition/builders/DoseEraSqlBuilder.java index 3f473f86..01055ca8 100644 --- a/src/main/java/org/ohdsi/circe/cohortdefinition/builders/DoseEraSqlBuilder.java +++ b/src/main/java/org/ohdsi/circe/cohortdefinition/builders/DoseEraSqlBuilder.java @@ -37,12 +37,12 @@ protected String getQueryTemplate() { } @Override - protected String getTableColumnForCriteriaColumn(CriteriaColumn column) { + protected String getTableColumnForCriteriaColumn(CriteriaColumn column, String timeIntervalUnit) { switch (column) { case DOMAIN_CONCEPT: return "C.drug_concept_id"; case DURATION: - return "DATEDIFF(d, C.start_date, C.end_date)"; + return String.format("DATEDIFF(%s,c.start_date, c.end_date)", StringUtils.isEmpty(timeIntervalUnit) ? "d" : timeIntervalUnit); case UNIT: return "C.unit_concept_id"; case VALUE_AS_NUMBER: diff --git a/src/main/java/org/ohdsi/circe/cohortdefinition/builders/DrugEraSqlBuilder.java b/src/main/java/org/ohdsi/circe/cohortdefinition/builders/DrugEraSqlBuilder.java index acb00829..397938dc 100644 --- a/src/main/java/org/ohdsi/circe/cohortdefinition/builders/DrugEraSqlBuilder.java +++ b/src/main/java/org/ohdsi/circe/cohortdefinition/builders/DrugEraSqlBuilder.java @@ -38,7 +38,7 @@ protected String getQueryTemplate() { } @Override - protected String getTableColumnForCriteriaColumn(CriteriaColumn column) { + protected String getTableColumnForCriteriaColumn(CriteriaColumn column, String timeIntervalUnit) { switch (column) { case DOMAIN_CONCEPT: return "C.drug_concept_id"; diff --git a/src/main/java/org/ohdsi/circe/cohortdefinition/builders/DrugExposureSqlBuilder.java b/src/main/java/org/ohdsi/circe/cohortdefinition/builders/DrugExposureSqlBuilder.java index 7a4a8045..eb69977d 100644 --- a/src/main/java/org/ohdsi/circe/cohortdefinition/builders/DrugExposureSqlBuilder.java +++ b/src/main/java/org/ohdsi/circe/cohortdefinition/builders/DrugExposureSqlBuilder.java @@ -2,6 +2,7 @@ import org.apache.commons.lang3.StringUtils; import org.ohdsi.circe.cohortdefinition.DrugExposure; +import org.ohdsi.circe.cohortdefinition.IntervalUnit; import org.ohdsi.circe.helper.ResourceHelper; import java.util.ArrayList; @@ -9,6 +10,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; + import org.ohdsi.circe.cohortdefinition.DateAdjustment; import static org.ohdsi.circe.cohortdefinition.builders.BuilderUtils.buildDateRangeClause; @@ -40,14 +42,14 @@ protected String getQueryTemplate() { } @Override - protected String getTableColumnForCriteriaColumn(CriteriaColumn column) { + protected String getTableColumnForCriteriaColumn(CriteriaColumn column, String timeIntervalUnit) { switch (column) { case DAYS_SUPPLY: return "C.days_supply"; case DOMAIN_CONCEPT: return "C.drug_concept_id"; case DURATION: - return "DATEDIFF(d, C.start_date, C.end_date)"; + return String.format("DATEDIFF(%s,c.start_date, c.end_date)", StringUtils.isEmpty(timeIntervalUnit) ? "d" : timeIntervalUnit); case QUANTITY: return "C.quantity"; case REFILLS: @@ -130,7 +132,13 @@ protected List resolveSelectClauses(T criteria) { criteria.dateAdjustment.endWith == DateAdjustment.DateType.START_DATE ? "de.drug_exposure_start_date" : "COALESCE(de.drug_exposure_end_date, DATEADD(day,de.days_supply,de.drug_exposure_start_date), DATEADD(day,1,de.drug_exposure_start_date))")); } else { - selectCols.add("de.drug_exposure_start_date as start_date, COALESCE(de.drug_exposure_end_date, DATEADD(day,de.days_supply,de.drug_exposure_start_date), DATEADD(day,1,de.drug_exposure_start_date)) as end_date"); + if (criteria.intervalUnit == null || IntervalUnit.DAY.getName().equals(criteria.intervalUnit)) { + selectCols.add("de.drug_exposure_start_date as start_date, COALESCE(de.drug_exposure_end_date, DATEADD(day,de.days_supply,de.drug_exposure_start_date), DATEADD(day,1,de.drug_exposure_start_date)) as end_date"); + } + else { + // if any specific business logic is necessary if drug_exposure_end_datetime is empty it should be added accordingly as for the 'day' case + selectCols.add("de.drug_exposure_start_datetime as start_date, de.drug_exposure_end_datetime as end_date"); + } } return selectCols; } diff --git a/src/main/java/org/ohdsi/circe/cohortdefinition/builders/LocationRegionSqlBuilder.java b/src/main/java/org/ohdsi/circe/cohortdefinition/builders/LocationRegionSqlBuilder.java index f7dbd59c..48049837 100644 --- a/src/main/java/org/ohdsi/circe/cohortdefinition/builders/LocationRegionSqlBuilder.java +++ b/src/main/java/org/ohdsi/circe/cohortdefinition/builders/LocationRegionSqlBuilder.java @@ -30,7 +30,7 @@ protected String getQueryTemplate() { } @Override - protected String getTableColumnForCriteriaColumn(CriteriaColumn column) { + protected String getTableColumnForCriteriaColumn(CriteriaColumn column, String timeIntervalUnit) { switch (column) { case DOMAIN_CONCEPT: return "C.region_concept_id"; diff --git a/src/main/java/org/ohdsi/circe/cohortdefinition/builders/MeasurementSqlBuilder.java b/src/main/java/org/ohdsi/circe/cohortdefinition/builders/MeasurementSqlBuilder.java index fe204467..289b8196 100644 --- a/src/main/java/org/ohdsi/circe/cohortdefinition/builders/MeasurementSqlBuilder.java +++ b/src/main/java/org/ohdsi/circe/cohortdefinition/builders/MeasurementSqlBuilder.java @@ -1,6 +1,7 @@ package org.ohdsi.circe.cohortdefinition.builders; import org.apache.commons.lang3.StringUtils; +import org.ohdsi.circe.cohortdefinition.IntervalUnit; import org.ohdsi.circe.cohortdefinition.Measurement; import org.ohdsi.circe.helper.ResourceHelper; @@ -9,6 +10,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; + import org.ohdsi.circe.cohortdefinition.DateAdjustment; import static org.ohdsi.circe.cohortdefinition.builders.BuilderUtils.buildDateRangeClause; @@ -39,7 +41,7 @@ protected String getQueryTemplate() { } @Override - protected String getTableColumnForCriteriaColumn(CriteriaColumn column) { + protected String getTableColumnForCriteriaColumn(CriteriaColumn column, String timeIntervalUnit) { switch (column) { case DOMAIN_CONCEPT: return "C.measurement_concept_id"; @@ -118,7 +120,13 @@ protected List resolveSelectClauses(T criteria) { criteria.dateAdjustment.startWith == DateAdjustment.DateType.START_DATE ? "m.measurement_date" : "DATEADD(day,1,m.measurement_date)", criteria.dateAdjustment.endWith == DateAdjustment.DateType.START_DATE ? "m.measurement_date" : "DATEADD(day,1,m.measurement_date)")); } else { - selectCols.add("m.measurement_date as start_date, DATEADD(day,1,m.measurement_date) as end_date"); + if (criteria.intervalUnit == null || IntervalUnit.DAY.getName().equals(criteria.intervalUnit)) { + selectCols.add("m.measurement_date as start_date, DATEADD(day,1,m.measurement_date) as end_date"); + } + else { + // if any specific business logic is necessary if measurement_datetime is empty it should be added accordingly + selectCols.add("m.measurement_datetime as start_date, m.measurement_datetime as end_date"); + } } return selectCols; } diff --git a/src/main/java/org/ohdsi/circe/cohortdefinition/builders/ObservationPeriodSqlBuilder.java b/src/main/java/org/ohdsi/circe/cohortdefinition/builders/ObservationPeriodSqlBuilder.java index d403be12..56629c4c 100644 --- a/src/main/java/org/ohdsi/circe/cohortdefinition/builders/ObservationPeriodSqlBuilder.java +++ b/src/main/java/org/ohdsi/circe/cohortdefinition/builders/ObservationPeriodSqlBuilder.java @@ -55,7 +55,7 @@ protected String getQueryTemplate() { } @Override - protected String getTableColumnForCriteriaColumn(CriteriaColumn column) { + protected String getTableColumnForCriteriaColumn(CriteriaColumn column, String timeIntervalUnit) { switch (column) { case DOMAIN_CONCEPT: return "C.period_type_concept_id"; diff --git a/src/main/java/org/ohdsi/circe/cohortdefinition/builders/ObservationSqlBuilder.java b/src/main/java/org/ohdsi/circe/cohortdefinition/builders/ObservationSqlBuilder.java index 5206ef23..65fd0ce2 100644 --- a/src/main/java/org/ohdsi/circe/cohortdefinition/builders/ObservationSqlBuilder.java +++ b/src/main/java/org/ohdsi/circe/cohortdefinition/builders/ObservationSqlBuilder.java @@ -1,6 +1,7 @@ package org.ohdsi.circe.cohortdefinition.builders; import org.apache.commons.lang3.StringUtils; +import org.ohdsi.circe.cohortdefinition.IntervalUnit; import org.ohdsi.circe.cohortdefinition.Observation; import org.ohdsi.circe.helper.ResourceHelper; @@ -10,6 +11,7 @@ import java.util.List; import java.util.Map; import java.util.Set; + import org.ohdsi.circe.cohortdefinition.DateAdjustment; import static org.ohdsi.circe.cohortdefinition.builders.BuilderUtils.buildDateRangeClause; @@ -41,7 +43,7 @@ protected String getQueryTemplate() { } @Override - protected String getTableColumnForCriteriaColumn(CriteriaColumn column) { + protected String getTableColumnForCriteriaColumn(CriteriaColumn column, String timeIntervalUnit) { switch (column) { case DOMAIN_CONCEPT: return "C.observation_concept_id"; @@ -119,7 +121,13 @@ protected List resolveSelectClauses(T criteria) { criteria.dateAdjustment.startWith == DateAdjustment.DateType.START_DATE ? "o.observation_date" : "DATEADD(day,1,o.observation_date)", criteria.dateAdjustment.endWith == DateAdjustment.DateType.START_DATE ? "o.observation_date" : "DATEADD(day,1,o.observation_date)")); } else { - selectCols.add("o.observation_date as start_date, DATEADD(day,1,o.observation_date) as end_date"); + if (criteria.intervalUnit == null || IntervalUnit.DAY.getName().equals(criteria.intervalUnit)) { + selectCols.add("o.observation_date as start_date, DATEADD(day,1,o.observation_date) as end_date"); + } + else { + // if any specific business logic is necessary if observation_datetime is empty it should be added accordingly + selectCols.add("o.observation_datetime as start_date, o.observation_datetime as end_date"); + } } return selectCols; } diff --git a/src/main/java/org/ohdsi/circe/cohortdefinition/builders/PayerPlanPeriodSqlBuilder.java b/src/main/java/org/ohdsi/circe/cohortdefinition/builders/PayerPlanPeriodSqlBuilder.java index 6b0b5135..14a268cc 100644 --- a/src/main/java/org/ohdsi/circe/cohortdefinition/builders/PayerPlanPeriodSqlBuilder.java +++ b/src/main/java/org/ohdsi/circe/cohortdefinition/builders/PayerPlanPeriodSqlBuilder.java @@ -54,7 +54,7 @@ protected String getQueryTemplate() { } @Override - protected String getTableColumnForCriteriaColumn(CriteriaColumn column) { + protected String getTableColumnForCriteriaColumn(CriteriaColumn column, String timeIntervalUnit) { switch (column) { case DOMAIN_CONCEPT: return "C.payer_concept_id"; diff --git a/src/main/java/org/ohdsi/circe/cohortdefinition/builders/ProcedureOccurrenceSqlBuilder.java b/src/main/java/org/ohdsi/circe/cohortdefinition/builders/ProcedureOccurrenceSqlBuilder.java index b6d30336..479e13a9 100644 --- a/src/main/java/org/ohdsi/circe/cohortdefinition/builders/ProcedureOccurrenceSqlBuilder.java +++ b/src/main/java/org/ohdsi/circe/cohortdefinition/builders/ProcedureOccurrenceSqlBuilder.java @@ -1,6 +1,7 @@ package org.ohdsi.circe.cohortdefinition.builders; import org.apache.commons.lang3.StringUtils; +import org.ohdsi.circe.cohortdefinition.IntervalUnit; import org.ohdsi.circe.cohortdefinition.ProcedureOccurrence; import org.ohdsi.circe.helper.ResourceHelper; @@ -9,6 +10,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; + import org.ohdsi.circe.cohortdefinition.DateAdjustment; import static org.ohdsi.circe.cohortdefinition.builders.BuilderUtils.buildDateRangeClause; @@ -39,7 +41,7 @@ protected String getQueryTemplate() { } @Override - protected String getTableColumnForCriteriaColumn(CriteriaColumn column) { + protected String getTableColumnForCriteriaColumn(CriteriaColumn column, String timeIntervalUnit) { switch (column) { case DOMAIN_CONCEPT: return "C.procedure_concept_id"; @@ -102,7 +104,13 @@ protected List resolveSelectClauses(T criteria) { criteria.dateAdjustment.startWith == DateAdjustment.DateType.START_DATE ? "po.procedure_date" : "DATEADD(day,1,po.procedure_date)", criteria.dateAdjustment.endWith == DateAdjustment.DateType.START_DATE ? "po.procedure_date" : "DATEADD(day,1,po.procedure_date)")); } else { - selectCols.add("po.procedure_date as start_date, DATEADD(day,1,po.procedure_date) as end_date"); + if (criteria.intervalUnit == null || IntervalUnit.DAY.getName().equals(criteria.intervalUnit)) { + selectCols.add("po.procedure_date as start_date, DATEADD(day,1,po.procedure_date) as end_date"); + } + else { + // if any specific business logic is necessary if procedure_end_datetime is empty it should be added accordingly + selectCols.add("po.procedure_datetime as start_date, po.procedure_end_datetime as end_date"); + } } return selectCols; } diff --git a/src/main/java/org/ohdsi/circe/cohortdefinition/builders/SpecimenSqlBuilder.java b/src/main/java/org/ohdsi/circe/cohortdefinition/builders/SpecimenSqlBuilder.java index ee8a802f..31bfd487 100644 --- a/src/main/java/org/ohdsi/circe/cohortdefinition/builders/SpecimenSqlBuilder.java +++ b/src/main/java/org/ohdsi/circe/cohortdefinition/builders/SpecimenSqlBuilder.java @@ -1,6 +1,8 @@ package org.ohdsi.circe.cohortdefinition.builders; import org.apache.commons.lang3.StringUtils; +import org.ohdsi.circe.cohortdefinition.DateAdjustment; +import org.ohdsi.circe.cohortdefinition.IntervalUnit; import org.ohdsi.circe.cohortdefinition.Specimen; import org.ohdsi.circe.helper.ResourceHelper; @@ -34,7 +36,7 @@ protected String getQueryTemplate() { } @Override - protected String getTableColumnForCriteriaColumn(CriteriaColumn column) { + protected String getTableColumnForCriteriaColumn(CriteriaColumn column, String timeIntervalUnit) { switch (column) { case DOMAIN_CONCEPT: return "C.specimen_concept_id"; @@ -135,4 +137,27 @@ protected List resolveWhereClauses(T criteria) { return whereClauses; } + + @Override + protected List resolveSelectClauses(T criteria) { + // as this logic was fully missing comparing to the other SQL builders adding only the ones which belong to the datetime functionality + ArrayList selectCols = new ArrayList<>(); + + // dateAdjustment or default start/end dates + if (criteria.dateAdjustment != null) { + selectCols.add(BuilderUtils.getDateAdjustmentExpression(criteria.dateAdjustment, + criteria.dateAdjustment.startWith == DateAdjustment.DateType.START_DATE ? "s.specimen_date" : "DATEADD(day,1,s.specimen_date)", + criteria.dateAdjustment.endWith == DateAdjustment.DateType.START_DATE ? "s.specimen_date" : "DATEADD(day,1,s.specimen_date)")); + } else { + if (criteria.intervalUnit == null || IntervalUnit.DAY.getName().equals(criteria.intervalUnit)) { + selectCols.add("s.specimen_date as start_date, DATEADD(day,1,s.specimen_date) as end_date"); + } + else { + // if any specific business logic is necessary if specimen_datetime is empty it should be added accordingly + selectCols.add("s.specimen_datetime as start_date, s.specimen_datetime as end_date"); + } + } + return selectCols; + } + } diff --git a/src/main/java/org/ohdsi/circe/cohortdefinition/builders/VisitDetailSqlBuilder.java b/src/main/java/org/ohdsi/circe/cohortdefinition/builders/VisitDetailSqlBuilder.java index 893b7cfd..c3224a7e 100644 --- a/src/main/java/org/ohdsi/circe/cohortdefinition/builders/VisitDetailSqlBuilder.java +++ b/src/main/java/org/ohdsi/circe/cohortdefinition/builders/VisitDetailSqlBuilder.java @@ -2,10 +2,12 @@ import org.apache.commons.lang3.StringUtils; import org.ohdsi.circe.cohortdefinition.ConceptSetSelection; +import org.ohdsi.circe.cohortdefinition.IntervalUnit; import org.ohdsi.circe.cohortdefinition.VisitDetail; import org.ohdsi.circe.helper.ResourceHelper; import java.util.*; + import org.ohdsi.circe.cohortdefinition.DateAdjustment; public class VisitDetailSqlBuilder extends CriteriaSqlBuilder { @@ -30,12 +32,12 @@ protected String getQueryTemplate() { } @Override - protected String getTableColumnForCriteriaColumn(CriteriaColumn column) { + protected String getTableColumnForCriteriaColumn(CriteriaColumn column, String timeIntervalUnit) { switch (column) { case DOMAIN_CONCEPT: return "C.visit_detail_concept_id"; case DURATION: - return "DATEDIFF(d, C.start_date, C.end_date)"; + return String.format("DATEDIFF(%s,c.start_date, c.end_date)", StringUtils.isEmpty(timeIntervalUnit) ? "d" : timeIntervalUnit); default: throw new IllegalArgumentException("Invalid CriteriaColumn for Visit Detail:" + column.toString()); } @@ -90,9 +92,14 @@ protected List resolveSelectClauses(T criteria) { criteria.dateAdjustment.startWith == DateAdjustment.DateType.START_DATE ? "vd.visit_detail_start_date" : "vd.visit_detail_end_date", criteria.dateAdjustment.endWith == DateAdjustment.DateType.START_DATE ? "vd.visit_detail_start_date" : "vd.visit_detail_end_date")); } else { - selectCols.add("vd.visit_detail_start_date as start_date, vd.visit_detail_end_date as end_date"); + if (criteria.intervalUnit == null || IntervalUnit.DAY.getName().equals(criteria.intervalUnit)) { + selectCols.add("vd.visit_detail_start_date as start_date, vd.visit_detail_end_date as end_date"); + } + else { + // if any specific business logic is necessary if visit_detail_end_datetime is empty it should be added accordingly + selectCols.add("vd.visit_detail_start_datetime as start_date, vd.visit_detail_end_datetime as end_date"); + } } - return selectCols; } diff --git a/src/main/java/org/ohdsi/circe/cohortdefinition/builders/VisitOccurrenceSqlBuilder.java b/src/main/java/org/ohdsi/circe/cohortdefinition/builders/VisitOccurrenceSqlBuilder.java index 8780f808..632bb64c 100644 --- a/src/main/java/org/ohdsi/circe/cohortdefinition/builders/VisitOccurrenceSqlBuilder.java +++ b/src/main/java/org/ohdsi/circe/cohortdefinition/builders/VisitOccurrenceSqlBuilder.java @@ -1,6 +1,7 @@ package org.ohdsi.circe.cohortdefinition.builders; import org.apache.commons.lang3.StringUtils; +import org.ohdsi.circe.cohortdefinition.IntervalUnit; import org.ohdsi.circe.cohortdefinition.VisitOccurrence; import org.ohdsi.circe.helper.ResourceHelper; @@ -9,6 +10,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; + import org.ohdsi.circe.cohortdefinition.DateAdjustment; public class VisitOccurrenceSqlBuilder extends CriteriaSqlBuilder { @@ -32,12 +34,12 @@ protected String getQueryTemplate() { } @Override - protected String getTableColumnForCriteriaColumn(CriteriaColumn column) { + protected String getTableColumnForCriteriaColumn(CriteriaColumn column, String timeIntervalUnit) { switch (column) { case DOMAIN_CONCEPT: return "C.visit_concept_id"; case DURATION: - return "DATEDIFF(d, C.start_date, C.end_date)"; + return String.format("DATEDIFF(%s,c.start_date, c.end_date)", StringUtils.isEmpty(timeIntervalUnit) ? "d" : timeIntervalUnit); default: throw new IllegalArgumentException("Invalid CriteriaColumn for Visit Occurrence:" + column.toString()); } @@ -92,9 +94,14 @@ protected List resolveSelectClauses(T criteria) { criteria.dateAdjustment.startWith == DateAdjustment.DateType.START_DATE ? "vo.visit_start_date" : "vo.visit_end_date", criteria.dateAdjustment.endWith == DateAdjustment.DateType.START_DATE ? "vo.visit_start_date" : "vo.visit_end_date")); } else { - selectCols.add("vo.visit_start_date as start_date, vo.visit_end_date as end_date"); + if (criteria.intervalUnit == null || IntervalUnit.DAY.getName().equals(criteria.intervalUnit)) { + selectCols.add("vo.visit_start_date as start_date, vo.visit_end_date as end_date"); + } + else { + // if any specific business logic is necessary if visit_end_datetime is empty it should be added accordingly + selectCols.add("vo.visit_start_datetime as start_date, vo.visit_end_datetime as end_date"); + } } - return selectCols; } diff --git a/src/main/resources/resources/cohortdefinition/printfriendly/cohortExpression.ftl b/src/main/resources/resources/cohortdefinition/printfriendly/cohortExpression.ftl index 16bc52e4..49a5af25 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 || collapseSettings.eraPadUnit = "day")> Remaining events will be combined into cohort eras if they are within ${collapseSettings.eraPad} days of each other. +<#else > +Remaining 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/conceptSet.ftl b/src/main/resources/resources/cohortdefinition/printfriendly/conceptSet.ftl index 54a65012..d7dd0762 100644 --- a/src/main/resources/resources/cohortdefinition/printfriendly/conceptSet.ftl +++ b/src/main/resources/resources/cohortdefinition/printfriendly/conceptSet.ftl @@ -13,10 +13,10 @@ -->${utils.renderCheckbox(conceptSetItem.includeDescendants)}|<#-- -->${utils.renderCheckbox(conceptSetItem.includeMapped)}| - + <#else> -There are no concept set items in this concept set. +There are no concept set items in this concept set. diff --git a/src/main/resources/resources/cohortdefinition/printfriendly/criteriaTypes.ftl b/src/main/resources/resources/cohortdefinition/printfriendly/criteriaTypes.ftl index 715d86d5..2275714c 100644 --- a/src/main/resources/resources/cohortdefinition/printfriendly/criteriaTypes.ftl +++ b/src/main/resources/resources/cohortdefinition/printfriendly/criteriaTypes.ftl @@ -292,13 +292,13 @@ 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.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??)> +<#macro WindowCriteria countCriteria indexLabel="cohort entry" level=0><#local windowParts=[] restrictParts=[]><#if +countCriteria.startWindow.start.timeUnitValue?? || countCriteria.startWindow.end.timeUnitValue?? || countCriteria.startWindow.start.days?? || countCriteria.startWindow.end.days??><#local temp><@inputTypes.Window countCriteria.startWindow indexLabel /><#local windowParts+=[temp]><#if +countCriteria.endWindow?? && (countCriteria.endWindow.start.timeUnitValue?? || countCriteria.endWindow.end.timeUnitValue?? || countCriteria.startWindow.start.days?? || countCriteria.startWindow.end.days??)> <#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 +<#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 --> diff --git a/src/main/resources/resources/cohortdefinition/printfriendly/endStrategyTypes.ftl b/src/main/resources/resources/cohortdefinition/printfriendly/endStrategyTypes.ftl index 8f021d3d..71a0bc5e 100644 --- a/src/main/resources/resources/cohortdefinition/printfriendly/endStrategyTypes.ftl +++ b/src/main/resources/resources/cohortdefinition/printfriendly/endStrategyTypes.ftl @@ -24,11 +24,21 @@ 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.offset "day"/>. + <#if s.offsetUnit = "day" || !s.offsetUnit?has_content> + The cohort end date will be offset from index event's ${utils.optionName(dateOffsetFieldOptions, s.dateField)} plus ${s.offset} days. + <#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> + <#if s.offsetUnit == "day"> The cohort end date will be based on a continuous exposure to ${utils.codesetName(s.drugCodesetId!"", "_invalid drug specified_")}: allowing ${s.gapDays} days between exposures, adding <@utils.formatValue s.offset "day"/> after exposure ends, and <#if s.daysSupplyOverride??>forcing drug exposure days supply to: <@utils.formatValue s.daysSupplyOverride "day"/>.<#else>using days supply and exposure end date for exposure duration. + <#else > +The cohort end date will be based on a continuous exposure to ${utils.codesetName(s.drugCodesetId!"", "_invalid drug specified_")}: +allowing ${s.gapUnitValue} ${s.offsetUnit} between exposures, adding <@utils.formatValue s.offsetUnitValue s.offsetUnit/> after exposure ends, and <#if +s.daysSupplyOverride??>forcing drug exposure ${s.offsetUnit} supply to: <@utils.formatValue s.daysSupplyOverride s.offsetUnit/>.<#else>using ${s.offsetUnit} supply and exposure end date for exposure duration. + diff --git a/src/main/resources/resources/cohortdefinition/printfriendly/inputTypes.ftl b/src/main/resources/resources/cohortdefinition/printfriendly/inputTypes.ftl index 2860374a..e85b3ec8 100644 --- a/src/main/resources/resources/cohortdefinition/printfriendly/inputTypes.ftl +++ b/src/main/resources/resources/cohortdefinition/printfriendly/inputTypes.ftl @@ -25,7 +25,7 @@ END Note!!!! --><#if range.op?ends_with("bt")> and <#if range.extent?has_content>${utils.formatDate(range.extent)}<#else>_empty_ <#-- ConceptList --> -<#macro ConceptList list quote="\""><#if (list?size > 0)><#list list?map(item->(quote + item.conceptName?lower_case + quote)) as item><#if +<#macro ConceptList list quote="\""><#if (list?size > 0)><#list list?map(item->(quote + item.conceptName?lower_case + quote)) as item><#if item?counter gt 1><#if item?counter == list?size> or <#else>, ${item}<#else>[none specified] <#-- ConceptSetSelection --> @@ -33,9 +33,9 @@ item?counter gt 1><#if item?counter == list?size> or <#else>, ${item <#-- NumericRange --> <#assign numericRangeOptions = [ - {"id": "lt", "name": "<"}, + {"id": "lt", "name": "<"}, {"id": "lte", "name": "<="}, - {"id": "eq", "name": "="}, + {"id": "eq", "name": "="}, {"id": "gt", "name": ">"}, {"id": "gte", "name": ">="}, {"id": "bt", "name": "between"}, @@ -90,15 +90,23 @@ item?counter gt 1><#if item?counter == list?size> or <#else>, ${item <#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.timeUnit == "day" && w.end.timeUnit == "day") || (!w.start.timeUnit?? && !w.end.timeUnit??)><#-- --><#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)} +--><#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)}<#-- +--><#else><#-- +--><#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}<#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.start.timeUnit} ${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} ${temporalDirection(w.start.coeff)} ${indexLabel} ${whichIndexPart(w.useIndexEnd!false)}<#-- +--><#else>between ${w.start.timeUnitValue!"all"} ${w.start.timeUnit} ${temporalDirection(w.start.coeff)} and ${w.end.timeUnitValue!"all"} ${w.start.timeUnit} ${temporalDirection(w.end.coeff)} ${indexLabel} ${whichIndexPart(w.useIndexEnd!false)}<#-- +--> <#-- User Defined Period --> -<#macro UserDefinedPeriod p><#if +<#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)} @@ -106,6 +114,6 @@ p.endDate?has_content><#if !p.startDate?has_content>a user defined end dat <#function toDatePart dateType><#if dateType == "START_DATE"><#return "start date"><#else><#return "end date"> -<#macro DateAdjustment da><#if +<#macro DateAdjustment da><#if da?has_content>starting ${(da.startOffset != 0)?then((da.startOffset?abs + " days " + (da.startOffset < 0)?then("before","after")), "on")}${(da.startWith != da.endWith)?then(" the event ${toDatePart(da.startWith)}","")}<#-- --> and ending ${(da.endOffset != 0)?then((da.endOffset?abs + " days " + (da.endOffset < 0)?then("before","after")), "on")} the event ${toDatePart(da.endWith)} \ No newline at end of file diff --git a/src/main/resources/resources/cohortdefinition/sql/customEraStrategy.sql b/src/main/resources/resources/cohortdefinition/sql/customEraStrategy.sql index c606ec0f..3bde650c 100644 --- a/src/main/resources/resources/cohortdefinition/sql/customEraStrategy.sql +++ b/src/main/resources/resources/cohortdefinition/sql/customEraStrategy.sql @@ -26,7 +26,7 @@ INTO #strategy_ends from @eventTable et JOIN ( - select ENDS.person_id, min(drug_exposure_start_date) as era_start_date, DATEADD(day,@offset, ENDS.era_end_date) as era_end_date + select ENDS.person_id, min(drug_exposure_start_date) as era_start_date, DATEADD(day, @offsetUnitValue, ENDS.era_end_date) as era_end_date from ( select de.person_id, de.drug_exposure_start_date, MIN(e.END_DATE) as era_end_date @@ -34,7 +34,7 @@ JOIN JOIN ( --cteEndDates - select PERSON_ID, DATEADD(day,-1 * @gapDays,EVENT_DATE) as END_DATE -- unpad the end date by @gapDays + select PERSON_ID, DATEADD(@gapUnit,-1 * @gapUnitValue,EVENT_DATE) as END_DATE -- unpad the end date by @gapDays FROM ( select PERSON_ID, EVENT_DATE, EVENT_TYPE, @@ -49,7 +49,7 @@ JOIN UNION ALL -- add the end dates with NULL as the row number, padding the end dates by @gapDays to allow a grace period for overlapping ranges. - select PERSON_ID, DATEADD(day,@gapDays,DRUG_EXPOSURE_END_DATE), 1 as EVENT_TYPE, NULL + select PERSON_ID, DATEADD(@gapUnit,@gapUnitValue,DRUG_EXPOSURE_END_DATE), 1 as EVENT_TYPE, NULL FROM #drugTarget D ) RAWDATA ) E diff --git a/src/main/resources/resources/cohortdefinition/sql/dateOffsetStrategy.sql b/src/main/resources/resources/cohortdefinition/sql/dateOffsetStrategy.sql index 16a7e395..b79289dd 100644 --- a/src/main/resources/resources/cohortdefinition/sql/dateOffsetStrategy.sql +++ b/src/main/resources/resources/cohortdefinition/sql/dateOffsetStrategy.sql @@ -1,6 +1,9 @@ -- date offset strategy -select event_id, person_id, - case when DATEADD(day,@offset,@dateField) > op_end_date then op_end_date else DATEADD(day,@offset,@dateField) end as end_date +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 INTO #strategy_ends from @eventTable; diff --git a/src/main/resources/resources/cohortdefinition/sql/generateCohort.sql b/src/main/resources/resources/cohortdefinition/sql/generateCohort.sql index 436f37bd..3d7a1829 100644 --- a/src/main/resources/resources/cohortdefinition/sql/generateCohort.sql +++ b/src/main/resources/resources/cohortdefinition/sql/generateCohort.sql @@ -2,7 +2,7 @@ SELECT event_id, person_id, start_date, end_date, op_start_date, op_end_date, visit_occurrence_id INTO #qualified_events -FROM +FROM ( select pe.event_id, pe.person_id, pe.start_date, pe.end_date, pe.op_start_date, pe.op_end_date, row_number() over (partition by pe.person_id order by pe.start_date @QualifiedEventSort) as ordinal, cast(pe.visit_occurrence_id as bigint) as visit_occurrence_id FROM (@primaryEventsQuery) pe @@ -63,7 +63,7 @@ from ( --cteEnds JOIN ( -- cteEndDates SELECT person_id - , DATEADD(day,-1 * @eraconstructorpad, event_date) as end_date + , DATEADD(@era_pad_unit, -1 * @eraconstructorpad, event_date) as end_date FROM ( SELECT @@ -84,7 +84,7 @@ from ( --cteEnds SELECT person_id - , DATEADD(day,@eraconstructorpad,end_date) as end_date + , DATEADD(@era_pad_unit, @eraconstructorpad, end_date) as end_date , 1 AS event_type FROM #cohort_rows ) RAWDATA diff --git a/src/test/java/org/ohdsi/circe/check/checkers/ComparisonsTest.java b/src/test/java/org/ohdsi/circe/check/checkers/ComparisonsTest.java index a9edf413..60cca983 100644 --- a/src/test/java/org/ohdsi/circe/check/checkers/ComparisonsTest.java +++ b/src/test/java/org/ohdsi/circe/check/checkers/ComparisonsTest.java @@ -132,6 +132,65 @@ public void compareTo() { assertEquals(5, Comparisons.compareTo(filter, window)); } + @Test + public void compareToHours() { + ObservationFilter filter = new ObservationFilter(); + filter.postDays = 10; + filter.priorDays = 5; + + Window window = new Window(); + window.start = new Window.Endpoint(); + window.end = new Window.Endpoint(); + window.start.timeUnit = "hour"; + window.start.timeUnitValue = 72; + window.start.coeff = -1; + + window.end.timeUnit = "hour"; + window.end.timeUnitValue = 120; + window.end.coeff = 1; + + assertEquals(604800, Comparisons.compareTo(filter, window)); + } + @Test + public void compareToMinute() { + ObservationFilter filter = new ObservationFilter(); + filter.postDays = 10; + filter.priorDays = 5; + + Window window = new Window(); + window.start = new Window.Endpoint(); + window.end = new Window.Endpoint(); + window.start.timeUnit = "minute"; + window.start.timeUnitValue = 30; + window.start.coeff = -1; + + window.end.timeUnit = "minute"; + window.end.timeUnitValue = 75; + window.end.coeff = 1; + + assertEquals(1289700, Comparisons.compareTo(filter, window)); + } + + @Test + public void compareToSecond() { + ObservationFilter filter = new ObservationFilter(); + filter.postDays = 10; + filter.priorDays = 5; + + Window window = new Window(); + window.start = new Window.Endpoint(); + window.end = new Window.Endpoint(); + window.start.timeUnit = "second"; + window.start.timeUnitValue = 30; + window.start.coeff = -1; + + window.end.timeUnit = "second"; + window.end.timeUnitValue = 75; + window.end.coeff = 1; + + assertEquals(1295895, Comparisons.compareTo(filter, window)); + } + @Test public void compareCriteria() { ConditionEra conditionEra1 = new ConditionEra(); diff --git a/src/test/java/org/ohdsi/circe/check/checkers/CriteriaCheckValueTest.java b/src/test/java/org/ohdsi/circe/check/checkers/CriteriaCheckValueTest.java index 062ef9ae..bed87985 100644 --- a/src/test/java/org/ohdsi/circe/check/checkers/CriteriaCheckValueTest.java +++ b/src/test/java/org/ohdsi/circe/check/checkers/CriteriaCheckValueTest.java @@ -18,8 +18,12 @@ public class CriteriaCheckValueTest { private static final CohortExpression WRONG_ADDITIONAL_EXPRESSION = CohortExpression.fromJson(ResourceHelper.GetResourceAsString("/checkers/additionalCriteriaCheckValueIncorrect.json")); + private static final CohortExpression WRONG_ADDITIONAL_EXPRESSION_HOUR = + CohortExpression.fromJson(ResourceHelper.GetResourceAsString("/checkers/additionalCriteriaCheckValueIncorrectHour.json")); private static final CohortExpression CORRECT_ADDITIONAL_EXPRESSION = - CohortExpression.fromJson(ResourceHelper.GetResourceAsString("/checkers/additionalCriteriaCheckValueCorrect.json")); + CohortExpression.fromJson(ResourceHelper.GetResourceAsString("/checkers/additionalCriteriaCheckValueCorrect.json")); + private static final CohortExpression CORRECT_ADDITIONAL_EXPRESSION_MINUTE = + CohortExpression.fromJson(ResourceHelper.GetResourceAsString("/checkers/additionalCriteriaCheckValueCorrectMinute.json")); private static final CohortExpression WRONG_INCLUSION_EXPRESSION = CohortExpression.fromJson(ResourceHelper.GetResourceAsString("/checkers/inclusionRulesCheckValueIncorrect.json")); @@ -119,12 +123,22 @@ public void checkAdditionalRangeIncorrect() { List warnings = rangeCheck.check(WRONG_ADDITIONAL_EXPRESSION); assertEquals(RANGE_ADDITIONAL_WARNING_COUNT, warnings.size()); } + @Test + public void checkAdditionalRangeIncorrectHour() { + List warnings = rangeCheck.check(WRONG_ADDITIONAL_EXPRESSION_HOUR); + assertEquals(RANGE_ADDITIONAL_WARNING_COUNT, warnings.size()); + } @Test public void checkAdditionalRangeCorrect() { List warnings = rangeCheck.check(CORRECT_ADDITIONAL_EXPRESSION); assertEquals(Collections.emptyList(), warnings); } + @Test + public void checkAdditionalRangeCorrectMinute() { + List warnings = rangeCheck.check(CORRECT_ADDITIONAL_EXPRESSION_MINUTE); + assertEquals(Collections.emptyList(), warnings); + } @Test public void checkAdditionalConceptIncorrect() { diff --git a/src/test/java/org/ohdsi/circe/check/checkers/DeathTimeWindowCheckTest.java b/src/test/java/org/ohdsi/circe/check/checkers/DeathTimeWindowCheckTest.java index bdfd11c1..ffed6523 100644 --- a/src/test/java/org/ohdsi/circe/check/checkers/DeathTimeWindowCheckTest.java +++ b/src/test/java/org/ohdsi/circe/check/checkers/DeathTimeWindowCheckTest.java @@ -14,6 +14,8 @@ public class DeathTimeWindowCheckTest { CohortExpression.fromJson(ResourceHelper.GetResourceAsString("/checkers/deathTimeWindowCheckIncorrect.json")); private static final CohortExpression CORRECT_EXPRESSION = CohortExpression.fromJson(ResourceHelper.GetResourceAsString("/checkers/deathTimeWindowCheckCorrect.json")); + private static final CohortExpression CORRECT_EXPRESSION_SECOND = + CohortExpression.fromJson(ResourceHelper.GetResourceAsString("/checkers/deathTimeWindowCheckCorrectSecond.json")); private static final int WARNING_COUNT = 2; @@ -30,4 +32,9 @@ public void checkCorrect() { List warnings = check.check(CORRECT_EXPRESSION); assertEquals(0, warnings.size()); } + @Test + public void checkCorrectSecond() { + List warnings = check.check(CORRECT_EXPRESSION_SECOND); + assertEquals(0, warnings.size()); + } } diff --git a/src/test/java/org/ohdsi/circe/cohortdefinition/CohortGeneration_5_0_0_Test.java b/src/test/java/org/ohdsi/circe/cohortdefinition/CohortGeneration_5_0_0_Test.java index 06d8652a..4da53add 100644 --- a/src/test/java/org/ohdsi/circe/cohortdefinition/CohortGeneration_5_0_0_Test.java +++ b/src/test/java/org/ohdsi/circe/cohortdefinition/CohortGeneration_5_0_0_Test.java @@ -7,7 +7,6 @@ import java.lang.reflect.Field; import java.sql.SQLException; import com.github.mjeanroy.dbunit.core.dataset.DataSetFactory; -import org.apache.commons.lang3.StringUtils; import org.dbunit.Assertion; import org.dbunit.DatabaseUnitException; import org.dbunit.database.IDatabaseConnection; @@ -136,7 +135,24 @@ public void allCriteriaTest() throws SQLException { // execute on database, expect no errors jdbcTemplate.batchUpdate(SqlSplit.splitSql(cohortSql)); } + @Test + public void allCriteriaTestWithTimeUnitInterval() throws SQLException { + + final CohortExpressionQueryBuilder.BuildExpressionQueryOptions options = buildExpressionQueryOptions(1,"allCriteriaTest"); + + // prepare results schema + prepareSchema(options.resultSchema, RESULTS_DDL_PATH); + + // load 'all' criteria json + final CohortExpression expression = CohortExpression + .fromJson(ResourceHelper.GetResourceAsString("/cohortgeneration/allCriteria/allCriteriaExpressionWithTimeUnitInterval.json")); + + // build Sql + final String cohortSql = buildExpressionSql(expression, options); + // execute on database, expect no errors + jdbcTemplate.batchUpdate(SqlSplit.splitSql(cohortSql)); + } /* first occurrence tests */ private void setFirstOccurrenceCriteria(final Criteria c) throws Exception { final Field first = c.getClass().getDeclaredField("first"); @@ -357,7 +373,83 @@ public void testGroups() throws Exception { final ITable expectedTable = expectedDataSet.getTable(RESULTS_SCHEMA + ".cohort"); // Assert actual database table match expected table - Assertion.assertEquals(expectedTable, actualTable); + Assertion.assertEquals(expectedTable, actualTable); + } + @Test + public void testGroupsHour() throws Exception { + final String RESULTS_SCHEMA = "groupsTest"; + final String[] testDataSetsPrep = new String[] { + "/datasets/vocabulary.json", + "/cohortgeneration/correlatedCriteria/groupTest_PREP.json" + }; + final IDatabaseConnection dbUnitCon = getConnection(); + + // prepare results schema for the specified options.resultSchema + prepareSchema(RESULTS_SCHEMA, RESULTS_DDL_PATH); + + // load test data into DB. + final IDataSet dsPrep = DataSetFactory.createDataSet(testDataSetsPrep); + DatabaseOperation.CLEAN_INSERT.execute(dbUnitCon, dsPrep); // clean load of the DB. Careful, clean means "delete the old stuff" + + CohortExpressionQueryBuilder.BuildExpressionQueryOptions options; + CohortExpression expression; + String cohortSql; + + // this expression has groups defined as: (A OR B OR (C AND D)) + // Cohort 1: Persons 1,2,3 satisfy these criteria through different branches + expression = CohortExpression.fromJson(ResourceHelper.GetResourceAsString("/cohortgeneration/correlatedCriteria/groupExpressionHour.json")); + options = buildExpressionQueryOptions(1, RESULTS_SCHEMA); + cohortSql = buildExpressionSql(expression, options); + // execute on database, expect no errors + jdbcTemplate.batchUpdate(SqlSplit.splitSql(cohortSql)); + + // Cohort 2: Change outer group to 'at least 2' + // person 1 satisfies A, B and (C AND D) + expression.additionalCriteria.type = "AT_LEAST"; + expression.additionalCriteria.count = 2; + options.cohortId = 2; + cohortSql = buildExpressionSql(expression, options); + // execute on database, expect no errors + jdbcTemplate.batchUpdate(SqlSplit.splitSql(cohortSql)); + + // cohort 3: change outer group to 'at most 1' + // person 2 and 3 only have 1: 2 has A and 3 has (C AND D) + expression.additionalCriteria.type = "AT_MOST"; + expression.additionalCriteria.count = 2; + options.cohortId = 3; + cohortSql = buildExpressionSql(expression, options); + // execute on database, expect no errors + jdbcTemplate.batchUpdate(SqlSplit.splitSql(cohortSql)); + + + // cohort 4: outer group 'ALL', change inner group to 'at most 0' + // No one passes, since that arrangement is a contradiction + expression.additionalCriteria.type = "ALL"; + expression.additionalCriteria.groups[0].type = "AT_MOST"; + expression.additionalCriteria.groups[0].count = 0; + options.cohortId = 4; + cohortSql = buildExpressionSql(expression, options); + // execute on database, expect no errors + jdbcTemplate.batchUpdate(SqlSplit.splitSql(cohortSql)); + + // cohort 5: remove inner group, an empty group should have no impact + // Person 1 has both A and B + expression.additionalCriteria.groups = new CriteriaGroup[0]; + options.cohortId = 5; + cohortSql = buildExpressionSql(expression, options); + // execute on database, expect no errors + jdbcTemplate.batchUpdate(SqlSplit.splitSql(cohortSql)); + + // Validate results + // Load actual records from cohort table + final ITable actualTable = dbUnitCon.createQueryTable(RESULTS_SCHEMA + ".cohort", String.format("SELECT * from %s ORDER BY cohort_definition_id, subject_id, cohort_start_date", RESULTS_SCHEMA + ".cohort")); + // Load expected data from an XML dataset + final String[] testDataSetsVerify = new String[] {"/cohortgeneration/correlatedCriteria/groupTest_VERIFY.json"}; + final IDataSet expectedDataSet = DataSetFactory.createDataSet(testDataSetsVerify); + final ITable expectedTable = expectedDataSet.getTable(RESULTS_SCHEMA + ".cohort"); + + // Assert actual database table match expected table + Assertion.assertEquals(expectedTable, actualTable); } diff --git a/src/test/java/org/ohdsi/circe/cohortdefinition/builders/CorelatedCriteria_5_0_0_Test.java b/src/test/java/org/ohdsi/circe/cohortdefinition/builders/CorelatedCriteria_5_0_0_Test.java index dfcfbc79..d1f8ccae 100644 --- a/src/test/java/org/ohdsi/circe/cohortdefinition/builders/CorelatedCriteria_5_0_0_Test.java +++ b/src/test/java/org/ohdsi/circe/cohortdefinition/builders/CorelatedCriteria_5_0_0_Test.java @@ -19,6 +19,7 @@ import org.ohdsi.circe.cohortdefinition.CorelatedCriteria; import org.ohdsi.circe.cohortdefinition.CriteriaGroup; import org.ohdsi.circe.cohortdefinition.Occurrence; +import org.ohdsi.circe.cohortdefinition.IntervalUnit; import org.ohdsi.sql.SqlRender; import org.ohdsi.sql.SqlTranslate; import org.slf4j.Logger; @@ -193,6 +194,53 @@ public void distinctVisitTest() throws Exception { } + @Test + public void distinctVisitTestWithTimeInterval() throws Exception { + final CohortExpressionQueryBuilder queryBuilder = new CohortExpressionQueryBuilder(); + final String RESULTS_SCHEMA = "distinct_visit"; + final String[] testDataSetsPrep = new String[] { "/datasets/vocabulary.json", + "/corelatedcriteria/distinctVisit_PREP.json"}; + + // Load expected data from an XML dataset + final String[] testDataSetsVerify = new String[] {"/corelatedcriteria/distinctVisit_VERIFY.json"}; + final IDataSet expectedDataSet = DataSetFactory.createDataSet(testDataSetsVerify); + + // prepare results schema for the specified options.resultSchema + prepareSchema(RESULTS_SCHEMA, RESULTS_DDL_PATH); + + final IDatabaseConnection dbUnitCon = getConnection(); + + // load test data into DB. + final IDataSet dsPrep = DataSetFactory.createDataSet(testDataSetsPrep); + DatabaseOperation.CLEAN_INSERT.execute(dbUnitCon, dsPrep); // clean load of the DB. Careful, clean means "delete the old stuff" + + /// build inclusion query for Group Criteria + CriteriaGroup cg = new CriteriaGroup(); + cg.type= "ALL"; + CorelatedCriteria cc = new CorelatedCriteria(); + cc.criteria = new ConditionOccurrence(); // find any condition occurence + cc.criteria.intervalUnit = IntervalUnit.HOUR.getName(); + cc.startWindow = CriteriaUtils.getPrior365Window(); + cc.occurrence = CriteriaUtils.getDistinctCount(CriteriaColumn.VISIT_ID, Occurrence.AT_LEAST, 2); + cg.criteriaList = new CorelatedCriteria[] { cc }; + + // translate to PG + String eventTable = String.format(CriteriaUtils.EVENT_TABLE_TEMPLATE, RESULTS_SCHEMA + ".cohort", "cdm", 1); + String countQuery = queryBuilder.getCriteriaGroupQuery(cg, eventTable); + String translatedCountQuery = SqlRender.renderSql(SqlTranslate.translateSql(countQuery, "postgresql"), + new String[] {"cdm_database_schema", "indexId"}, + new String[] {"cdm", "0"}); + + // Validate results + // perform inclusion query + final ITable actualInclusion = dbUnitCon.createQueryTable(RESULTS_SCHEMA + ".output", translatedCountQuery); + final ITable expectedInclusion = expectedDataSet.getTable(RESULTS_SCHEMA + ".output"); + + // Assert actual database table match expected table + Assertion.assertEquals(expectedInclusion, actualInclusion); + + } + @Test public void distinctDefaultTest() throws Exception { final CohortExpressionQueryBuilder queryBuilder = new CohortExpressionQueryBuilder(); diff --git a/src/test/java/org/ohdsi/circe/cohortdefinition/builders/CriteriaUtils.java b/src/test/java/org/ohdsi/circe/cohortdefinition/builders/CriteriaUtils.java index 40481a30..1d09bd0c 100644 --- a/src/test/java/org/ohdsi/circe/cohortdefinition/builders/CriteriaUtils.java +++ b/src/test/java/org/ohdsi/circe/cohortdefinition/builders/CriteriaUtils.java @@ -15,6 +15,7 @@ */ package org.ohdsi.circe.cohortdefinition.builders; +import org.ohdsi.circe.cohortdefinition.IntervalUnit; import org.ohdsi.circe.cohortdefinition.Occurrence; import org.ohdsi.circe.cohortdefinition.Window; @@ -30,15 +31,48 @@ public static Window getPrior365Window() { Window.Endpoint startPoint = new Window.Endpoint(); startPoint.coeff = -1; startPoint.days = 365; + startPoint.timeUnitValue = null; prior365Window.start = startPoint; // ... and 0 days before Window.Endpoint endPoint = new Window.Endpoint(); endPoint.coeff = -1; endPoint.days = 0; + endPoint.timeUnitValue = 0; prior365Window.end = endPoint; return prior365Window; } - + + public static Window getPrior365WindowTimeUnitInterval(String timeUnit) { + Window prior365Window = new Window(); + // index starts between 365d before + Window.Endpoint startPoint = new Window.Endpoint(); + startPoint.coeff = -1; + startPoint.timeUnit = timeUnit; + startPoint.timeUnitValue = getTimeUnitValue(timeUnit, 365); + prior365Window.start = startPoint; + // ... and 0 days before + Window.Endpoint endPoint = new Window.Endpoint(); + endPoint.coeff = -1; + endPoint.days = getTimeUnitValue(timeUnit, 0); + prior365Window.end = endPoint; + return prior365Window; + } + + private static int getTimeUnitValue(String timeUnit, Integer timeUnitValueInDay) { + int timeUnitValue = timeUnitValueInDay; + if (IntervalUnit.HOUR.getName().equals(timeUnit)) { + timeUnitValue = timeUnitValueInDay * 24; + } + if (IntervalUnit.MINUTE.getName().equals(timeUnit)) { + timeUnitValue = timeUnitValueInDay * 24 * 60; + } + if (IntervalUnit.SECOND.getName().equals(timeUnit)) { + timeUnitValue = timeUnitValueInDay * 24 * 60 * 60; + } + return timeUnitValue; + } + + public static Window getAnyTimeWindow() { Window anytimeWindow = new Window(); // index starts between 365d before diff --git a/src/test/java/org/ohdsi/circe/cohortdefinition/builders/WindowCriteria_5_0_0_Test.java b/src/test/java/org/ohdsi/circe/cohortdefinition/builders/WindowCriteria_5_0_0_Test.java index 5988a172..5776d01a 100644 --- a/src/test/java/org/ohdsi/circe/cohortdefinition/builders/WindowCriteria_5_0_0_Test.java +++ b/src/test/java/org/ohdsi/circe/cohortdefinition/builders/WindowCriteria_5_0_0_Test.java @@ -130,6 +130,21 @@ public void windowConditionOccurrenceTest() throws Exception { } + @Test + public void windowConditionOccurrenceTestInMinuteInterval() throws Exception { + + final String resultsSchema = "window_condition_occurrence"; + final String[] testDataSetsPrep = new String[]{"/datasets/vocabulary.json", + "/windowcriteria/windowConditionOccurrence_PREP.json"}; + final String[] testDataSetsVerify = new String[]{"/windowcriteria/windowConditionOccurrence_VERIFY.json"}; + WindowedCriteria wc = new WindowedCriteria(); + wc.criteria = new ConditionOccurrence(); // find any condition occurence + wc.startWindow = CriteriaUtils.getPrior365WindowTimeUnitInterval(IntervalUnit.MINUTE.getName()); + List additionalColumns = Arrays.asList(CriteriaColumn.START_DATE, CriteriaColumn.END_DATE, CriteriaColumn.DOMAIN_CONCEPT, CriteriaColumn.DURATION); + this.performWindowTest(wc, resultsSchema, testDataSetsPrep, testDataSetsVerify, additionalColumns); + + } + @Test public void windowDeathTest() throws Exception { @@ -145,6 +160,22 @@ public void windowDeathTest() throws Exception { } + @Test + public void windowDeathTestInSecondInterval() throws Exception { + + final String resultsSchema = "window_death"; + final String[] testDataSetsPrep = new String[]{"/datasets/vocabulary.json", + "/windowcriteria/windowDeathTimeInterval_PREP.json"}; + final String[] testDataSetsVerify = new String[]{"/windowcriteria/windowDeathTimeInterval_VERIFY.json"}; + WindowedCriteria wc = new WindowedCriteria(); + wc.criteria = new Death(); // find any death + wc.criteria.intervalUnit = IntervalUnit.SECOND.getName(); + wc.startWindow = CriteriaUtils.getPrior365WindowTimeUnitInterval(IntervalUnit.SECOND.getName()); + List additionalColumns = Arrays.asList(CriteriaColumn.START_DATE, CriteriaColumn.END_DATE, CriteriaColumn.DOMAIN_CONCEPT, CriteriaColumn.DURATION); + this.performWindowTest(wc, resultsSchema, testDataSetsPrep, testDataSetsVerify, additionalColumns); + + } + @Test public void windowDeviceExposureTest() throws Exception { @@ -160,6 +191,20 @@ public void windowDeviceExposureTest() throws Exception { } + @Test + public void windowDeviceExposureTestInHourInterval() throws Exception { + final String resultsSchema = "window_device_exposure"; + final String[] testDataSetsPrep = new String[]{"/datasets/vocabulary.json", + "/windowcriteria/windowDeviceExposureTimeInterval_PREP.json"}; + final String[] testDataSetsVerify = new String[]{"/windowcriteria/windowDeviceExposureTimeInterval_VERIFY.json"}; + WindowedCriteria wc = new WindowedCriteria(); + wc.criteria = new DeviceExposure(); // find any device exposure + wc.criteria.intervalUnit = IntervalUnit.HOUR.getName(); + wc.startWindow = CriteriaUtils.getPrior365WindowTimeUnitInterval(IntervalUnit.HOUR.getName()); + List additionalColumns = Arrays.asList(CriteriaColumn.START_DATE, CriteriaColumn.END_DATE, CriteriaColumn.DOMAIN_CONCEPT, CriteriaColumn.QUANTITY, CriteriaColumn.DURATION); + this.performWindowTest(wc, resultsSchema, testDataSetsPrep, testDataSetsVerify, additionalColumns); + } + @Test public void windowDoseEraTest() throws Exception { @@ -208,6 +253,20 @@ public void windowDrugExposureTest() throws Exception { } + @Test + public void windowDrugExposureTestInMinuteInterval() throws Exception { + final String resultsSchema = "window_drug_exposure"; + final String[] testDataSetsPrep = new String[]{"/datasets/vocabulary.json", "/windowcriteria/windowDrugExposureTimeInterval_PREP.json"}; + final String[] testDataSetsVerify = new String[]{"/windowcriteria/windowDrugExposureTimeInterval_VERIFY.json"}; + WindowedCriteria wc = new WindowedCriteria(); + wc.criteria = new DrugExposure(); // find any drug exposure + wc.criteria.intervalUnit = IntervalUnit.MINUTE.getName(); + wc.startWindow = CriteriaUtils.getPrior365WindowTimeUnitInterval(IntervalUnit.MINUTE.getName()); + List additionalColumns = Arrays.asList(CriteriaColumn.START_DATE, CriteriaColumn.END_DATE, + CriteriaColumn.DOMAIN_CONCEPT, CriteriaColumn.REFILLS, CriteriaColumn.QUANTITY, CriteriaColumn.DAYS_SUPPLY, CriteriaColumn.DURATION); + this.performWindowTest(wc, resultsSchema, testDataSetsPrep, testDataSetsVerify, additionalColumns); + } + @Test public void windowMeasurementTest() throws Exception { diff --git a/src/test/java/org/ohdsi/circe/cohortdefinition/builders/WindowCriteria_5_3_0_Test.java b/src/test/java/org/ohdsi/circe/cohortdefinition/builders/WindowCriteria_5_3_0_Test.java index a7614636..37a25255 100644 --- a/src/test/java/org/ohdsi/circe/cohortdefinition/builders/WindowCriteria_5_3_0_Test.java +++ b/src/test/java/org/ohdsi/circe/cohortdefinition/builders/WindowCriteria_5_3_0_Test.java @@ -114,4 +114,18 @@ public void windowVisitDetailTest() throws Exception { List additionalColumns = Arrays.asList(CriteriaColumn.START_DATE, CriteriaColumn.END_DATE, CriteriaColumn.DOMAIN_CONCEPT, CriteriaColumn.DURATION); this.performWindowTest(wc, resultsSchema, testDataSetsPrep, testDataSetsVerify, additionalColumns); } + + @Test + public void windowVisitDetailTestInSecondInterval() throws Exception { + final String resultsSchema = "window_visit_detail"; + final String[] testDataSetsPrep = new String[]{"/datasets/vocabulary.json", + "/windowcriteria/windowVisitDetailTimeInterval_PREP.json"}; + final String[] testDataSetsVerify = new String[]{"/windowcriteria/windowVisitDetailTimeInterval_VERIFY.json"}; + WindowedCriteria wc = new WindowedCriteria(); + wc.criteria = new VisitDetail(); // find any visit detail + wc.criteria.intervalUnit = IntervalUnit.SECOND.getName(); + wc.startWindow = CriteriaUtils.getPrior365WindowTimeUnitInterval(IntervalUnit.SECOND.getName()); + List additionalColumns = Arrays.asList(CriteriaColumn.START_DATE, CriteriaColumn.END_DATE, CriteriaColumn.DOMAIN_CONCEPT, CriteriaColumn.DURATION); + this.performWindowTest(wc, resultsSchema, testDataSetsPrep, testDataSetsVerify, additionalColumns); + } } diff --git a/src/test/java/org/ohdsi/circe/cohortdefinition/printfriendly/PrintFriendlyTest.java b/src/test/java/org/ohdsi/circe/cohortdefinition/printfriendly/PrintFriendlyTest.java index 4e0dc1a0..49ea1f26 100644 --- a/src/test/java/org/ohdsi/circe/cohortdefinition/printfriendly/PrintFriendlyTest.java +++ b/src/test/java/org/ohdsi/circe/cohortdefinition/printfriendly/PrintFriendlyTest.java @@ -455,6 +455,36 @@ public void customEraExitTest() { )); } + @Test + public void customEraExitTestHour() { + CohortExpression expression = CohortExpression.fromJson(ResourceHelper.GetResourceAsString("/printfriendly/customEraExitHour.json")); + String markdown = pf.renderCohort(expression); + assertThat(markdown, stringContainsInOrder( + "The cohort end date will be based on a continuous exposure to 'Concept Set 1':", + "allowing 336 hour between exposures, adding 24 hours after exposure ends, and forcing drug exposure hour supply to: 168 hours." + )); + + } + @Test + public void customEraExitTestMinute() { + CohortExpression expression = CohortExpression.fromJson(ResourceHelper.GetResourceAsString("/printfriendly/customEraExitMinute.json")); + String markdown = pf.renderCohort(expression); + assertThat(markdown, stringContainsInOrder( + "The cohort end date will be based on a continuous exposure to 'Concept Set 1':", + "allowing 20,160 minute between exposures, adding 1,440 minutes after exposure ends, and forcing drug exposure minute supply to: 10,080 minutes." + )); + + } + @Test + public void customEraExitTestSecond() { + CohortExpression expression = CohortExpression.fromJson(ResourceHelper.GetResourceAsString("/printfriendly/customEraExitSecond.json")); + String markdown = pf.renderCohort(expression); + assertThat(markdown, stringContainsInOrder( + "The cohort end date will be based on a continuous exposure to 'Concept Set 1':", + "allowing 1,209,600 second between exposures, adding 86,400 seconds after exposure ends, and forcing drug exposure second supply to: 604,800 seconds." + )); + + } @Test public void conceptSetSimpleTest() { diff --git a/src/test/resources/checkers/additionalCriteriaCheckValueCorrect.json b/src/test/resources/checkers/additionalCriteriaCheckValueCorrect.json index 9204b353..3e1ea072 100644 --- a/src/test/resources/checkers/additionalCriteriaCheckValueCorrect.json +++ b/src/test/resources/checkers/additionalCriteriaCheckValueCorrect.json @@ -539,7 +539,7 @@ "CensoringCriteria": [], "CollapseSettings": { "CollapseType": "ERA", - "EraPad": 0 + "EraPad": 1 }, "CensorWindow": {}, "cdmVersionRange": ">=6.1.0" diff --git a/src/test/resources/checkers/additionalCriteriaCheckValueCorrectMinute.json b/src/test/resources/checkers/additionalCriteriaCheckValueCorrectMinute.json new file mode 100644 index 00000000..a1dac8bb --- /dev/null +++ b/src/test/resources/checkers/additionalCriteriaCheckValueCorrectMinute.json @@ -0,0 +1,562 @@ +{ + "ConceptSets": [ + { + "id": 0, + "name": "Aspirin", + "expression": { + "items": [ + { + "concept": { + "CONCEPT_CLASS_ID": "Ingredient", + "CONCEPT_CODE": "1191", + "CONCEPT_ID": 1112807, + "CONCEPT_NAME": "Aspirin", + "DOMAIN_ID": "Drug", + "INVALID_REASON": "V", + "INVALID_REASON_CAPTION": "Valid", + "STANDARD_CONCEPT": "S", + "STANDARD_CONCEPT_CAPTION": "Standard", + "VOCABULARY_ID": "RxNorm" + }, + "includeDescendants": true + } + ] + } + } + ], + "PrimaryCriteria": { + "CriteriaList": [], + "ObservationWindow": { + "PriorDays": 0, + "PostDays": 0 + }, + "PrimaryCriteriaLimit": { + "Type": "First" + } + }, + "AdditionalCriteria": { + "Type": "ALL", + "CriteriaList": [], + "DemographicCriteriaList": [], + "Groups": [ + { + "Type": "ALL", + "CriteriaList": [ + { + "Criteria": { + "ConditionEra": { + "CorrelatedCriteria": { + "Type": "ALL", + "CriteriaList": [ + { + "Criteria": { + "ConditionOccurrence": { + "CorrelatedCriteria": { + "Type": "ALL", + "CriteriaList": [ + { + "Criteria": { + "Measurement": { + "OccurrenceStartDate": { + "Value": "2020-01-03", + "Op": "lt" + }, + "MeasurementType": [ + { + "CONCEPT_CODE": "10221000000101", + "CONCEPT_ID": 44787754, + "CONCEPT_NAME": "FP70A postgraduate training allowance form", + "DOMAIN_ID": "Type Concept", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "SNOMED" + } + ], + "Operator": [ + { + "CONCEPT_CODE": "276139006", + "CONCEPT_ID": 4171756, + "CONCEPT_NAME": "<", + "DOMAIN_ID": "Meas Value Operator", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "SNOMED" + } + ], + "ValueAsNumber": { + "Value": 3, + "Op": "gt" + }, + "ValueAsConcept": [ + { + "CONCEPT_CODE": "17511009", + "CONCEPT_ID": 4068402, + "CONCEPT_NAME": "Normal absence", + "DOMAIN_ID": "Meas Value", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "SNOMED" + } + ], + "Unit": [ + { + "CONCEPT_CODE": "%{abnormal}", + "CONCEPT_ID": 9216, + "CONCEPT_NAME": "percent abnormal", + "DOMAIN_ID": "Unit", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "UCUM" + } + ], + "RangeLow": { + "Value": 2, + "Op": "gt" + }, + "RangeHigh": { + "Value": 2, + "Op": "gt" + }, + "RangeLowRatio": { + "Value": 2, + "Op": "gt" + }, + "RangeHighRatio": { + "Value": 2, + "Op": "gt" + }, + "Age": { + "Value": 2, + "Op": "gt" + }, + "Gender": [ + { + "CONCEPT_CODE": "703118005", + "CONCEPT_ID": 45766035, + "CONCEPT_NAME": "Feminine gender", + "DOMAIN_ID": "Gender", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "SNOMED" + } + ], + "ProviderSpecialty": [ + { + "CONCEPT_CODE": "101YS0200X", + "CONCEPT_ID": 38003628, + "CONCEPT_NAME": "Behavioral Health & Social Service Providers, Counselor, School", + "DOMAIN_ID": "Provider Specialty", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "NUCC" + } + ], + "VisitType": [ + { + "CONCEPT_CODE": "OP", + "CONCEPT_ID": 9202, + "CONCEPT_NAME": "Outpatient Visit", + "DOMAIN_ID": "Visit", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Visit" + } + ] + } + }, + "StartWindow": { + "Start": { + "timeUnitValue": "1440", + "TimeUnit": "minute", + "Coeff": -1 + }, + "End": { + "timeUnitValue": "0", + "TimeUnit": "minute", + "Coeff": 1 + }, + "UseEventEnd": false + }, + "Occurrence": { + "Type": 2, + "Count": 1 + } + }, + { + "Criteria": { + "DoseEra": { + "EraStartDate": { + "Value": "2019-12-31", + "Op": "lt" + }, + "EraEndDate": { + "Value": "2020-01-01", + "Op": "lt" + }, + "DoseValue": { + "Value": 2, + "Op": "gt" + }, + "EraLength": { + "Value": 3, + "Op": "gt" + }, + "AgeAtStart": { + "Value": 4, + "Op": "gt" + }, + "AgeAtEnd": { + "Value": 5, + "Op": "gt" + }, + "Gender": [ + { + "CONCEPT_CODE": "407378000", + "CONCEPT_ID": 4251434, + "CONCEPT_NAME": "Surgically transgendered transsexual, male-to-female", + "DOMAIN_ID": "Gender", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "SNOMED" + } + ] + } + }, + "StartWindow": { + "Start": { + "timeUnitValue": "2880", + "TimeUnit": "minute", + "Coeff": -1 + }, + "End": { + "timeUnitValue": "0", + "TimeUnit": "minute", + "Coeff": 1 + }, + "UseEventEnd": false + }, + "Occurrence": { + "Type": 2, + "Count": 1 + } + }, + { + "Criteria": { + "DeviceExposure": { + "UniqueDeviceId": { + "Text": "13", + "Op": "contains" + } + } + }, + "StartWindow": { + "Start": { + "Coeff": -1 + }, + "End": { + "Coeff": 1 + }, + "UseEventEnd": false + }, + "Occurrence": { + "Type": 2, + "Count": 1 + } + } + ], + "DemographicCriteriaList": [ + { + "Age": { + "Value": 2, + "Op": "gt" + }, + "Gender": [ + { + "CONCEPT_CODE": "394744001", + "CONCEPT_ID": 4215271, + "CONCEPT_NAME": "Gender unspecified", + "DOMAIN_ID": "Gender", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "SNOMED" + } + ], + "Race": [ + { + "CONCEPT_CODE": "2.01", + "CONCEPT_ID": 38003574, + "CONCEPT_NAME": "Asian Indian", + "DOMAIN_ID": "Race", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Race" + } + ], + "Ethnicity": [ + { + "CONCEPT_CODE": "Not Hispanic", + "CONCEPT_ID": 38003564, + "CONCEPT_NAME": "Not Hispanic or Latino", + "DOMAIN_ID": "Ethnicity", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Ethnicity" + } + ], + "OccurrenceStartDate": { + "Value": "2020-01-01", + "Op": "lt" + }, + "OccurrenceEndDate": { + "Value": "2020-01-02", + "Op": "lt" + } + } + ], + "Groups": [] + }, + "OccurrenceStartDate": { + "Value": "2019-12-31", + "Op": "lt" + }, + "OccurrenceEndDate": { + "Value": "2020-01-01", + "Op": "lt" + }, + "ConditionType": [ + { + "CONCEPT_CODE": "10251000000106", + "CONCEPT_ID": 45770878, + "CONCEPT_NAME": "FP75 leave payment form", + "DOMAIN_ID": "Type Concept", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "SNOMED" + } + ], + "StopReason": { + "Text": "stop", + "Op": "contains" + }, + "Age": { + "Value": 1, + "Op": "lt" + }, + "Gender": [ + { + "CONCEPT_CODE": "703117000", + "CONCEPT_ID": 45766034, + "CONCEPT_NAME": "Masculine gender", + "DOMAIN_ID": "Gender", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "SNOMED" + } + ], + "VisitType": [ + { + "CONCEPT_CODE": "LTCP", + "CONCEPT_ID": 42898160, + "CONCEPT_NAME": "Long Term Care Visit", + "DOMAIN_ID": "Visit", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Visit" + } + ] + } + }, + "StartWindow": { + "Start": { + "timeUnitValue": "4320", + "TimeUnit": "minute", + "Coeff": -1 + }, + "End": { + "timeUnitValue": "0", + "TimeUnit": "minute", + "Coeff": 1 + }, + "UseEventEnd": false + }, + "Occurrence": { + "Type": 2, + "Count": 1 + } + } + ], + "DemographicCriteriaList": [ + { + "Age": { + "Value": 2, + "Op": "lt" + }, + "Gender": [ + { + "CONCEPT_CODE": "407379008", + "CONCEPT_ID": 4231242, + "CONCEPT_NAME": "Surgically transgendered transsexual, female-to-male", + "DOMAIN_ID": "Gender", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "SNOMED" + } + ], + "Race": [ + { + "CONCEPT_CODE": "2.01", + "CONCEPT_ID": 38003574, + "CONCEPT_NAME": "Asian Indian", + "DOMAIN_ID": "Race", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Race" + } + ], + "Ethnicity": [ + { + "CONCEPT_CODE": "Not Hispanic", + "CONCEPT_ID": 38003564, + "CONCEPT_NAME": "Not Hispanic or Latino", + "DOMAIN_ID": "Ethnicity", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Ethnicity" + } + ], + "OccurrenceStartDate": { + "Value": "2019-12-31", + "Op": "lt" + }, + "OccurrenceEndDate": { + "Value": "2020-01-01", + "Op": "lt" + } + } + ], + "Groups": [] + }, + "EraStartDate": { + "Value": "2019-12-31", + "Op": "lt" + }, + "EraEndDate": { + "Value": "2019-12-31", + "Op": "lt" + }, + "OccurrenceCount": { + "Value": 1, + "Op": "lt" + }, + "EraLength": { + "Value": 1, + "Op": "lt" + }, + "AgeAtStart": { + "Value": 2, + "Op": "lt" + }, + "AgeAtEnd": { + "Value": 2, + "Op": "lt" + }, + "Gender": [ + { + "CONCEPT_CODE": "407375002", + "CONCEPT_ID": 4234363, + "CONCEPT_NAME": "Surgically transgendered transsexual", + "DOMAIN_ID": "Gender", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "SNOMED" + } + ] + } + }, + "StartWindow": { + "Start": { + "timeUnitValue": "5460", + "TimeUnit": "minute", + "Coeff": -1 + }, + "End": { + "timeUnitValue": "0", + "TimeUnit": "minute", + "Coeff": 1 + }, + "UseEventEnd": false + }, + "Occurrence": { + "Type": 2, + "Count": 1 + } + } + ], + "DemographicCriteriaList": [ + { + "Age": { + "Value": 1, + "Op": "gt" + }, + "Gender": [ + { + "CONCEPT_CODE": "407375002", + "CONCEPT_ID": 4234363, + "CONCEPT_NAME": "Surgically transgendered transsexual", + "DOMAIN_ID": "Gender", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "SNOMED" + } + ], + "Race": [ + { + "CONCEPT_CODE": "2.02", + "CONCEPT_ID": 38003575, + "CONCEPT_NAME": "Bangladeshi", + "DOMAIN_ID": "Race", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Race" + } + ], + "Ethnicity": [ + { + "CONCEPT_CODE": "Hispanic", + "CONCEPT_ID": 38003563, + "CONCEPT_NAME": "Hispanic or Latino", + "DOMAIN_ID": "Ethnicity", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Ethnicity" + } + ], + "OccurrenceStartDate": { + "Value": "2019-12-31", + "Op": "lt" + }, + "OccurrenceEndDate": { + "Value": "2019-12-31", + "Op": "lt" + } + } + ], + "Groups": [] + } + ] + }, + "QualifiedLimit": { + "Type": "First" + }, + "ExpressionLimit": { + "Type": "First" + }, + "InclusionRules": [], + "CensoringCriteria": [], + "CollapseSettings": { + "CollapseType": "ERA", + "EraPad": 1 + }, + "CensorWindow": {}, + "cdmVersionRange": ">=6.1.0" +} \ No newline at end of file diff --git a/src/test/resources/checkers/additionalCriteriaCheckValueIncorrectHour.json b/src/test/resources/checkers/additionalCriteriaCheckValueIncorrectHour.json new file mode 100644 index 00000000..9c69f542 --- /dev/null +++ b/src/test/resources/checkers/additionalCriteriaCheckValueIncorrectHour.json @@ -0,0 +1,329 @@ +{ + "ConceptSets": [ + { + "id": 0, + "name": "Aspirin", + "expression": { + "items": [ + { + "concept": { + "CONCEPT_CLASS_ID": "Ingredient", + "CONCEPT_CODE": "1191", + "CONCEPT_ID": 1112807, + "CONCEPT_NAME": "Aspirin", + "DOMAIN_ID": "Drug", + "INVALID_REASON": "V", + "INVALID_REASON_CAPTION": "Valid", + "STANDARD_CONCEPT": "S", + "STANDARD_CONCEPT_CAPTION": "Standard", + "VOCABULARY_ID": "RxNorm" + }, + "includeDescendants": true + } + ] + } + } + ], + "PrimaryCriteria": { + "CriteriaList": [], + "ObservationWindow": { + "PriorDays": -3, + "PostDays": -2 + }, + "PrimaryCriteriaLimit": { + "Type": "All" + } + }, + "AdditionalCriteria": { + "Type": "ALL", + "CriteriaList": [], + "DemographicCriteriaList": [], + "Groups": [ + { + "Type": "ALL", + "CriteriaList": [ + { + "Criteria": { + "ConditionEra": { + "CorrelatedCriteria": { + "Type": "ALL", + "CriteriaList": [ + { + "Criteria": { + "ConditionOccurrence": { + "CorrelatedCriteria": { + "Type": "ALL", + "CriteriaList": [ + { + "Criteria": { + "Measurement": { + "OccurrenceStartDate": { + "Op": "lt" + }, + "MeasurementType": [], + "Operator": [], + "ValueAsNumber": { + "Op": "gt" + }, + "ValueAsConcept": [], + "Unit": [], + "RangeLow": { + "Op": "gt" + }, + "RangeHigh": { + "Op": "gt" + }, + "RangeLowRatio": { + "Op": "gt" + }, + "RangeHighRatio": { + "Op": "gt" + }, + "Age": { + "Op": "gt" + }, + "Gender": [], + "ProviderSpecialty": [], + "VisitType": [] + } + }, + "StartWindow": { + "Start": { + "Coeff": -1 + }, + "End": { + "Coeff": 1 + }, + "UseEventEnd": false + }, + "Occurrence": { + "Type": 2, + "Count": 1 + } + }, + { + "Criteria": { + "DeviceExposure": { + "UniqueDeviceId": { + "Op": "contains" + } + } + }, + "StartWindow": { + "Start": { + "TimeUnitValue": 24, + "TimeUnit": "hour", + "Coeff": -1 + }, + "End": { + "TimeUnitValue": 0, + "TimeUnit": "hour", + "Coeff": 1 + }, + "UseEventEnd": false + }, + "Occurrence": { + "Type": 2, + "Count": 1 + } + } + ], + "DemographicCriteriaList": [ + { + "Age": { + "Op": "gt" + }, + "Gender": [], + "Race": [], + "Ethnicity": [], + "OccurrenceStartDate": { + "Op": "lt" + }, + "OccurrenceEndDate": { + "Op": "lt" + } + } + ], + "Groups": [] + }, + "OccurrenceStartDate": { + "Op": "lt" + }, + "OccurrenceEndDate": { + "Op": "lt" + }, + "ConditionType": [], + "StopReason": { + "Op": "contains" + }, + "Age": { + "Op": "lt" + }, + "Gender": [], + "ProviderSpecialty": [], + "VisitType": [] + } + }, + "StartWindow": { + "Start": { + "TimeUnitValue": 24, + "TimeUnit": "hour", + "Coeff": -1 + }, + "End": { + "TimeUnitValue": 0, + "TimeUnit": "hour", + "Coeff": 1 + }, + "UseEventEnd": false + }, + "Occurrence": { + "Type": 2, + "Count": 1 + } + }, + { + "Criteria": { + "DoseEra": { + "EraStartDate": { + "Op": "lt" + }, + "EraEndDate": { + "Op": "lt" + }, + "Unit": [], + "DoseValue": { + "Op": "gt" + }, + "EraLength": { + "Op": "gt" + }, + "AgeAtStart": { + "Op": "gt" + }, + "AgeAtEnd": { + "Op": "gt" + }, + "Gender": [] + } + }, + "StartWindow": { + "Start": { + "TimeUnitValue": 12, + "TimeUnit": "hour", + "Coeff": -1 + }, + "End": { + "TimeUnitValue": 0, + "TimeUnit": "hour", + "Coeff": 1 + }, + "UseEventEnd": false + }, + "Occurrence": { + "Type": 2, + "Count": 1 + } + } + ], + "DemographicCriteriaList": [ + { + "Age": { + "Op": "lt" + }, + "Gender": [], + "Race": [], + "Ethnicity": [], + "OccurrenceStartDate": { + "Op": "lt" + }, + "OccurrenceEndDate": { + "Op": "lt" + } + } + ], + "Groups": [] + }, + "EraStartDate": { + "Value": "2020-01-16", + "Extent": "2020-01-16", + "Op": "bt" + }, + "EraEndDate": { + "Op": "lt" + }, + "OccurrenceCount": { + "Value": -5, + "Op": "lt" + }, + "EraLength": { + "Op": "lt" + }, + "AgeAtStart": { + "Op": "lt" + }, + "AgeAtEnd": { + "Op": "lt" + }, + "Gender": [] + } + }, + "StartWindow": { + "Start": { + "TimeUnitValue": -72, + "timeUnit": "hour", + "Coeff": -1 + }, + "End": { + "TimeUnitValue": -48, + "timeUnit": "hour", + "Coeff": 1 + }, + "UseEventEnd": false + }, + "Occurrence": { + "Type": 2, + "Count": 1 + } + } + ], + "DemographicCriteriaList": [ + { + "Age": { + "Value": 5, + "Extent": 1, + "Op": "bt" + }, + "Gender": [], + "Race": [], + "Ethnicity": [], + "OccurrenceStartDate": { + "Value": "2020-01-03", + "Extent": "2020-01-32", + "Op": "bt" + }, + "OccurrenceEndDate": { + "Op": "lt" + } + } + ], + "Groups": [] + } + ] + }, + "QualifiedLimit": { + "Type": "First" + }, + "ExpressionLimit": { + "Type": "First" + }, + "InclusionRules": [], + "CensoringCriteria": [], + "CollapseSettings": { + "CollapseType": "ERA", + "EraPadUnit": "hour", + "EraPadUnitValue": 0 + }, + "CensorWindow": {}, + "cdmVersionRange": ">=6.1.0" +} \ No newline at end of file diff --git a/src/test/resources/checkers/deathTimeWindowCheckCorrectSecond.json b/src/test/resources/checkers/deathTimeWindowCheckCorrectSecond.json new file mode 100644 index 00000000..906e64e6 --- /dev/null +++ b/src/test/resources/checkers/deathTimeWindowCheckCorrectSecond.json @@ -0,0 +1,140 @@ +{ + "ConceptSets": [ + { + "id": 2, + "name": "ibuprofen", + "expression": { + "items": [ + { + "concept": { + "CONCEPT_CLASS_ID": "Ingredient", + "CONCEPT_CODE": "5640", + "CONCEPT_ID": 1177480, + "CONCEPT_NAME": "Ibuprofen", + "DOMAIN_ID": "Drug", + "INVALID_REASON": "V", + "INVALID_REASON_CAPTION": "Valid", + "STANDARD_CONCEPT": "S", + "STANDARD_CONCEPT_CAPTION": "Standard", + "VOCABULARY_ID": "RxNorm" + }, + "includeDescendants": true + } + ] + } + } + ], + "PrimaryCriteria": { + "CriteriaList": [ + { + "Death": { + "CorrelatedCriteria": { + "Type": "ALL", + "CriteriaList": [ + { + "Criteria": { + "Death": { + "CodesetId": 2 + } + }, + "StartWindow": { + "Start": { + "timeUnitValue": "604800", + "TimeUnit": "second", + "Coeff": -1 + }, + "End": { + "Coeff": 1 + }, + "UseEventEnd": false + }, + "Occurrence": { + "Type": 2, + "Count": 1 + } + } + ], + "DemographicCriteriaList": [], + "Groups": [] + }, + "CodesetId": 2 + } + } + ], + "ObservationWindow": { + "PriorDays": 14, + "PostDays": 0 + }, + "PrimaryCriteriaLimit": { + "Type": "All" + } + }, + "AdditionalCriteria": { + "Type": "ALL", + "CriteriaList": [ + { + "Criteria": { + "Death": { + "CorrelatedCriteria": { + "Type": "ALL", + "CriteriaList": [ + { + "Criteria": { + "Death": {} + }, + "StartWindow": { + "Start": { + "Days": 7, + "Coeff": -1 + }, + "End": { + "Coeff": 1 + }, + "UseEventEnd": false + }, + "Occurrence": { + "Type": 2, + "Count": 1 + } + } + ], + "DemographicCriteriaList": [], + "Groups": [] + } + } + }, + "StartWindow": { + "Start": { + "timeUnitValue": "1209600", + "TimeUnit": "second", + "Coeff": -1 + }, + "End": { + "Coeff": 1 + }, + "UseEventEnd": false + }, + "Occurrence": { + "Type": 2, + "Count": 1 + } + } + ], + "DemographicCriteriaList": [], + "Groups": [] + }, + "QualifiedLimit": { + "Type": "All" + }, + "ExpressionLimit": { + "Type": "Last" + }, + "InclusionRules": [], + "CensoringCriteria": [], + "CollapseSettings": { + "CollapseType": "ERA", + "EraPad": 0 + }, + "CensorWindow": {}, + "cdmVersionRange": ">=5.3.0" +} \ No newline at end of file diff --git a/src/test/resources/checkers/drugDomainCheckCorrect.json b/src/test/resources/checkers/drugDomainCheckCorrect.json index 42e15e84..44f2dfae 100644 --- a/src/test/resources/checkers/drugDomainCheckCorrect.json +++ b/src/test/resources/checkers/drugDomainCheckCorrect.json @@ -162,7 +162,11 @@ "CustomEra": { "DrugCodesetId": 2, "GapDays": 0, - "Offset": 0 + "GapUnit": "day", + "GapUnitValue": 0, + "Offset": 0, + "OffsetUnit": "day", + "OffsetUnitValue": 0 } }, "CensoringCriteria": [], diff --git a/src/test/resources/checkers/drugDomainCheckIncorrect.json b/src/test/resources/checkers/drugDomainCheckIncorrect.json index 4b7364a6..65259d56 100644 --- a/src/test/resources/checkers/drugDomainCheckIncorrect.json +++ b/src/test/resources/checkers/drugDomainCheckIncorrect.json @@ -162,7 +162,11 @@ "CustomEra": { "DrugCodesetId": 1, "GapDays": 0, - "Offset": 0 + "GapUnit": "day", + "GapUnitValue": 0, + "Offset": 0, + "OffsetUnit": "day", + "OffsetUnitValue": 0 } }, "CensoringCriteria": [], diff --git a/src/test/resources/checkers/emptyDemographicCheckCorrect.json b/src/test/resources/checkers/emptyDemographicCheckCorrect.json index 676d9e06..8d3cc58c 100644 --- a/src/test/resources/checkers/emptyDemographicCheckCorrect.json +++ b/src/test/resources/checkers/emptyDemographicCheckCorrect.json @@ -456,7 +456,11 @@ "CustomEra": { "DrugCodesetId": 0, "GapDays": 30, - "Offset": 0 + "GapUnit": "day", + "GapUnitValue": 30, + "Offset": 0, + "OffsetUnit": "day", + "OffsetUnitValue": 0 } }, "CensoringCriteria": [], diff --git a/src/test/resources/checkers/emptyDemographicCheckIncorrect.json b/src/test/resources/checkers/emptyDemographicCheckIncorrect.json index f22ae1b2..58428c18 100644 --- a/src/test/resources/checkers/emptyDemographicCheckIncorrect.json +++ b/src/test/resources/checkers/emptyDemographicCheckIncorrect.json @@ -452,7 +452,11 @@ "CustomEra": { "DrugCodesetId": 0, "GapDays": 30, - "Offset": 0 + "GapUnit": "day", + "GapUnitValue": 30, + "Offset": 0, + "OffsetUnit": "day", + "OffsetUnitValue": 0 } }, "CensoringCriteria": [], diff --git a/src/test/resources/checkers/eventsProgressionCheckCorrect.json b/src/test/resources/checkers/eventsProgressionCheckCorrect.json index 8d8e1118..5ebed29c 100644 --- a/src/test/resources/checkers/eventsProgressionCheckCorrect.json +++ b/src/test/resources/checkers/eventsProgressionCheckCorrect.json @@ -76,7 +76,11 @@ "CustomEra": { "DrugCodesetId": 2, "GapDays": 0, - "Offset": 0 + "GapUnit": "day", + "GapUnitValue": 0, + "Offset": 0, + "OffsetUnit": "day", + "OffsetUnitValue": 0 } }, "CensoringCriteria": [], diff --git a/src/test/resources/checkers/eventsProgressionCheckIncorrect.json b/src/test/resources/checkers/eventsProgressionCheckIncorrect.json index 4ed760a3..8cdbb5a2 100644 --- a/src/test/resources/checkers/eventsProgressionCheckIncorrect.json +++ b/src/test/resources/checkers/eventsProgressionCheckIncorrect.json @@ -76,7 +76,11 @@ "CustomEra": { "DrugCodesetId": 2, "GapDays": 0, - "Offset": 0 + "GapUnit": "day", + "GapUnitValue": 0, + "Offset": 0, + "OffsetUnit": "day", + "OffsetUnitValue": 0 } }, "CensoringCriteria": [], diff --git a/src/test/resources/cohortgeneration/allCriteria/allCriteriaExpressionWithTimeUnitInterval.json b/src/test/resources/cohortgeneration/allCriteria/allCriteriaExpressionWithTimeUnitInterval.json new file mode 100644 index 00000000..f2d7ce01 --- /dev/null +++ b/src/test/resources/cohortgeneration/allCriteria/allCriteriaExpressionWithTimeUnitInterval.json @@ -0,0 +1,2216 @@ +{ + "ConceptSets": [ + { + "id": 0, + "name": "Empty Concept Set", + "expression": { + "items": [] + } + } + ], + "PrimaryCriteria": { + "CriteriaList": [ + { + "ObservationPeriod": { + "CorrelatedCriteria": { + "Type": "ALL", + "CriteriaList": [ + { + "Criteria": { + "ConditionOccurrence": {} + }, + "StartWindow": { + "Start": { + "Coeff": -1 + }, + "End": { + "Coeff": 1 + }, + "UseEventEnd": false + }, + "RestrictVisit": true, + "IgnoreObservationPeriod": true, + "Occurrence": { + "Type": 2, + "Count": 1 + } + } + ], + "DemographicCriteriaList": [], + "Groups": [] + }, + "First": true, + "PeriodEndDate": { + "Value": "2099-01-01", + "Extent": "2999-01-01", + "Op": "bt" + }, + "PeriodType": [ + { + "CONCEPT_CODE": "OMOP4822290", + "CONCEPT_ID": 44814725, + "CONCEPT_NAME": "Period inferred by algorithm", + "DOMAIN_ID": "Type Concept", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Obs Period Type" + }, + { + "CONCEPT_CODE": "10021000000106", + "CONCEPT_ID": 44787739, + "CONCEPT_NAME": "FP34D drugs supplied payment form", + "DOMAIN_ID": "Type Concept", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "SNOMED" + } + ], + "UserDefinedPeriod": { + "StartDate": "1900-01-01", + "EndDate": "2060-01-01" + }, + "AgeAtStart": { + "Value": 0, + "Extent": 99, + "Op": "bt" + }, + "AgeAtEnd": { + "Value": 0, + "Extent": 99, + "Op": "bt" + }, + "PeriodLength": { + "Value": 0, + "Extent": 999, + "Op": "bt" + } + } + } + ], + "ObservationWindow": { + "PriorDays": 0, + "PostDays": 0 + }, + "PrimaryCriteriaLimit": { + "Type": "First" + } + }, + "QualifiedLimit": { + "Type": "First" + }, + "ExpressionLimit": { + "Type": "First" + }, + "InclusionRules": [ + { + "name": "Demographic Test", + "description": "Demographic Test", + "expression": { + "Type": "ALL", + "CriteriaList": [], + "DemographicCriteriaList": [ + { + "Age": { + "Value": 0, + "Extent": 99, + "Op": "bt" + }, + "Gender": [ + { + "CONCEPT_CODE": "M", + "CONCEPT_ID": 8507, + "CONCEPT_NAME": "MALE", + "DOMAIN_ID": "Gender", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Gender" + } + ], + "Race": [ + { + "CONCEPT_CODE": "2", + "CONCEPT_ID": 8515, + "CONCEPT_NAME": "Asian", + "DOMAIN_ID": "Race", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Race" + }, + { + "CONCEPT_CODE": "2.05", + "CONCEPT_ID": 38003578, + "CONCEPT_NAME": "Cambodian", + "DOMAIN_ID": "Race", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Race" + } + ], + "Ethnicity": [ + { + "CONCEPT_CODE": "Not Hispanic", + "CONCEPT_ID": 38003564, + "CONCEPT_NAME": "Not Hispanic or Latino", + "DOMAIN_ID": "Ethnicity", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Ethnicity" + } + ], + "OccurrenceStartDate": { + "Value": "1900-01-01", + "Extent": "2099-01-01", + "Op": "bt" + }, + "OccurrenceEndDate": { + "Value": "1900-01-01", + "Extent": "2099-01-01", + "Op": "bt" + } + } + ], + "Groups": [] + } + }, + { + "name": "Condition Test", + "description": "Condition Test", + "expression": { + "Type": "ALL", + "CriteriaList": [ + { + "Criteria": { + "ConditionOccurrence": { + "CorrelatedCriteria": { + "Type": "ALL", + "CriteriaList": [ + { + "Criteria": { + "ConditionOccurrence": {} + }, + "StartWindow": { + "Start": { + "Coeff": -1 + }, + "End": { + "Coeff": 1 + }, + "UseEventEnd": false + }, + "RestrictVisit": true, + "IgnoreObservationPeriod": true, + "Occurrence": { + "Type": 2, + "Count": 1 + } + } + ], + "DemographicCriteriaList": [], + "Groups": [] + }, + "CodesetId": 0, + "OccurrenceStartDate": { + "Value": "1900-01-01", + "Extent": "2099-01-01", + "Op": "bt" + }, + "OccurrenceEndDate": { + "Value": "1900-01-01", + "Extent": "2099-01-01", + "Op": "bt" + }, + "ConditionType": [ + { + "CONCEPT_CODE": "OMOP4822075", + "CONCEPT_ID": 38000199, + "CONCEPT_NAME": "Inpatient header - primary", + "DOMAIN_ID": "Type Concept", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Condition Type" + } + ], + "StopReason": { + "Text": "this is some test text", + "Op": "contains" + }, + "ConditionSourceConcept": 0, + "First": true, + "Age": { + "Value": 0, + "Extent": 99, + "Op": "bt" + }, + "Gender": [ + { + "CONCEPT_CODE": "M", + "CONCEPT_ID": 8507, + "CONCEPT_NAME": "MALE", + "DOMAIN_ID": "Gender", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Gender" + } + ], + "ProviderSpecialty": [ + { + "CONCEPT_CODE": "0396.00", + "CONCEPT_ID": 45444755, + "CONCEPT_NAME": "Psychologist", + "DOMAIN_ID": "Provider", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Read" + }, + { + "CONCEPT_CODE": "03D5.00", + "CONCEPT_ID": 45418395, + "CONCEPT_NAME": "Consultant surgeon", + "DOMAIN_ID": "Provider", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Read" + } + ], + "VisitType": [ + { + "CONCEPT_CODE": "IP", + "CONCEPT_ID": 9201, + "CONCEPT_NAME": "Inpatient Visit", + "DOMAIN_ID": "Visit", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Visit" + } + ] + } + }, + "StartWindow": { + "Start": { + "Days": 30, + "Coeff": -1 + }, + "End": { + "Days": 30, + "Coeff": 1 + }, + "UseEventEnd": false + }, + "Occurrence": { + "Type": 2, + "Count": 1 + } + } + ], + "DemographicCriteriaList": [], + "Groups": [] + } + }, + { + "name": "Condition Era Test", + "description": "Condition Era Test", + "expression": { + "Type": "ALL", + "CriteriaList": [ + { + "Criteria": { + "ConditionEra": { + "CorrelatedCriteria": { + "Type": "ALL", + "CriteriaList": [ + { + "Criteria": { + "ConditionEra": {} + }, + "StartWindow": { + "Start": { + "Coeff": -1 + }, + "End": { + "Coeff": 1 + }, + "UseEventEnd": false + }, + "IgnoreObservationPeriod": true, + "Occurrence": { + "Type": 2, + "Count": 1 + } + } + ], + "DemographicCriteriaList": [], + "Groups": [] + }, + "CodesetId": 0, + "EraStartDate": { + "Value": "1900-01-01", + "Extent": "2099-01-01", + "Op": "bt" + }, + "EraEndDate": { + "Value": "1900-01-01", + "Extent": "2099-01-01", + "Op": "bt" + }, + "OccurrenceCount": { + "Value": 0, + "Extent": 99, + "Op": "bt" + }, + "EraLength": { + "Value": 0, + "Extent": 99, + "Op": "bt" + }, + "First": true, + "AgeAtStart": { + "Value": 0, + "Extent": 99, + "Op": "bt" + }, + "AgeAtEnd": { + "Value": 0, + "Extent": 99, + "Op": "bt" + }, + "Gender": [ + { + "CONCEPT_CODE": "M", + "CONCEPT_ID": 8507, + "CONCEPT_NAME": "MALE", + "DOMAIN_ID": "Gender", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Gender" + } + ] + } + }, + "StartWindow": { + "Start": { + "Days": 30, + "Coeff": -1 + }, + "End": { + "Days": 30, + "Coeff": 1 + }, + "UseEventEnd": false + }, + "Occurrence": { + "Type": 2, + "Count": 1 + } + } + ], + "DemographicCriteriaList": [], + "Groups": [] + } + }, + { + "name": "Death Test", + "description": "Death Test", + "expression": { + "Type": "ALL", + "CriteriaList": [ + { + "Criteria": { + "Death": { + "CorrelatedCriteria": { + "Type": "ALL", + "CriteriaList": [ + { + "Criteria": { + "Death": { + "CodesetId": 0 + } + }, + "StartWindow": { + "Start": { + "Coeff": -1 + }, + "End": { + "Coeff": 1 + }, + "UseEventEnd": false + }, + "IgnoreObservationPeriod": true, + "Occurrence": { + "Type": 2, + "Count": 1 + } + } + ], + "DemographicCriteriaList": [], + "Groups": [] + }, + "CodesetId": 0, + "OccurrenceStartDate": { + "Value": "1900-01-01", + "Extent": "2099-01-01", + "Op": "bt" + }, + "DeathType": [ + { + "CONCEPT_CODE": "OMOP4822044", + "CONCEPT_ID": 256, + "CONCEPT_NAME": "EHR Record underlying cause of death", + "DOMAIN_ID": "Type Concept", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Condition Type" + }, + { + "CONCEPT_CODE": "OMOP4822045", + "CONCEPT_ID": 255, + "CONCEPT_NAME": "EHR Record contributory cause of death", + "DOMAIN_ID": "Type Concept", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Condition Type" + }, + { + "CONCEPT_CODE": "OMOP4822046", + "CONCEPT_ID": 254, + "CONCEPT_NAME": "EHR Record immediate cause of death", + "DOMAIN_ID": "Type Concept", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Condition Type" + } + ], + "Age": { + "Value": 0, + "Extent": 99, + "Op": "bt" + }, + "Gender": [ + { + "CONCEPT_CODE": "F", + "CONCEPT_ID": 8532, + "CONCEPT_NAME": "FEMALE", + "DOMAIN_ID": "Gender", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Gender" + }, + { + "CONCEPT_CODE": "M", + "CONCEPT_ID": 8507, + "CONCEPT_NAME": "MALE", + "DOMAIN_ID": "Gender", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Gender" + } + ] + } + }, + "StartWindow": { + "Start": { + "Days": 30, + "Coeff": -1 + }, + "End": { + "Days": 30, + "Coeff": 1 + }, + "UseEventEnd": false + }, + "Occurrence": { + "Type": 2, + "Count": 1 + } + } + ], + "DemographicCriteriaList": [], + "Groups": [] + } + }, + { + "name": "Device Exposure Test", + "description": "Device Exposure Test", + "expression": { + "Type": "ALL", + "CriteriaList": [ + { + "Criteria": { + "DeviceExposure": { + "CorrelatedCriteria": { + "Type": "ALL", + "CriteriaList": [ + { + "Criteria": { + "DeviceExposure": { + "CodesetId": 0 + } + }, + "StartWindow": { + "Start": { + "Coeff": -1 + }, + "End": { + "Coeff": 1 + }, + "UseEventEnd": false + }, + "RestrictVisit": true, + "IgnoreObservationPeriod": true, + "Occurrence": { + "Type": 2, + "Count": 1 + } + } + ], + "DemographicCriteriaList": [], + "Groups": [] + }, + "CodesetId": 0, + "OccurrenceStartDate": { + "Value": "1900-01-01", + "Extent": "2099-01-01", + "Op": "bt" + }, + "OccurrenceEndDate": { + "Value": "1900-01-01", + "Extent": "2099-01-01", + "Op": "bt" + }, + "DeviceType": [ + { + "CONCEPT_CODE": "OMOP4822234", + "CONCEPT_ID": 32465, + "CONCEPT_NAME": "Inferred from claim", + "DOMAIN_ID": "Type Concept", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Device Type" + }, + { + "CONCEPT_CODE": "OMOP4822236", + "CONCEPT_ID": 44818706, + "CONCEPT_NAME": "Patient reported device", + "DOMAIN_ID": "Type Concept", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Device Type" + } + ], + "UniqueDeviceId": { + "Op": "contains" + }, + "Quantity": { + "Value": 0, + "Extent": 99, + "Op": "bt" + }, + "DeviceSourceConcept": 0, + "First": true, + "Age": { + "Value": 0, + "Extent": 99, + "Op": "bt" + }, + "Gender": [ + { + "CONCEPT_CODE": "F", + "CONCEPT_ID": 8532, + "CONCEPT_NAME": "FEMALE", + "DOMAIN_ID": "Gender", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Gender" + }, + { + "CONCEPT_CODE": "M", + "CONCEPT_ID": 8507, + "CONCEPT_NAME": "MALE", + "DOMAIN_ID": "Gender", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Gender" + } + ], + "ProviderSpecialty": [ + { + "CONCEPT_CODE": "10", + "CONCEPT_ID": 38004455, + "CONCEPT_NAME": "Gastroenterology", + "DOMAIN_ID": "Provider", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Medicare Specialty" + }, + { + "CONCEPT_CODE": "101YM0800X", + "CONCEPT_ID": 38003625, + "CONCEPT_NAME": "Mental Health Counselor", + "DOMAIN_ID": "Provider", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "NUCC" + }, + { + "CONCEPT_CODE": "101YP1600X", + "CONCEPT_ID": 38003626, + "CONCEPT_NAME": "Pastoral Counselor", + "DOMAIN_ID": "Provider", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "NUCC" + } + ], + "VisitType": [ + { + "CONCEPT_CODE": "21", + "CONCEPT_ID": 8717, + "CONCEPT_NAME": "Inpatient Hospital", + "DOMAIN_ID": "Visit", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "CMS Place of Service" + }, + { + "CONCEPT_CODE": "22", + "CONCEPT_ID": 8756, + "CONCEPT_NAME": "Outpatient Hospital", + "DOMAIN_ID": "Visit", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "CMS Place of Service" + } + ] + } + }, + "StartWindow": { + "Start": { + "Days": 30, + "Coeff": -1 + }, + "End": { + "Days": 30, + "Coeff": 1 + }, + "UseEventEnd": false + }, + "Occurrence": { + "Type": 2, + "Count": 1 + } + } + ], + "DemographicCriteriaList": [], + "Groups": [] + } + }, + { + "name": "Drug Era Test", + "description": "Drug Era Test", + "expression": { + "Type": "ALL", + "CriteriaList": [ + { + "Criteria": { + "DrugEra": { + "CorrelatedCriteria": { + "Type": "ALL", + "CriteriaList": [ + { + "Criteria": { + "DrugEra": { + "CodesetId": 0 + } + }, + "StartWindow": { + "Start": { + "Coeff": -1 + }, + "End": { + "Coeff": 1 + }, + "UseEventEnd": false + }, + "IgnoreObservationPeriod": true, + "Occurrence": { + "Type": 2, + "Count": 1 + } + } + ], + "DemographicCriteriaList": [], + "Groups": [] + }, + "EraStartDate": { + "Value": "1900-01-01", + "Extent": "2099-01-01", + "Op": "bt" + }, + "EraEndDate": { + "Value": "1900-01-01", + "Extent": "2099-01-01", + "Op": "bt" + }, + "OccurrenceCount": { + "Value": 0, + "Extent": 99, + "Op": "bt" + }, + "EraLength": { + "Value": 0, + "Extent": 99, + "Op": "bt" + }, + "First": true, + "AgeAtStart": { + "Value": 0, + "Extent": 99, + "Op": "bt" + }, + "AgeAtEnd": { + "Value": 0, + "Extent": 99, + "Op": "bt" + }, + "Gender": [ + { + "CONCEPT_CODE": "F", + "CONCEPT_ID": 8532, + "CONCEPT_NAME": "FEMALE", + "DOMAIN_ID": "Gender", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Gender" + }, + { + "CONCEPT_CODE": "M", + "CONCEPT_ID": 8507, + "CONCEPT_NAME": "MALE", + "DOMAIN_ID": "Gender", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Gender" + } + ] + } + }, + "StartWindow": { + "Start": { + "Days": 30, + "Coeff": -1 + }, + "End": { + "Days": 30, + "Coeff": 1 + }, + "UseEventEnd": false + }, + "Occurrence": { + "Type": 2, + "Count": 1 + } + } + ], + "DemographicCriteriaList": [], + "Groups": [] + } + }, + { + "name": "Drug Exposure Test", + "description": "Drug Exposure Test", + "expression": { + "Type": "ALL", + "CriteriaList": [ + { + "Criteria": { + "DrugExposure": { + "CorrelatedCriteria": { + "Type": "ALL", + "CriteriaList": [ + { + "Criteria": { + "DrugExposure": {} + }, + "StartWindow": { + "Start": { + "Coeff": -1 + }, + "End": { + "Coeff": 1 + }, + "UseEventEnd": false + }, + "RestrictVisit": true, + "IgnoreObservationPeriod": true, + "Occurrence": { + "Type": 2, + "Count": 1 + } + } + ], + "DemographicCriteriaList": [], + "Groups": [] + }, + "CodesetId": 0, + "OccurrenceStartDate": { + "Value": "1900-01-01", + "Extent": "2099-01-01", + "Op": "bt" + }, + "OccurrenceEndDate": { + "Value": "1900-01-01", + "Extent": "2099-01-01", + "Op": "bt" + }, + "DrugType": [ + { + "CONCEPT_CODE": "OMOP4822239", + "CONCEPT_ID": 38000175, + "CONCEPT_NAME": "Prescription dispensed in pharmacy", + "DOMAIN_ID": "Type Concept", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Drug Type" + }, + { + "CONCEPT_CODE": "OMOP4822240", + "CONCEPT_ID": 38000176, + "CONCEPT_NAME": "Prescription dispensed through mail order", + "DOMAIN_ID": "Type Concept", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Drug Type" + } + ], + "StopReason": { + "Text": "stop reason", + "Op": "contains" + }, + "Refills": { + "Value": 0, + "Extent": 99, + "Op": "bt" + }, + "Quantity": { + "Value": 0, + "Extent": 99, + "Op": "bt" + }, + "DaysSupply": { + "Value": 0, + "Extent": 99, + "Op": "bt" + }, + "RouteConcept": [ + { + "CONCEPT_CODE": "6064005", + "CONCEPT_ID": 4263689, + "CONCEPT_NAME": "Topical route", + "DOMAIN_ID": "Route", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "SNOMED" + }, + { + "CONCEPT_CODE": "10547007", + "CONCEPT_ID": 4023156, + "CONCEPT_NAME": "Auricular", + "DOMAIN_ID": "Route", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "SNOMED" + }, + { + "CONCEPT_CODE": "12130007", + "CONCEPT_ID": 4006860, + "CONCEPT_NAME": "Intraarticular", + "DOMAIN_ID": "Route", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "SNOMED" + } + ], + "EffectiveDrugDose": { + "Value": 0, + "Extent": 99, + "Op": "bt" + }, + "DoseUnit": [ + { + "CONCEPT_CODE": "%", + "CONCEPT_ID": 8554, + "CONCEPT_NAME": "percent", + "DOMAIN_ID": "Unit", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "UCUM" + }, + { + "CONCEPT_CODE": "%(enzyme'activity}", + "CONCEPT_ID": 8849, + "CONCEPT_NAME": "percent enzyme activity", + "DOMAIN_ID": "Unit", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "UCUM" + }, + { + "CONCEPT_CODE": "%[slope]", + "CONCEPT_ID": 9214, + "CONCEPT_NAME": "percent slope", + "DOMAIN_ID": "Unit", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "UCUM" + } + ], + "LotNumber": { + "Text": "lot number", + "Op": "contains" + }, + "DrugSourceConcept": 0, + "First": true, + "Age": { + "Value": 0, + "Extent": 99, + "Op": "bt" + }, + "Gender": [ + { + "CONCEPT_CODE": "F", + "CONCEPT_ID": 8532, + "CONCEPT_NAME": "FEMALE", + "DOMAIN_ID": "Gender", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Gender" + }, + { + "CONCEPT_CODE": "M", + "CONCEPT_ID": 8507, + "CONCEPT_NAME": "MALE", + "DOMAIN_ID": "Gender", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Gender" + } + ], + "ProviderSpecialty": [ + { + "CONCEPT_CODE": "1", + "CONCEPT_ID": 38004446, + "CONCEPT_NAME": "General Practice", + "DOMAIN_ID": "Provider", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Medicare Specialty" + }, + { + "CONCEPT_CODE": "101YM0800X", + "CONCEPT_ID": 38003625, + "CONCEPT_NAME": "Mental Health Counselor", + "DOMAIN_ID": "Provider", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "NUCC" + } + ], + "VisitType": [ + { + "CONCEPT_CODE": "ERIP", + "CONCEPT_ID": 262, + "CONCEPT_NAME": "Emergency Room and Inpatient Visit", + "DOMAIN_ID": "Visit", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Visit" + }, + { + "CONCEPT_CODE": "IP", + "CONCEPT_ID": 9201, + "CONCEPT_NAME": "Inpatient Visit", + "DOMAIN_ID": "Visit", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Visit" + } + ] + } + }, + "StartWindow": { + "Start": { + "Days": 30, + "Coeff": -1 + }, + "End": { + "Days": 30, + "Coeff": 1 + }, + "UseEventEnd": false + }, + "Occurrence": { + "Type": 2, + "Count": 1 + } + } + ], + "DemographicCriteriaList": [], + "Groups": [] + } + }, + { + "name": "Measurement Test", + "description": "Measurement Test", + "expression": { + "Type": "ALL", + "CriteriaList": [ + { + "Criteria": { + "Measurement": { + "CorrelatedCriteria": { + "Type": "ALL", + "CriteriaList": [ + { + "Criteria": { + "Measurement": { + "CodesetId": 0 + } + }, + "StartWindow": { + "Start": { + "Coeff": -1 + }, + "End": { + "Coeff": 1 + }, + "UseEventEnd": false + }, + "RestrictVisit": true, + "IgnoreObservationPeriod": true, + "Occurrence": { + "Type": 2, + "Count": 1 + } + } + ], + "DemographicCriteriaList": [], + "Groups": [] + }, + "First": true, + "OccurrenceStartDate": { + "Value": "1900-01-01", + "Extent": "2099-01-01", + "Op": "bt" + }, + "MeasurementType": [ + { + "CONCEPT_CODE": "OMOP4822059", + "CONCEPT_ID": 38000183, + "CONCEPT_NAME": "Inpatient detail - primary", + "DOMAIN_ID": "Type Concept", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Condition Type" + }, + { + "CONCEPT_CODE": "OMOP4822075", + "CONCEPT_ID": 38000199, + "CONCEPT_NAME": "Inpatient header - primary", + "DOMAIN_ID": "Type Concept", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Condition Type" + } + ], + "Operator": [ + { + "CONCEPT_CODE": "276140008", + "CONCEPT_ID": 4172704, + "CONCEPT_NAME": ">", + "DOMAIN_ID": "Meas Value Operator", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "SNOMED" + } + ], + "ValueAsNumber": { + "Value": 0, + "Extent": 99, + "Op": "bt" + }, + "ValueAsConcept": [ + { + "CONCEPT_CODE": "10828004", + "CONCEPT_ID": 9191, + "CONCEPT_NAME": "Positive", + "DOMAIN_ID": "Meas Value", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "SNOMED" + } + ], + "Unit": [ + { + "CONCEPT_CODE": "/mg", + "CONCEPT_ID": 9256, + "CONCEPT_NAME": "per milligram", + "DOMAIN_ID": "Unit", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "UCUM" + } + ], + "RangeLow": { + "Value": 0, + "Extent": 99, + "Op": "bt" + }, + "RangeHigh": { + "Value": 0, + "Extent": 99, + "Op": "bt" + }, + "MeasurementSourceConcept": 0, + "RangeLowRatio": { + "Value": 0, + "Extent": 99, + "Op": "bt" + }, + "RangeHighRatio": { + "Value": 0, + "Extent": 99, + "Op": "bt" + }, + "Abnormal": true, + "Age": { + "Value": 0, + "Extent": 99, + "Op": "bt" + }, + "Gender": [ + { + "CONCEPT_CODE": "F", + "CONCEPT_ID": 8532, + "CONCEPT_NAME": "FEMALE", + "DOMAIN_ID": "Gender", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Gender" + }, + { + "CONCEPT_CODE": "M", + "CONCEPT_ID": 8507, + "CONCEPT_NAME": "MALE", + "DOMAIN_ID": "Gender", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Gender" + } + ], + "ProviderSpecialty": [ + { + "CONCEPT_CODE": "1", + "CONCEPT_ID": 38004446, + "CONCEPT_NAME": "General Practice", + "DOMAIN_ID": "Provider", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Medicare Specialty" + }, + { + "CONCEPT_CODE": "101YP1600X", + "CONCEPT_ID": 38003626, + "CONCEPT_NAME": "Pastoral Counselor", + "DOMAIN_ID": "Provider", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "NUCC" + }, + { + "CONCEPT_CODE": "101YP2500X", + "CONCEPT_ID": 38003627, + "CONCEPT_NAME": "Professional Counselor", + "DOMAIN_ID": "Provider", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "NUCC" + } + ], + "VisitType": [ + { + "CONCEPT_CODE": "ERIP", + "CONCEPT_ID": 262, + "CONCEPT_NAME": "Emergency Room and Inpatient Visit", + "DOMAIN_ID": "Visit", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Visit" + }, + { + "CONCEPT_CODE": "IP", + "CONCEPT_ID": 9201, + "CONCEPT_NAME": "Inpatient Visit", + "DOMAIN_ID": "Visit", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Visit" + } + ] + } + }, + "StartWindow": { + "Start": { + "Days": 30, + "Coeff": -1 + }, + "End": { + "Days": 30, + "Coeff": 1 + }, + "UseEventEnd": false + }, + "Occurrence": { + "Type": 2, + "Count": 1 + } + } + ], + "DemographicCriteriaList": [], + "Groups": [] + } + }, + { + "name": "Observation Test", + "description": "Observation Test", + "expression": { + "Type": "ALL", + "CriteriaList": [ + { + "Criteria": { + "Observation": { + "CorrelatedCriteria": { + "Type": "ALL", + "CriteriaList": [ + { + "Criteria": { + "Observation": { + "CodesetId": 0 + } + }, + "StartWindow": { + "Start": { + "Coeff": -1 + }, + "End": { + "Coeff": 1 + }, + "UseEventEnd": false + }, + "RestrictVisit": true, + "Occurrence": { + "Type": 2, + "Count": 1 + } + } + ], + "DemographicCriteriaList": [], + "Groups": [] + }, + "CodesetId": 0, + "OccurrenceStartDate": { + "Value": "1900-01-01", + "Extent": "2099-01-01", + "Op": "bt" + }, + "ObservationType": [ + { + "CONCEPT_CODE": "OMOP4822059", + "CONCEPT_ID": 38000183, + "CONCEPT_NAME": "Inpatient detail - primary", + "DOMAIN_ID": "Type Concept", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Condition Type" + }, + { + "CONCEPT_CODE": "OMOP4822075", + "CONCEPT_ID": 38000199, + "CONCEPT_NAME": "Inpatient header - primary", + "DOMAIN_ID": "Type Concept", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Condition Type" + } + ], + "ValueAsNumber": { + "Value": 0, + "Extent": 99, + "Op": "bt" + }, + "ValueAsString": { + "Text": "some value", + "Op": "contains" + }, + "ValueAsConcept": [ + { + "CONCEPT_CODE": "10000005", + "CONCEPT_ID": 36312355, + "CONCEPT_NAME": "17 ketosteroids urine", + "DOMAIN_ID": "Measurement", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "MedDRA" + } + ], + "Qualifier": [ + { + "CONCEPT_CODE": "276136004", + "CONCEPT_ID": 4172703, + "CONCEPT_NAME": "=", + "DOMAIN_ID": "Meas Value Operator", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "SNOMED" + }, + { + "CONCEPT_CODE": "276137008", + "CONCEPT_ID": 4171754, + "CONCEPT_NAME": "<=", + "DOMAIN_ID": "Meas Value Operator", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "SNOMED" + } + ], + "Unit": [ + { + "CONCEPT_CODE": "%", + "CONCEPT_ID": 8554, + "CONCEPT_NAME": "percent", + "DOMAIN_ID": "Unit", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "UCUM" + } + ], + "ObservationSourceConcept": 0, + "First": true, + "Age": { + "Value": 0, + "Extent": 99, + "Op": "bt" + }, + "Gender": [ + { + "CONCEPT_CODE": "F", + "CONCEPT_ID": 8532, + "CONCEPT_NAME": "FEMALE", + "DOMAIN_ID": "Gender", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Gender" + }, + { + "CONCEPT_CODE": "M", + "CONCEPT_ID": 8507, + "CONCEPT_NAME": "MALE", + "DOMAIN_ID": "Gender", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Gender" + } + ], + "ProviderSpecialty": [ + { + "CONCEPT_CODE": "1", + "CONCEPT_ID": 38004446, + "CONCEPT_NAME": "General Practice", + "DOMAIN_ID": "Provider", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Medicare Specialty" + }, + { + "CONCEPT_CODE": "101YP2500X", + "CONCEPT_ID": 38003627, + "CONCEPT_NAME": "Professional Counselor", + "DOMAIN_ID": "Provider", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "NUCC" + } + ], + "VisitType": [ + { + "CONCEPT_CODE": "ERIP", + "CONCEPT_ID": 262, + "CONCEPT_NAME": "Emergency Room and Inpatient Visit", + "DOMAIN_ID": "Visit", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Visit" + }, + { + "CONCEPT_CODE": "IP", + "CONCEPT_ID": 9201, + "CONCEPT_NAME": "Inpatient Visit", + "DOMAIN_ID": "Visit", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Visit" + } + ] + } + }, + "StartWindow": { + "Start": { + "Days": 30, + "Coeff": -1 + }, + "End": { + "Days": 30, + "Coeff": 1 + }, + "UseEventEnd": false + }, + "Occurrence": { + "Type": 2, + "Count": 1 + } + } + ], + "DemographicCriteriaList": [], + "Groups": [] + } + }, + { + "name": "Observation Period Test", + "description": "Observation Period Test", + "expression": { + "Type": "ALL", + "CriteriaList": [ + { + "Criteria": { + "ObservationPeriod": { + "CorrelatedCriteria": { + "Type": "ALL", + "CriteriaList": [ + { + "Criteria": { + "ObservationPeriod": {} + }, + "StartWindow": { + "Start": { + "Coeff": -1 + }, + "End": { + "Coeff": 1 + }, + "UseEventEnd": false + }, + "IgnoreObservationPeriod": true, + "Occurrence": { + "Type": 2, + "Count": 1 + } + } + ], + "DemographicCriteriaList": [], + "Groups": [] + }, + "First": true, + "PeriodStartDate": { + "Value": "1900-01-01", + "Extent": "2099-01-01", + "Op": "bt" + }, + "PeriodEndDate": { + "Value": "1900-01-01", + "Extent": "2099-01-01", + "Op": "bt" + }, + "PeriodType": [ + { + "CONCEPT_CODE": "OMOP4822059", + "CONCEPT_ID": 38000183, + "CONCEPT_NAME": "Inpatient detail - primary", + "DOMAIN_ID": "Type Concept", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Condition Type" + }, + { + "CONCEPT_CODE": "OMOP4822075", + "CONCEPT_ID": 38000199, + "CONCEPT_NAME": "Inpatient header - primary", + "DOMAIN_ID": "Type Concept", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Condition Type" + } + ], + "UserDefinedPeriod": { + "StartDate": "1900-01-01", + "EndDate": "2099-01-01" + }, + "AgeAtStart": { + "Value": 0, + "Extent": 99, + "Op": "bt" + }, + "AgeAtEnd": { + "Value": 0, + "Extent": 99, + "Op": "bt" + }, + "PeriodLength": { + "Value": 0, + "Extent": 99, + "Op": "bt" + } + } + }, + "StartWindow": { + "Start": { + "Days": 30, + "Coeff": -1 + }, + "End": { + "Days": 30, + "Coeff": 1 + }, + "UseEventEnd": false + }, + "Occurrence": { + "Type": 2, + "Count": 1 + } + } + ], + "DemographicCriteriaList": [], + "Groups": [] + } + }, + { + "name": "Procedure Occurrence Test", + "description": "Procedure Occurrence Test", + "expression": { + "Type": "ALL", + "CriteriaList": [ + { + "Criteria": { + "ProcedureOccurrence": { + "CorrelatedCriteria": { + "Type": "ALL", + "CriteriaList": [ + { + "Criteria": { + "ProcedureOccurrence": { + "CodesetId": 0 + } + }, + "StartWindow": { + "Start": { + "Coeff": -1 + }, + "End": { + "Coeff": 1 + }, + "UseEventEnd": false + }, + "RestrictVisit": true, + "IgnoreObservationPeriod": true, + "Occurrence": { + "Type": 2, + "Count": 1 + } + } + ], + "DemographicCriteriaList": [], + "Groups": [] + }, + "CodesetId": 0, + "OccurrenceStartDate": { + "Value": "1900-01-01", + "Extent": "2099-01-01", + "Op": "bt" + }, + "ProcedureType": [ + { + "CONCEPT_CODE": "OMOP4822059", + "CONCEPT_ID": 38000183, + "CONCEPT_NAME": "Inpatient detail - primary", + "DOMAIN_ID": "Type Concept", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Condition Type" + }, + { + "CONCEPT_CODE": "OMOP4822075", + "CONCEPT_ID": 38000199, + "CONCEPT_NAME": "Inpatient header - primary", + "DOMAIN_ID": "Type Concept", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Condition Type" + } + ], + "Modifier": [ + { + "CONCEPT_CODE": "10013000", + "CONCEPT_ID": 4000804, + "CONCEPT_NAME": "Lateral meniscus structure", + "DOMAIN_ID": "Spec Anatomic Site", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "SNOMED" + }, + { + "CONCEPT_CODE": "10024003", + "CONCEPT_ID": 4001771, + "CONCEPT_NAME": "Structure of base of lung", + "DOMAIN_ID": "Spec Anatomic Site", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "SNOMED" + } + ], + "Quantity": { + "Value": 0, + "Extent": 99, + "Op": "bt" + }, + "First": true, + "Age": { + "Value": 0, + "Extent": 99, + "Op": "bt" + }, + "Gender": [ + { + "CONCEPT_CODE": "F", + "CONCEPT_ID": 8532, + "CONCEPT_NAME": "FEMALE", + "DOMAIN_ID": "Gender", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Gender" + }, + { + "CONCEPT_CODE": "M", + "CONCEPT_ID": 8507, + "CONCEPT_NAME": "MALE", + "DOMAIN_ID": "Gender", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Gender" + } + ], + "ProviderSpecialty": [ + { + "CONCEPT_CODE": "1", + "CONCEPT_ID": 38004446, + "CONCEPT_NAME": "General Practice", + "DOMAIN_ID": "Provider", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Medicare Specialty" + }, + { + "CONCEPT_CODE": "101YP2500X", + "CONCEPT_ID": 38003627, + "CONCEPT_NAME": "Professional Counselor", + "DOMAIN_ID": "Provider", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "NUCC" + } + ], + "VisitType": [ + { + "CONCEPT_CODE": "ERIP", + "CONCEPT_ID": 262, + "CONCEPT_NAME": "Emergency Room and Inpatient Visit", + "DOMAIN_ID": "Visit", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Visit" + }, + { + "CONCEPT_CODE": "IP", + "CONCEPT_ID": 9201, + "CONCEPT_NAME": "Inpatient Visit", + "DOMAIN_ID": "Visit", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Visit" + } + ] + } + }, + "StartWindow": { + "Start": { + "Days": 30, + "Coeff": -1 + }, + "End": { + "Days": 30, + "Coeff": 1 + }, + "UseEventEnd": false + }, + "Occurrence": { + "Type": 2, + "Count": 1 + } + } + ], + "DemographicCriteriaList": [], + "Groups": [] + } + }, + { + "name": "Specimen Test", + "description": "Specimen Test", + "expression": { + "Type": "ALL", + "CriteriaList": [ + { + "Criteria": { + "Specimen": { + "CorrelatedCriteria": { + "Type": "ALL", + "CriteriaList": [ + { + "Criteria": { + "Specimen": { + "CodesetId": 0 + } + }, + "StartWindow": { + "Start": { + "Coeff": -1 + }, + "End": { + "Coeff": 1 + }, + "UseEventEnd": false + }, + "IgnoreObservationPeriod": true, + "Occurrence": { + "Type": 2, + "Count": 1 + } + } + ], + "DemographicCriteriaList": [], + "Groups": [] + }, + "CodesetId": 0, + "OccurrenceStartDate": { + "Value": "1900-01-01", + "Extent": "2099-01-01", + "Op": "bt" + }, + "SpecimenType": [ + { + "CONCEPT_CODE": "OMOP4822059", + "CONCEPT_ID": 38000183, + "CONCEPT_NAME": "Inpatient detail - primary", + "DOMAIN_ID": "Type Concept", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Condition Type" + }, + { + "CONCEPT_CODE": "OMOP4822075", + "CONCEPT_ID": 38000199, + "CONCEPT_NAME": "Inpatient header - primary", + "DOMAIN_ID": "Type Concept", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Condition Type" + } + ], + "Quantity": { + "Value": 0, + "Extent": 99, + "Op": "bt" + }, + "Unit": [ + { + "CONCEPT_CODE": "%{baseline}", + "CONCEPT_ID": 8688, + "CONCEPT_NAME": "percent baseline", + "DOMAIN_ID": "Unit", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "UCUM" + }, + { + "CONCEPT_CODE": "%{binding}", + "CONCEPT_ID": 9218, + "CONCEPT_NAME": "percent binding", + "DOMAIN_ID": "Unit", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "UCUM" + } + ], + "AnatomicSite": [ + { + "CONCEPT_CODE": "10036009", + "CONCEPT_ID": 4002601, + "CONCEPT_NAME": "Structure of nucleus pulposus of intervertebral disc of eighth thoracic vertebra", + "DOMAIN_ID": "Spec Anatomic Site", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "SNOMED" + }, + { + "CONCEPT_CODE": "10042008", + "CONCEPT_ID": 4001422, + "CONCEPT_NAME": "Structure of intervertebral foramen of fifth thoracic vertebra", + "DOMAIN_ID": "Spec Anatomic Site", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "SNOMED" + }, + { + "CONCEPT_CODE": "10047002", + "CONCEPT_ID": 4001433, + "CONCEPT_NAME": "Structure of transplanted lung", + "DOMAIN_ID": "Spec Anatomic Site", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "SNOMED" + } + ], + "DiseaseStatus": [ + { + "CONCEPT_CODE": "21594007", + "CONCEPT_ID": 4066212, + "CONCEPT_NAME": "Malignant", + "DOMAIN_ID": "Spec Disease Status", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "SNOMED" + } + ], + "SourceId": { + "Text": "some sourceID", + "Op": "contains" + }, + "First": true, + "Age": { + "Value": 0, + "Extent": 99, + "Op": "bt" + }, + "Gender": [ + { + "CONCEPT_CODE": "F", + "CONCEPT_ID": 8532, + "CONCEPT_NAME": "FEMALE", + "DOMAIN_ID": "Gender", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Gender" + }, + { + "CONCEPT_CODE": "M", + "CONCEPT_ID": 8507, + "CONCEPT_NAME": "MALE", + "DOMAIN_ID": "Gender", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Gender" + } + ] + } + }, + "StartWindow": { + "Start": { + "Days": 30, + "Coeff": -1 + }, + "End": { + "Days": 30, + "Coeff": 1 + }, + "UseEventEnd": false + }, + "Occurrence": { + "Type": 2, + "Count": 1 + } + } + ], + "DemographicCriteriaList": [], + "Groups": [] + } + }, + { + "name": "Visit Test", + "description": "Visit Test", + "expression": { + "Type": "ALL", + "CriteriaList": [ + { + "Criteria": { + "VisitOccurrence": { + "CorrelatedCriteria": { + "Type": "ALL", + "CriteriaList": [ + { + "Criteria": { + "VisitOccurrence": {} + }, + "StartWindow": { + "Start": { + "Coeff": -1 + }, + "End": { + "Coeff": 1 + }, + "UseEventEnd": false + }, + "RestrictVisit": true, + "IgnoreObservationPeriod": true, + "Occurrence": { + "Type": 2, + "Count": 1 + } + } + ], + "DemographicCriteriaList": [], + "Groups": [] + }, + "CodesetId": 0, + "OccurrenceStartDate": { + "Value": "1900-01-01", + "Extent": "2099-01-01", + "Op": "bt" + }, + "OccurrenceEndDate": { + "Value": "1900-01-01", + "Extent": "2099-01-01", + "Op": "bt" + }, + "VisitType": [ + { + "CONCEPT_CODE": "OMOP4822059", + "CONCEPT_ID": 38000183, + "CONCEPT_NAME": "Inpatient detail - primary", + "DOMAIN_ID": "Type Concept", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Condition Type" + }, + { + "CONCEPT_CODE": "OMOP4822075", + "CONCEPT_ID": 38000199, + "CONCEPT_NAME": "Inpatient header - primary", + "DOMAIN_ID": "Type Concept", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Condition Type" + } + ], + "VisitSourceConcept": 0, + "VisitLength": { + "Value": 0, + "Extent": 99, + "Op": "bt" + }, + "First": true, + "Age": { + "Value": 0, + "Extent": 99, + "Op": "bt" + }, + "Gender": [ + { + "CONCEPT_CODE": "F", + "CONCEPT_ID": 8532, + "CONCEPT_NAME": "FEMALE", + "DOMAIN_ID": "Gender", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Gender" + }, + { + "CONCEPT_CODE": "M", + "CONCEPT_ID": 8507, + "CONCEPT_NAME": "MALE", + "DOMAIN_ID": "Gender", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Gender" + } + ], + "ProviderSpecialty": [ + { + "CONCEPT_CODE": "1", + "CONCEPT_ID": 38004446, + "CONCEPT_NAME": "General Practice", + "DOMAIN_ID": "Provider", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "Medicare Specialty" + }, + { + "CONCEPT_CODE": "101YP2500X", + "CONCEPT_ID": 38003627, + "CONCEPT_NAME": "Professional Counselor", + "DOMAIN_ID": "Provider", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "NUCC" + } + ], + "PlaceOfService": [ + { + "CONCEPT_CODE": "1021864", + "CONCEPT_ID": 42628591, + "CONCEPT_NAME": "Pharmacy", + "DOMAIN_ID": "Place of Service", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "CPT4" + }, + { + "CONCEPT_CODE": "1021865", + "CONCEPT_ID": 42628592, + "CONCEPT_NAME": "School", + "DOMAIN_ID": "Place of Service", + "INVALID_REASON_CAPTION": "Unknown", + "STANDARD_CONCEPT_CAPTION": "Unknown", + "VOCABULARY_ID": "CPT4" + } + ] + } + }, + "StartWindow": { + "Start": { + "TimeUnitValue": 30, + "TimeUnit": "day", + "Coeff": -1 + }, + "End": { + "TimeUnitValue": 43200, + "TimeUnit": "minute", + "Coeff": 1 + }, + "UseEventEnd": false + }, + "Occurrence": { + "Type": 2, + "Count": 1 + } + } + ], + "DemographicCriteriaList": [], + "Groups": [] + } + }, + { + "name": "Group Test", + "description": "Group Test", + "expression": { + "Type": "ALL", + "CriteriaList": [ + { + "Criteria": { + "ConditionOccurrence": { + "CodesetId": 0 + } + }, + "StartWindow": { + "Start": { + "TimeUnitValue": 8640000, + "TimeUnit": "second", + "Coeff": -1 + }, + "End": { + "TimeUnitValue": 144000, + "TimeUnit": "minute", + "Coeff": 1 + }, + "UseEventEnd": false + }, + "Occurrence": { + "Type": 2, + "Count": 1 + } + } + ], + "DemographicCriteriaList": [], + "Groups": [ + { + "Type": "ANY", + "CriteriaList": [ + { + "Criteria": { + "ProcedureOccurrence": { + "CodesetId": 0 + } + }, + "StartWindow": { + "Start": { + "TimeUnitValue": 86400, + "TimeUnit": "minute", + "Coeff": -1 + }, + "End": { + "TimeUnitValue": 60, + "TimeUnit": "day", + "Coeff": 1 + }, + "UseEventEnd": false + }, + "Occurrence": { + "Type": 2, + "Count": 1 + } + }, + { + "Criteria": { + "DrugExposure": { + "CodesetId": 0 + } + }, + "StartWindow": { + "Start": { + "TimeUnitValue": 2592000, + "TimeUnit": "second", + "Coeff": -1 + }, + "End": { + "TimeUnitValue": 43200, + "TimeUnit": "minute", + "Coeff": 1 + }, + "UseEventEnd": false + }, + "Occurrence": { + "Type": 2, + "Count": 1 + } + } + ], + "DemographicCriteriaList": [], + "Groups": [] + } + ] + } + } + ], + "CensoringCriteria": [], + "CollapseSettings": { + "CollapseType": "ERA", + "EraPadUnitValue": 0, + "EraPadUnit": "minute" + }, + "CensorWindow": {}, + "cdmVersionRange": ">=5.0.0" +} \ No newline at end of file diff --git a/src/test/resources/cohortgeneration/censorWindow/censorWindow_PREP.json b/src/test/resources/cohortgeneration/censorWindow/censorWindow_PREP.json index 5561c5c2..c1f659b3 100644 --- a/src/test/resources/cohortgeneration/censorWindow/censorWindow_PREP.json +++ b/src/test/resources/cohortgeneration/censorWindow/censorWindow_PREP.json @@ -21,6 +21,7 @@ "person_id":1, "condition_concept_id":2, "condition_start_date":"2000-02-01", + "condition_start_datetime": "2000-02-01 00:00:00", "condition_type_concept_id":0 }, { @@ -28,6 +29,7 @@ "person_id":1, "condition_concept_id":2, "condition_start_date":"2000-07-01", + "condition_start_datetime": "2000-07-01 00:00:00", "condition_type_concept_id":0 }, { @@ -35,6 +37,7 @@ "person_id":2, "condition_concept_id":2, "condition_start_date":"2000-01-01", + "condition_start_datetime": "2000-01-01 00:00:00", "condition_type_concept_id":0 } ], diff --git a/src/test/resources/cohortgeneration/correlatedCriteria/countsExpression.json b/src/test/resources/cohortgeneration/correlatedCriteria/countsExpression.json index b257b407..80dd8dce 100644 --- a/src/test/resources/cohortgeneration/correlatedCriteria/countsExpression.json +++ b/src/test/resources/cohortgeneration/correlatedCriteria/countsExpression.json @@ -30,7 +30,7 @@ "ConditionOccurrence": { "CodesetId": 0 } - } + } ], "ObservationWindow": { "PriorDays": 0, diff --git a/src/test/resources/cohortgeneration/correlatedCriteria/countsTest_PREP.json b/src/test/resources/cohortgeneration/correlatedCriteria/countsTest_PREP.json index 6487957d..d4ea63a1 100644 --- a/src/test/resources/cohortgeneration/correlatedCriteria/countsTest_PREP.json +++ b/src/test/resources/cohortgeneration/correlatedCriteria/countsTest_PREP.json @@ -14,6 +14,7 @@ "person_id":1, "condition_concept_id":2, "condition_start_date":"2000-06-07", + "condition_start_datetime": "2000-06-07 00:00:00", "condition_type_concept_id":0 }, { @@ -21,6 +22,7 @@ "person_id":1, "condition_concept_id":2, "condition_start_date":"2000-09-01", + "condition_start_datetime": "2000-09-01 00:00:00", "condition_type_concept_id":0 } ], diff --git a/src/test/resources/cohortgeneration/correlatedCriteria/groupExpressionHour.json b/src/test/resources/cohortgeneration/correlatedCriteria/groupExpressionHour.json new file mode 100644 index 00000000..82d80ba3 --- /dev/null +++ b/src/test/resources/cohortgeneration/correlatedCriteria/groupExpressionHour.json @@ -0,0 +1,169 @@ +{ + "ConceptSets": [ + { + "id": 0, + "name": "Parent Conceptset", + "expression": { + "items": [ + { + "concept": { + "CONCEPT_CLASS_ID": "Clinical Finding", + "CONCEPT_CODE": "P1", + "CONCEPT_ID": 1, + "CONCEPT_NAME": "Parent 1", + "DOMAIN_ID": "CONDITION", + "INVALID_REASON": "V", + "INVALID_REASON_CAPTION": "Valid", + "STANDARD_CONCEPT": "S", + "STANDARD_CONCEPT_CAPTION": "Standard", + "VOCABULARY_ID": "TestVocab" + }, + "includeDescendants": true + } + ] + } + } + ], + "PrimaryCriteria": { + "CriteriaList": [ + { + "ConditionOccurrence": { + "CodesetId": 0 + } + } + ], + "ObservationWindow": { + "PriorDays": 0, + "PostDays": 0 + }, + "PrimaryCriteriaLimit": { + "Type": "All" + } + }, + "AdditionalCriteria": { + "Type": "ANY", + "CriteriaList": [ + { + "Criteria": { + "DrugExposure": { + "CodesetId": 0 + } + }, + "StartWindow": { + "Start": { + "TimeUnitValue": 720, + "TimeUnit": "hour", + "Coeff": -1 + }, + "End": { + "Days": 0, + "TimeUnitValue": 0, + "TimeUnit": "hour", + "Coeff": -1 + }, + "UseEventEnd": false + }, + "Occurrence": { + "Type": 2, + "Count": 1 + } + }, + { + "Criteria": { + "ProcedureOccurrence": { + "CodesetId": 0 + } + }, + "StartWindow": { + "Start": { + "TimeUnitValue": 720, + "TimeUnit": "hour", + "Coeff": -1 + }, + "End": { + "TimeUnitValue": 0, + "TimeUnit": "hour", + "Coeff": -1 + }, + "UseEventEnd": false + }, + "Occurrence": { + "Type": 2, + "Count": 1 + } + } + ], + "DemographicCriteriaList": [], + "Groups": [ + { + "Type": "ALL", + "CriteriaList": [ + { + "Criteria": { + "DrugExposure": { + "CodesetId": 0 + } + }, + "StartWindow": { + "Start": { + "TimeUnitValue": 1440, + "TimeUnit": "hour", + "Coeff": -1 + }, + "End": { + "TimeUnitValue": 0, + "TimeUnit": "hour", + "Coeff": -1 + }, + "UseEventEnd": false + }, + "Occurrence": { + "Type": 2, + "Count": 1 + } + }, + { + "Criteria": { + "ProcedureOccurrence": { + "CodesetId": 0 + } + }, + "StartWindow": { + "Start": { + "TimeUnitValue": 1440, + "TimeUnit": "hour", + "Coeff": -1 + }, + "End": { + "TimeUnitValue": 0, + "TimeUnit": "hour", + "Coeff": -1 + }, + "UseEventEnd": false + }, + "Occurrence": { + "Type": 2, + "Count": 1 + } + } + ], + "DemographicCriteriaList": [], + "Groups": [] + } + ] + }, + "QualifiedLimit": { + "Type": "All" + }, + "ExpressionLimit": { + "Type": "All" + }, + "InclusionRules": [], + "CensoringCriteria": [], + "CollapseSettings": { + "CollapseType": "ERA", + "EraPadUnit": "hour", + "EraPadUnitValue": 0 + }, + "CensorWindow": {} +} \ No newline at end of file diff --git a/src/test/resources/cohortgeneration/correlatedCriteria/groupTest_PREP.json b/src/test/resources/cohortgeneration/correlatedCriteria/groupTest_PREP.json index c119053c..2d3f8620 100644 --- a/src/test/resources/cohortgeneration/correlatedCriteria/groupTest_PREP.json +++ b/src/test/resources/cohortgeneration/correlatedCriteria/groupTest_PREP.json @@ -51,6 +51,7 @@ "person_id":1, "condition_concept_id":2, "condition_start_date":"2000-06-01", + "condition_start_datetime": "2000-06-01 00:00:00", "condition_type_concept_id":0 }, { @@ -58,6 +59,7 @@ "person_id":2, "condition_concept_id":2, "condition_start_date":"2000-06-01", + "condition_start_datetime": "2000-06-01 00:00:00", "condition_type_concept_id":0 }, { @@ -65,6 +67,7 @@ "person_id":3, "condition_concept_id":2, "condition_start_date":"2000-06-01", + "condition_start_datetime": "2000-06-01 00:00:00", "condition_type_concept_id":0 } ], diff --git a/src/test/resources/cohortgeneration/exits/continuousExposure_PREP.json b/src/test/resources/cohortgeneration/exits/continuousExposure_PREP.json index 549ac6ad..587e1dfe 100644 --- a/src/test/resources/cohortgeneration/exits/continuousExposure_PREP.json +++ b/src/test/resources/cohortgeneration/exits/continuousExposure_PREP.json @@ -16,7 +16,7 @@ "observation_period_end_date":"2001-01-01", "period_type_concept_id": 0 } - ], + ], "cdm.drug_exposure": [ { "drug_exposure_id": 1, @@ -66,6 +66,6 @@ "drug_concept_id":2, "drug_exposure_start_date":"2000-10-01", "drug_type_concept_id":0 - } + } ] } \ No newline at end of file diff --git a/src/test/resources/cohortgeneration/firstOccurrence/firstOccurrenceTest_PREP.json b/src/test/resources/cohortgeneration/firstOccurrence/firstOccurrenceTest_PREP.json index ae4d177b..70b64b4c 100644 --- a/src/test/resources/cohortgeneration/firstOccurrence/firstOccurrenceTest_PREP.json +++ b/src/test/resources/cohortgeneration/firstOccurrence/firstOccurrenceTest_PREP.json @@ -44,6 +44,7 @@ "person_id":1, "condition_concept_id":2, "condition_start_date":"2000-06-07", + "condition_start_datetime": "2000-06-07 00:00:00", "condition_type_concept_id":0 }, { @@ -51,6 +52,7 @@ "person_id":1, "condition_concept_id":2, "condition_start_date":"2000-09-01", + "condition_start_datetime": "2000-09-01 00:00:00", "condition_type_concept_id":0 }, { @@ -58,6 +60,7 @@ "person_id":2, "condition_concept_id":2, "condition_start_date":"2000-01-01", + "condition_start_datetime": "2000-01-01 00:00:00", "condition_type_concept_id":0 } ], diff --git a/src/test/resources/cohortgeneration/inclusionRules/simpleInclusionRule_PREP.json b/src/test/resources/cohortgeneration/inclusionRules/simpleInclusionRule_PREP.json index 7a2bf8d8..49a41753 100644 --- a/src/test/resources/cohortgeneration/inclusionRules/simpleInclusionRule_PREP.json +++ b/src/test/resources/cohortgeneration/inclusionRules/simpleInclusionRule_PREP.json @@ -21,6 +21,7 @@ "person_id":1, "condition_concept_id":2, "condition_start_date":"2000-06-01", + "condition_start_datetime": "2000-06-01 00:00:00", "condition_type_concept_id":0 }, { @@ -28,6 +29,7 @@ "person_id":1, "condition_concept_id":3, "condition_start_date":"2000-01-01", + "condition_start_datetime": "2000-01-01 00:00:00", "condition_type_concept_id":0 }, { @@ -35,6 +37,7 @@ "person_id":2, "condition_concept_id":2, "condition_start_date":"2000-01-01", + "condition_start_datetime": "2000-01-01 00:00:00", "condition_type_concept_id":0 } ], diff --git a/src/test/resources/cohortgeneration/limits/limitTest_PREP.json b/src/test/resources/cohortgeneration/limits/limitTest_PREP.json index af1b7504..cc846b9b 100644 --- a/src/test/resources/cohortgeneration/limits/limitTest_PREP.json +++ b/src/test/resources/cohortgeneration/limits/limitTest_PREP.json @@ -14,6 +14,7 @@ "person_id":1, "condition_concept_id":2, "condition_start_date":"2000-01-01", + "condition_start_datetime": "2000-01-01 00:00:00", "condition_type_concept_id":0 }, { @@ -21,6 +22,7 @@ "person_id":1, "condition_concept_id":2, "condition_start_date":"2000-02-01", + "condition_start_datetime": "2000-02-01 00:00:00", "condition_type_concept_id":0 }, { @@ -28,6 +30,7 @@ "person_id":1, "condition_concept_id":2, "condition_start_date":"2000-06-01", + "condition_start_datetime": "2000-06-01 00:00:00", "condition_type_concept_id":0 }, { @@ -35,6 +38,7 @@ "person_id":1, "condition_concept_id":2, "condition_start_date":"2000-07-01", + "condition_start_datetime": "2000-07-01 00:00:00", "condition_type_concept_id":0 }, { @@ -42,6 +46,7 @@ "person_id":1, "condition_concept_id":2, "condition_start_date":"2000-08-01", + "condition_start_datetime": "2000-08-01 00:00:00", "condition_type_concept_id":0 }, { @@ -49,6 +54,7 @@ "person_id":1, "condition_concept_id":2, "condition_start_date":"2000-11-01", + "condition_start_datetime": "2000-11-01 00:00:00", "condition_type_concept_id":0 }, { @@ -56,6 +62,7 @@ "person_id":1, "condition_concept_id":2, "condition_start_date":"2000-12-01", + "condition_start_datetime": "2000-12-01 00:00:00", "condition_type_concept_id":0 } ], diff --git a/src/test/resources/cohortgeneration/mixedConceptsets/mixedConceptsets_PREP.json b/src/test/resources/cohortgeneration/mixedConceptsets/mixedConceptsets_PREP.json index 2e1ac335..0890f2ae 100644 --- a/src/test/resources/cohortgeneration/mixedConceptsets/mixedConceptsets_PREP.json +++ b/src/test/resources/cohortgeneration/mixedConceptsets/mixedConceptsets_PREP.json @@ -14,6 +14,7 @@ "person_id":1, "condition_concept_id":2, "condition_start_date":"2000-02-01", + "condition_start_datetime":"2000-02-01 00:00:00", "condition_type_concept_id":0 }, { @@ -22,6 +23,7 @@ "condition_concept_id":2, "condition_source_concept_id":3, "condition_start_date":"2000-07-01", + "condition_start_datetime":"2000-07-01 00:00:00", "condition_type_concept_id":0 } ], diff --git a/src/test/resources/corelatedcriteria/distinctDefault_PREP.json b/src/test/resources/corelatedcriteria/distinctDefault_PREP.json index cb1ec56a..bfb23b37 100644 --- a/src/test/resources/corelatedcriteria/distinctDefault_PREP.json +++ b/src/test/resources/corelatedcriteria/distinctDefault_PREP.json @@ -29,6 +29,7 @@ "person_id":1, "condition_concept_id":2, "condition_start_date":"2005-01-01", + "condition_start_datetime": "2005-01-01 00:00:00", "condition_type_concept_id":0, "visit_occurrence_id": 2 }, @@ -37,6 +38,7 @@ "person_id":1, "condition_concept_id":3, "condition_start_date":"2005-01-02", + "condition_start_datetime": "2005-01-02 00:00:00", "condition_type_concept_id":0, "visit_occurrence_id": 2 }, @@ -45,6 +47,7 @@ "person_id":2, "condition_concept_id":2, "condition_start_date":"2005-01-01", + "condition_start_datetime":"2005-01-01 00:00:00", "condition_type_concept_id":0, "visit_occurrence_id": 3 }, @@ -53,6 +56,7 @@ "person_id":2, "condition_concept_id":2, "condition_start_date":"2005-01-01", + "condition_start_datetime": "2005-01-01 00:00:00", "condition_type_concept_id":0, "visit_occurrence_id": 4 } diff --git a/src/test/resources/corelatedcriteria/distinctStart_PREP.json b/src/test/resources/corelatedcriteria/distinctStart_PREP.json index 450e2cec..a355dc23 100644 --- a/src/test/resources/corelatedcriteria/distinctStart_PREP.json +++ b/src/test/resources/corelatedcriteria/distinctStart_PREP.json @@ -21,6 +21,7 @@ "person_id":1, "condition_concept_id":2, "condition_start_date":"2000-01-01", + "condition_start_datetime": "2000-01-01 00:00:00", "condition_type_concept_id":0, "visit_occurrence_id": 1 }, @@ -29,6 +30,7 @@ "person_id":1, "condition_concept_id":2, "condition_start_date":"2005-01-01", + "condition_start_datetime": "2005-01-01 00:00:00", "condition_type_concept_id":0, "visit_occurrence_id": 2 }, @@ -37,6 +39,7 @@ "person_id":1, "condition_concept_id":2, "condition_start_date":"2005-01-02", + "condition_start_datetime": "2005-01-02 00:00:00", "condition_type_concept_id":0, "visit_occurrence_id": 2 }, @@ -45,6 +48,7 @@ "person_id":2, "condition_concept_id":2, "condition_start_date":"2005-01-01", + "condition_start_datetime": "2005-01-01 00:00:00", "condition_type_concept_id":0, "visit_occurrence_id": 3 }, @@ -53,6 +57,7 @@ "person_id":2, "condition_concept_id":2, "condition_start_date":"2005-01-01", + "condition_start_datetime": "2005-01-01 00:00:00", "condition_type_concept_id":0, "visit_occurrence_id": 4 } diff --git a/src/test/resources/corelatedcriteria/distinctVisit_PREP.json b/src/test/resources/corelatedcriteria/distinctVisit_PREP.json index adbdb532..b6fb6186 100644 --- a/src/test/resources/corelatedcriteria/distinctVisit_PREP.json +++ b/src/test/resources/corelatedcriteria/distinctVisit_PREP.json @@ -21,6 +21,7 @@ "person_id":1, "condition_concept_id":2, "condition_start_date":"2000-01-01", + "condition_start_datetime":"2000-01-01 09:00:00", "condition_type_concept_id":0, "visit_occurrence_id": 1 }, @@ -29,6 +30,7 @@ "person_id":1, "condition_concept_id":2, "condition_start_date":"2005-01-01", + "condition_start_datetime":"2005-01-01 09:00:00", "condition_type_concept_id":0, "visit_occurrence_id": 2 }, @@ -37,6 +39,7 @@ "person_id":1, "condition_concept_id":2, "condition_start_date":"2005-01-02", + "condition_start_datetime":"2005-01-02 09:00:00", "condition_type_concept_id":0, "visit_occurrence_id": 2 }, @@ -45,6 +48,7 @@ "person_id":2, "condition_concept_id":2, "condition_start_date":"2005-01-01", + "condition_start_datetime":"2005-01-01 09:00:00", "condition_type_concept_id":0, "visit_occurrence_id": 3 }, @@ -53,6 +57,7 @@ "person_id":2, "condition_concept_id":2, "condition_start_date":"2005-01-01", + "condition_start_datetime":"2005-01-01 09:00:00", "condition_type_concept_id":0, "visit_occurrence_id": 4 } diff --git a/src/test/resources/corelatedcriteria/simpleInclusion_PREP.json b/src/test/resources/corelatedcriteria/simpleInclusion_PREP.json index ae85c790..8e8b9f2c 100644 --- a/src/test/resources/corelatedcriteria/simpleInclusion_PREP.json +++ b/src/test/resources/corelatedcriteria/simpleInclusion_PREP.json @@ -21,6 +21,7 @@ "person_id":1, "condition_concept_id":2, "condition_start_date":"2000-01-01", + "condition_start_datetime":"2000-01-01 01:00:00", "condition_type_concept_id":0 }, { @@ -28,6 +29,7 @@ "person_id":1, "condition_concept_id":2, "condition_start_date":"2005-01-01", + "condition_start_datetime":"2005-01-01 01:00:00", "condition_type_concept_id":0 }, { @@ -35,6 +37,7 @@ "person_id":1, "condition_concept_id":2, "condition_start_date":"2005-02-01", + "condition_start_datetime":"2005-02-01 01:00:00", "condition_type_concept_id":0 }, { @@ -42,6 +45,7 @@ "person_id":1, "condition_concept_id":2, "condition_start_date":"2005-03-01", + "condition_start_datetime":"2005-03-01 01:00:00", "condition_type_concept_id":0 }, { @@ -49,6 +53,7 @@ "person_id":1, "condition_concept_id":2, "condition_start_date":"2005-04-01", + "condition_start_datetime":"2005-04-01 01:00:00", "condition_type_concept_id":0 } ], diff --git a/src/test/resources/criteria/conditionOccurrenceDateOffset_PREP.json b/src/test/resources/criteria/conditionOccurrenceDateOffset_PREP.json index a0f1dee9..63fe4bbc 100644 --- a/src/test/resources/criteria/conditionOccurrenceDateOffset_PREP.json +++ b/src/test/resources/criteria/conditionOccurrenceDateOffset_PREP.json @@ -21,6 +21,7 @@ "person_id":1, "condition_concept_id":1, "condition_start_date":"2000-01-01", + "condition_start_datetime": "2000-01-01 00:00:00", "condition_type_concept_id":0 }, { @@ -28,7 +29,9 @@ "person_id":1, "condition_concept_id":1, "condition_start_date":"2000-06-01", + "condition_start_datetime": "2000-06-01 00:00:00", "condition_end_date":"2000-07-01", + "condition_end_datetime": "2000-07-01 00:00:00", "condition_type_concept_id":0 }, { @@ -36,6 +39,7 @@ "person_id":1, "condition_concept_id":1, "condition_start_date":"2000-12-31", + "condition_start_datetime": "2000-12-31 00:00:00", "condition_type_concept_id":0 }, { @@ -43,6 +47,7 @@ "person_id":2, "condition_concept_id":1, "condition_start_date":"2000-01-01", + "condition_start_datetime": "2000-01-01 00:00:00", "condition_type_concept_id":0 }, { @@ -50,6 +55,7 @@ "person_id":2, "condition_concept_id":1, "condition_start_date":"2000-12-31", + "condition_start_datetime": "2000-12-31 00:00:00", "condition_type_concept_id":0 } ], diff --git a/src/test/resources/ddl/cdm_v5.0.sql b/src/test/resources/ddl/cdm_v5.0.sql index eb9ce9af..0eb1dad8 100644 --- a/src/test/resources/ddl/cdm_v5.0.sql +++ b/src/test/resources/ddl/cdm_v5.0.sql @@ -119,8 +119,8 @@ CREATE TABLE @schemaName.attribute_definition ( ) ; -CREATE TABLE @schemaName.cdm_source - ( +CREATE TABLE @schemaName.cdm_source + ( cdm_source_name VARCHAR(255) NOT NULL, cdm_source_abbreviation VARCHAR(25) NULL, cdm_holder VARCHAR(255) NULL, @@ -131,46 +131,46 @@ CREATE TABLE @schemaName.cdm_source cdm_release_date DATE NULL, cdm_version VARCHAR(10) NULL, vocabulary_version VARCHAR(20) NULL - ) + ) ; -CREATE TABLE @schemaName.person +CREATE TABLE @schemaName.person ( - person_id INTEGER NOT NULL , - gender_concept_id INTEGER NOT NULL , - year_of_birth INTEGER NOT NULL , - month_of_birth INTEGER NULL, - day_of_birth INTEGER NULL, + person_id INTEGER NOT NULL , + gender_concept_id INTEGER NOT NULL , + year_of_birth INTEGER NOT NULL , + month_of_birth INTEGER NULL, + day_of_birth INTEGER NULL, time_of_birth VARCHAR(10) NULL, - race_concept_id INTEGER NOT NULL, - ethnicity_concept_id INTEGER NOT NULL, - location_id INTEGER NULL, - provider_id INTEGER NULL, - care_site_id INTEGER NULL, - person_source_value VARCHAR(50) NULL, + race_concept_id INTEGER NOT NULL, + ethnicity_concept_id INTEGER NOT NULL, + location_id INTEGER NULL, + provider_id INTEGER NULL, + care_site_id INTEGER NULL, + person_source_value VARCHAR(50) NULL, gender_source_value VARCHAR(50) NULL, - gender_source_concept_id INTEGER NULL, - race_source_value VARCHAR(50) NULL, - race_source_concept_id INTEGER NULL, + gender_source_concept_id INTEGER NULL, + race_source_value VARCHAR(50) NULL, + race_source_concept_id INTEGER NULL, ethnicity_source_value VARCHAR(50) NULL, ethnicity_source_concept_id INTEGER NULL - ) + ) ; -CREATE TABLE @schemaName.observation_period - ( - observation_period_id INTEGER NOT NULL , - person_id INTEGER NOT NULL , - observation_period_start_date DATE NOT NULL , +CREATE TABLE @schemaName.observation_period + ( + observation_period_id INTEGER NOT NULL , + person_id INTEGER NOT NULL , + observation_period_start_date DATE NOT NULL , observation_period_end_date DATE NOT NULL , period_type_concept_id INTEGER NOT NULL - ) + ) ; CREATE TABLE @schemaName.specimen - ( + ( specimen_id INTEGER NOT NULL , person_id INTEGER NOT NULL , specimen_concept_id INTEGER NOT NULL , @@ -190,141 +190,148 @@ CREATE TABLE @schemaName.specimen ; -CREATE TABLE @schemaName.death - ( - person_id INTEGER NOT NULL , - death_date DATE NOT NULL , - death_type_concept_id INTEGER NOT NULL , - cause_concept_id INTEGER NULL , +CREATE TABLE @schemaName.death + ( + person_id INTEGER NOT NULL , + death_date DATE NOT NULL , + death_datetime TIMESTAMP NULL , + death_type_concept_id INTEGER NOT NULL , + cause_concept_id INTEGER NULL , cause_source_value VARCHAR(50) NULL, cause_source_concept_id INTEGER NULL - ) + ) ; -CREATE TABLE @schemaName.visit_occurrence - ( - visit_occurrence_id INTEGER NOT NULL , - person_id INTEGER NOT NULL , - visit_concept_id INTEGER NOT NULL , - visit_start_date DATE NOT NULL , +CREATE TABLE @schemaName.visit_occurrence + ( + visit_occurrence_id INTEGER NOT NULL , + person_id INTEGER NOT NULL , + visit_concept_id INTEGER NOT NULL , + visit_start_date DATE NOT NULL , visit_start_time VARCHAR(10) NULL , visit_end_date DATE NOT NULL , - visit_end_time VARCHAR(10) NULL , + visit_end_time VARCHAR(10) NULL , visit_type_concept_id INTEGER NOT NULL , provider_id INTEGER NULL, - care_site_id INTEGER NULL, + care_site_id INTEGER NULL, visit_source_value VARCHAR(50) NULL, visit_source_concept_id INTEGER NULL - ) + ) ; -CREATE TABLE @schemaName.procedure_occurrence - ( - procedure_occurrence_id INTEGER NOT NULL , - person_id INTEGER NOT NULL , - procedure_concept_id INTEGER NOT NULL , - procedure_date DATE NOT NULL , +CREATE TABLE @schemaName.procedure_occurrence + ( + procedure_occurrence_id INTEGER NOT NULL , + person_id INTEGER NOT NULL , + procedure_concept_id INTEGER NOT NULL , + procedure_date DATE NOT NULL , procedure_type_concept_id INTEGER NOT NULL , modifier_concept_id INTEGER NULL , - quantity INTEGER NULL , - provider_id INTEGER NULL , - visit_occurrence_id INTEGER NULL , + quantity INTEGER NULL , + provider_id INTEGER NULL , + visit_occurrence_id INTEGER NULL , procedure_source_value VARCHAR(50) NULL , procedure_source_concept_id INTEGER NULL , qualifier_source_value VARCHAR(50) NULL - ) -; - - -CREATE TABLE @schemaName.drug_exposure - ( - drug_exposure_id INTEGER NOT NULL , - person_id INTEGER NOT NULL , - drug_concept_id INTEGER NOT NULL , - drug_exposure_start_date DATE NOT NULL , - drug_exposure_end_date DATE NULL , - drug_type_concept_id INTEGER NOT NULL , - stop_reason VARCHAR(20) NULL , - refills INTEGER NULL , - quantity NUMERIC NULL , - days_supply INTEGER NULL , - sig TEXT NULL , + ) +; + + +CREATE TABLE @schemaName.drug_exposure + ( + drug_exposure_id INTEGER NOT NULL , + person_id INTEGER NOT NULL , + drug_concept_id INTEGER NOT NULL , + drug_exposure_start_date DATE NOT NULL , + drug_exposure_start_datetime TIMESTAMP NULL , + drug_exposure_end_date DATE NULL , + drug_exposure_end_datetime TIMESTAMP NULL , + drug_type_concept_id INTEGER NOT NULL , + stop_reason VARCHAR(20) NULL , + refills INTEGER NULL , + quantity NUMERIC NULL , + days_supply INTEGER NULL , + sig TEXT NULL , route_concept_id INTEGER NULL , effective_drug_dose NUMERIC NULL , dose_unit_concept_id INTEGER NULL , lot_number VARCHAR(50) NULL , - provider_id INTEGER NULL , - visit_occurrence_id INTEGER NULL , + provider_id INTEGER NULL , + visit_occurrence_id INTEGER NULL , drug_source_value VARCHAR(50) NULL , drug_source_concept_id INTEGER NULL , route_source_value VARCHAR(50) NULL , dose_unit_source_value VARCHAR(50) NULL - ) + ) ; -CREATE TABLE @schemaName.device_exposure - ( - device_exposure_id INTEGER NOT NULL , - person_id INTEGER NOT NULL , - device_concept_id INTEGER NOT NULL , - device_exposure_start_date DATE NOT NULL , - device_exposure_end_date DATE NULL , - device_type_concept_id INTEGER NOT NULL , +CREATE TABLE @schemaName.device_exposure + ( + device_exposure_id INTEGER NOT NULL , + person_id INTEGER NOT NULL , + device_concept_id INTEGER NOT NULL , + device_exposure_start_date DATE NOT NULL , + device_exposure_start_datetime TIMESTAMP NULL , + device_exposure_end_date DATE NULL , + device_exposure_end_datetime TIMESTAMP NULL , + device_type_concept_id INTEGER NOT NULL , unique_device_id VARCHAR(50) NULL , quantity INTEGER NULL , - provider_id INTEGER NULL , - visit_occurrence_id INTEGER NULL , + provider_id INTEGER NULL , + visit_occurrence_id INTEGER NULL , device_source_value VARCHAR(100) NULL , device_source_concept_id INTEGER NULL - ) -; - -CREATE TABLE @schemaName.condition_occurrence - ( - condition_occurrence_id INTEGER NOT NULL , - person_id INTEGER NOT NULL , - condition_concept_id INTEGER NOT NULL , - condition_start_date DATE NOT NULL , - condition_end_date DATE NULL , - condition_type_concept_id INTEGER NOT NULL , - stop_reason VARCHAR(20) NULL , - provider_id INTEGER NULL , - visit_occurrence_id INTEGER NULL , + ) +; + +CREATE TABLE @schemaName.condition_occurrence + ( + condition_occurrence_id INTEGER NOT NULL , + person_id INTEGER NOT NULL , + condition_concept_id INTEGER NOT NULL , + condition_start_date DATE NOT NULL , + condition_start_datetime TIMESTAMP NULL , + condition_end_date DATE NULL , + condition_end_datetime TIMESTAMP NULL , + condition_type_concept_id INTEGER NOT NULL , + stop_reason VARCHAR(20) NULL , + provider_id INTEGER NULL , + visit_occurrence_id INTEGER NULL , condition_source_value VARCHAR(50) NULL , condition_source_concept_id INTEGER NULL - ) + ) ; -CREATE TABLE @schemaName.measurement - ( - measurement_id INTEGER NOT NULL , - person_id INTEGER NOT NULL , - measurement_concept_id INTEGER NOT NULL , - measurement_date DATE NOT NULL , +CREATE TABLE @schemaName.measurement + ( + measurement_id INTEGER NOT NULL , + person_id INTEGER NOT NULL , + measurement_concept_id INTEGER NOT NULL , + measurement_date DATE NOT NULL , measurement_time VARCHAR(10) NULL , measurement_type_concept_id INTEGER NOT NULL , - operator_concept_id INTEGER NULL , - value_as_number NUMERIC NULL , - value_as_concept_id INTEGER NULL , - unit_concept_id INTEGER NULL , - range_low NUMERIC NULL , - range_high NUMERIC NULL , - provider_id INTEGER NULL , - visit_occurrence_id INTEGER NULL , - measurement_source_value VARCHAR(50) NULL , + operator_concept_id INTEGER NULL , + value_as_number NUMERIC NULL , + value_as_concept_id INTEGER NULL , + unit_concept_id INTEGER NULL , + range_low NUMERIC NULL , + range_high NUMERIC NULL , + provider_id INTEGER NULL , + visit_occurrence_id INTEGER NULL , + measurement_source_value VARCHAR(50) NULL , measurement_source_concept_id INTEGER NULL , unit_source_value VARCHAR(50) NULL , value_source_value VARCHAR(50) NULL - ) + ) ; -CREATE TABLE @schemaName.note - ( - note_id INTEGER NOT NULL , - person_id INTEGER NOT NULL , +CREATE TABLE @schemaName.note + ( + note_id INTEGER NOT NULL , + person_id INTEGER NOT NULL , note_date DATE NOT NULL , note_time VARCHAR(10) NULL , note_type_concept_id INTEGER NOT NULL , @@ -332,36 +339,36 @@ CREATE TABLE @schemaName.note provider_id INTEGER NULL , visit_occurrence_id INTEGER NULL , note_source_value VARCHAR(50) NULL - ) + ) ; -CREATE TABLE @schemaName.observation - ( - observation_id INTEGER NOT NULL , - person_id INTEGER NOT NULL , - observation_concept_id INTEGER NOT NULL , - observation_date DATE NOT NULL , - observation_time VARCHAR(10) NULL , - observation_type_concept_id INTEGER NOT NULL , - value_as_number NUMERIC NULL , - value_as_string VARCHAR(60) NULL , - value_as_concept_id INTEGER NULL , +CREATE TABLE @schemaName.observation + ( + observation_id INTEGER NOT NULL , + person_id INTEGER NOT NULL , + observation_concept_id INTEGER NOT NULL , + observation_date DATE NOT NULL , + observation_time VARCHAR(10) NULL , + observation_type_concept_id INTEGER NOT NULL , + value_as_number NUMERIC NULL , + value_as_string VARCHAR(60) NULL , + value_as_concept_id INTEGER NULL , qualifier_concept_id INTEGER NULL , - unit_concept_id INTEGER NULL , - provider_id INTEGER NULL , - visit_occurrence_id INTEGER NULL , + unit_concept_id INTEGER NULL , + provider_id INTEGER NULL , + visit_occurrence_id INTEGER NULL , observation_source_value VARCHAR(50) NULL , - observation_source_concept_id INTEGER NULL , + observation_source_concept_id INTEGER NULL , unit_source_value VARCHAR(50) NULL , qualifier_source_value VARCHAR(50) NULL - ) + ) ; -CREATE TABLE @schemaName.fact_relationship - ( - domain_concept_id_1 INTEGER NOT NULL , +CREATE TABLE @schemaName.fact_relationship + ( + domain_concept_id_1 INTEGER NOT NULL , fact_id_1 INTEGER NOT NULL , domain_concept_id_2 INTEGER NOT NULL , fact_id_2 INTEGER NOT NULL , @@ -369,187 +376,189 @@ CREATE TABLE @schemaName.fact_relationship ) ; -CREATE TABLE @schemaName.location - ( - location_id INTEGER NOT NULL , - address_1 VARCHAR(50) NULL , - address_2 VARCHAR(50) NULL , - city VARCHAR(50) NULL , - state VARCHAR(2) NULL , - zip VARCHAR(9) NULL , - county VARCHAR(20) NULL , +CREATE TABLE @schemaName.location + ( + location_id INTEGER NOT NULL , + address_1 VARCHAR(50) NULL , + address_2 VARCHAR(50) NULL , + city VARCHAR(50) NULL , + state VARCHAR(2) NULL , + zip VARCHAR(9) NULL , + county VARCHAR(20) NULL , location_source_value VARCHAR(50) NULL - ) + ) ; -CREATE TABLE @schemaName.care_site - ( - care_site_id INTEGER NOT NULL , +CREATE TABLE @schemaName.care_site + ( + care_site_id INTEGER NOT NULL , care_site_name VARCHAR(255) NULL , place_of_service_concept_id INTEGER NULL , - location_id INTEGER NULL , - care_site_source_value VARCHAR(50) NULL , + location_id INTEGER NULL , + care_site_source_value VARCHAR(50) NULL , place_of_service_source_value VARCHAR(50) NULL - ) + ) ; -CREATE TABLE @schemaName.provider - ( +CREATE TABLE @schemaName.provider + ( provider_id INTEGER NOT NULL , - provider_name VARCHAR(255) NULL , - NPI VARCHAR(20) NULL , - DEA VARCHAR(20) NULL , - specialty_concept_id INTEGER NULL , - care_site_id INTEGER NULL , + provider_name VARCHAR(255) NULL , + NPI VARCHAR(20) NULL , + DEA VARCHAR(20) NULL , + specialty_concept_id INTEGER NULL , + care_site_id INTEGER NULL , year_of_birth INTEGER NULL , gender_concept_id INTEGER NULL , - provider_source_value VARCHAR(50) NULL , + provider_source_value VARCHAR(50) NULL , specialty_source_value VARCHAR(50) NULL , - specialty_source_concept_id INTEGER NULL , + specialty_source_concept_id INTEGER NULL , gender_source_value VARCHAR(50) NULL , gender_source_concept_id INTEGER NULL - ) + ) ; -CREATE TABLE @schemaName.payer_plan_period - ( - payer_plan_period_id INTEGER NOT NULL , - person_id INTEGER NOT NULL , - payer_plan_period_start_date DATE NOT NULL , - payer_plan_period_end_date DATE NOT NULL , - payer_source_value VARCHAR (50) NULL , - plan_source_value VARCHAR (50) NULL , - family_source_value VARCHAR (50) NULL - ) +CREATE TABLE @schemaName.payer_plan_period + ( + payer_plan_period_id INTEGER NOT NULL , + person_id INTEGER NOT NULL , + payer_plan_period_start_date DATE NOT NULL , + payer_plan_period_end_date DATE NOT NULL , + payer_source_value VARCHAR (50) NULL , + plan_source_value VARCHAR (50) NULL , + family_source_value VARCHAR (50) NULL + ) ; -CREATE TABLE @schemaName.visit_cost - ( - visit_cost_id INTEGER NOT NULL , - visit_occurrence_id INTEGER NOT NULL , +CREATE TABLE @schemaName.visit_cost + ( + visit_cost_id INTEGER NOT NULL , + visit_occurrence_id INTEGER NOT NULL , currency_concept_id INTEGER NULL , - paid_copay NUMERIC NULL , - paid_coinsurance NUMERIC NULL , - paid_toward_deductible NUMERIC NULL , - paid_by_payer NUMERIC NULL , - paid_by_coordination_benefits NUMERIC NULL , - total_out_of_pocket NUMERIC NULL , - total_paid NUMERIC NULL , + paid_copay NUMERIC NULL , + paid_coinsurance NUMERIC NULL , + paid_toward_deductible NUMERIC NULL , + paid_by_payer NUMERIC NULL , + paid_by_coordination_benefits NUMERIC NULL , + total_out_of_pocket NUMERIC NULL , + total_paid NUMERIC NULL , payer_plan_period_id INTEGER NULL - ) + ) ; -CREATE TABLE @schemaName.procedure_cost - ( - procedure_cost_id INTEGER NOT NULL , - procedure_occurrence_id INTEGER NOT NULL , +CREATE TABLE @schemaName.procedure_cost + ( + procedure_cost_id INTEGER NOT NULL , + procedure_occurrence_id INTEGER NOT NULL , currency_concept_id INTEGER NULL , - paid_copay NUMERIC NULL , - paid_coinsurance NUMERIC NULL , - paid_toward_deductible NUMERIC NULL , - paid_by_payer NUMERIC NULL , - paid_by_coordination_benefits NUMERIC NULL , - total_out_of_pocket NUMERIC NULL , + paid_copay NUMERIC NULL , + paid_coinsurance NUMERIC NULL , + paid_toward_deductible NUMERIC NULL , + paid_by_payer NUMERIC NULL , + paid_by_coordination_benefits NUMERIC NULL , + total_out_of_pocket NUMERIC NULL , total_paid NUMERIC NULL , - revenue_code_concept_id INTEGER NULL , + revenue_code_concept_id INTEGER NULL , payer_plan_period_id INTEGER NULL , revenue_code_source_value VARCHAR(50) NULL - ) + ) ; -CREATE TABLE @schemaName.drug_cost +CREATE TABLE @schemaName.drug_cost ( - drug_cost_id INTEGER NOT NULL , - drug_exposure_id INTEGER NOT NULL , + drug_cost_id INTEGER NOT NULL , + drug_exposure_id INTEGER NOT NULL , currency_concept_id INTEGER NULL , - paid_copay NUMERIC NULL , - paid_coinsurance NUMERIC NULL , - paid_toward_deductible NUMERIC NULL , - paid_by_payer NUMERIC NULL , - paid_by_coordination_benefits NUMERIC NULL , - total_out_of_pocket NUMERIC NULL , - total_paid NUMERIC NULL , - ingredient_cost NUMERIC NULL , - dispensing_fee NUMERIC NULL , - average_wholesale_price NUMERIC NULL , + paid_copay NUMERIC NULL , + paid_coinsurance NUMERIC NULL , + paid_toward_deductible NUMERIC NULL , + paid_by_payer NUMERIC NULL , + paid_by_coordination_benefits NUMERIC NULL , + total_out_of_pocket NUMERIC NULL , + total_paid NUMERIC NULL , + ingredient_cost NUMERIC NULL , + dispensing_fee NUMERIC NULL , + average_wholesale_price NUMERIC NULL , payer_plan_period_id INTEGER NULL - ) + ) ; -CREATE TABLE @schemaName.device_cost +CREATE TABLE @schemaName.device_cost ( - device_cost_id INTEGER NOT NULL , - device_exposure_id INTEGER NOT NULL , + device_cost_id INTEGER NOT NULL , + device_exposure_id INTEGER NOT NULL , currency_concept_id INTEGER NULL , - paid_copay NUMERIC NULL , - paid_coinsurance NUMERIC NULL , - paid_toward_deductible NUMERIC NULL , - paid_by_payer NUMERIC NULL , - paid_by_coordination_benefits NUMERIC NULL , - total_out_of_pocket NUMERIC NULL , - total_paid NUMERIC NULL , + paid_copay NUMERIC NULL , + paid_coinsurance NUMERIC NULL , + paid_toward_deductible NUMERIC NULL , + paid_by_payer NUMERIC NULL , + paid_by_coordination_benefits NUMERIC NULL , + total_out_of_pocket NUMERIC NULL , + total_paid NUMERIC NULL , payer_plan_period_id INTEGER NULL - ) + ) ; -CREATE TABLE @schemaName.cohort - ( - cohort_definition_id INTEGER NOT NULL , +CREATE TABLE @schemaName.cohort + ( + cohort_definition_id INTEGER NOT NULL , subject_id INTEGER NOT NULL , - cohort_start_date DATE NOT NULL , + cohort_start_date DATE NOT NULL , cohort_end_date DATE NOT NULL - ) + ) ; -CREATE TABLE @schemaName.cohort_attribute - ( - cohort_definition_id INTEGER NOT NULL , - cohort_start_date DATE NOT NULL , - cohort_end_date DATE NOT NULL , - subject_id INTEGER NOT NULL , +CREATE TABLE @schemaName.cohort_attribute + ( + cohort_definition_id INTEGER NOT NULL , + cohort_start_date DATE NOT NULL , + cohort_end_date DATE NOT NULL , + subject_id INTEGER NOT NULL , attribute_definition_id INTEGER NOT NULL , value_as_number NUMERIC NULL , value_as_concept_id INTEGER NULL - ) + ) ; -CREATE TABLE @schemaName.drug_era - ( - drug_era_id INTEGER NOT NULL , - person_id INTEGER NOT NULL , - drug_concept_id INTEGER NOT NULL , - drug_era_start_date DATE NOT NULL , - drug_era_end_date DATE NOT NULL , +CREATE TABLE @schemaName.drug_era + ( + drug_era_id INTEGER NOT NULL , + person_id INTEGER NOT NULL , + drug_concept_id INTEGER NOT NULL , + drug_era_start_date DATE NOT NULL , + drug_era_end_date DATE NOT NULL , drug_exposure_count INTEGER NULL , gap_days INTEGER NULL - ) + ) ; -CREATE TABLE @schemaName.dose_era +CREATE TABLE @schemaName.dose_era ( - dose_era_id INTEGER NOT NULL , - person_id INTEGER NOT NULL , - drug_concept_id INTEGER NOT NULL , + dose_era_id INTEGER NOT NULL , + person_id INTEGER NOT NULL , + drug_concept_id INTEGER NOT NULL , unit_concept_id INTEGER NOT NULL , dose_value NUMERIC NOT NULL , - dose_era_start_date DATE NOT NULL , - dose_era_end_date DATE NOT NULL - ) + dose_era_start_date DATE NOT NULL , + dose_era_start_datetime TIMESTAMP NULL , + dose_era_end_date DATE NOT NULL , + dose_era_end_datetime TIMESTAMP NULL + ) ; -CREATE TABLE @schemaName.condition_era - ( - condition_era_id INTEGER NOT NULL , - person_id INTEGER NOT NULL , - condition_concept_id INTEGER NOT NULL , - condition_era_start_date DATE NOT NULL , - condition_era_end_date DATE NOT NULL , +CREATE TABLE @schemaName.condition_era + ( + condition_era_id INTEGER NOT NULL , + person_id INTEGER NOT NULL , + condition_concept_id INTEGER NOT NULL , + condition_era_start_date DATE NOT NULL , + condition_era_end_date DATE NOT NULL , condition_occurrence_count INTEGER NULL - ) + ) ; diff --git a/src/test/resources/ddl/cdm_v5.2.sql b/src/test/resources/ddl/cdm_v5.2.sql index cc1c9686..55773d3c 100644 --- a/src/test/resources/ddl/cdm_v5.2.sql +++ b/src/test/resources/ddl/cdm_v5.2.sql @@ -292,7 +292,7 @@ CREATE TABLE @schemaName.condition_occurrence person_id INTEGER NOT NULL , condition_concept_id INTEGER NOT NULL , condition_start_date DATE NOT NULL , - condition_start_datetime TIMESTAMP NOT NULL , + condition_start_datetime TIMESTAMP NULL , condition_end_date DATE NULL , condition_end_datetime TIMESTAMP NULL , condition_type_concept_id INTEGER NOT NULL , diff --git a/src/test/resources/ddl/cdm_v5.3.sql b/src/test/resources/ddl/cdm_v5.3.sql index 456ad4b8..d4975350 100644 --- a/src/test/resources/ddl/cdm_v5.3.sql +++ b/src/test/resources/ddl/cdm_v5.3.sql @@ -315,7 +315,7 @@ CREATE TABLE @schemaName.condition_occurrence person_id INTEGER NOT NULL , condition_concept_id INTEGER NOT NULL , condition_start_date DATE NOT NULL , - condition_start_datetime TIMESTAMP NOT NULL , + condition_start_datetime TIMESTAMP NULL , condition_end_date DATE NULL , condition_end_datetime TIMESTAMP NULL , condition_type_concept_id INTEGER NOT NULL , diff --git a/src/test/resources/negativecontrols/firstOccurrence/firstOccurrenceTest_PREP.json b/src/test/resources/negativecontrols/firstOccurrence/firstOccurrenceTest_PREP.json index 287f1e2a..4c687fc7 100644 --- a/src/test/resources/negativecontrols/firstOccurrence/firstOccurrenceTest_PREP.json +++ b/src/test/resources/negativecontrols/firstOccurrence/firstOccurrenceTest_PREP.json @@ -14,6 +14,7 @@ "person_id":1, "condition_concept_id":2, "condition_start_date":"2000-06-07", + "condition_start_datetime": "2000-06-07 00:00:00", "condition_type_concept_id":0 }, { @@ -21,6 +22,7 @@ "person_id":1, "condition_concept_id":2, "condition_start_date":"2000-09-01", + "condition_start_datetime": "2000-09-01 00:00:00", "condition_type_concept_id":0 } ] diff --git a/src/test/resources/printfriendly/countDistinctCriteriaWithTimeUnitInterval.json b/src/test/resources/printfriendly/countDistinctCriteriaWithTimeUnitInterval.json new file mode 100644 index 00000000..466358c7 --- /dev/null +++ b/src/test/resources/printfriendly/countDistinctCriteriaWithTimeUnitInterval.json @@ -0,0 +1,181 @@ +{ + "ConceptSets": [ + { + "id": 0, + "name": "Empty Concept Set", + "expression": { + "items": [] + } + } + ], + "PrimaryCriteria": { + "CriteriaList": [ + { + "ConditionOccurrence": { + "CodesetId": 0, + "OccurrenceStartDate": { + "Value": "2010-01-01", + "Op": "gte" + } + } + }, + { + "ConditionOccurrence": { + "CorrelatedCriteria": { + "Type": "ALL", + "CriteriaList": [ + { + "Criteria": { + "ConditionOccurrence": {} + }, + "StartWindow": { + "Start": { + "TimeUnitValue": 2592000, + "TimeUnit": "second", + "Coeff": -1 + }, + "End": { + "TimeUnitValue": 720, + "TimeUnit": "hour", + "Coeff": 1 + }, + "UseEventEnd": false + }, + "Occurrence": { + "IsDistinct": true, + "Type": 2, + "Count": 1 + } + } + ], + "DemographicCriteriaList": [], + "Groups": [] + }, + "CodesetId": 0, + "Age": { + "Value": 18, + "Extent": 64, + "Op": "bt" + } + } + }, + { + "ConditionOccurrence": { + "CorrelatedCriteria": { + "Type": "ALL", + "CriteriaList": [ + { + "Criteria": { + "ConditionOccurrence": { + "CodesetId": 0, + "Age": { + "Value": 18, + "Op": "gt" + } + } + }, + "StartWindow": { + "Start": { + "Coeff": -1 + }, + "End": { + "TimeUnitValue": 0, + "TimeUnit": "day", + "Coeff": -1 + }, + "UseEventEnd": false + }, + "Occurrence": { + "IsDistinct": true, + "Type": 2, + "Count": 1, + "CountColumn": "DOMAIN_CONCEPT" + } + }, + { + "Criteria": { + "ConditionOccurrence": { + "CodesetId": 0, + "Age": { + "Value": 18, + "Op": "gt" + } + } + }, + "StartWindow": { + "Start": { + "Coeff": -1 + }, + "End": { + "TimeUnitValue": 0, + "TimeUnit": "day", + "Coeff": -1 + }, + "UseEventEnd": false + }, + "Occurrence": { + "IsDistinct": true, + "Type": 2, + "Count": 1, + "CountColumn": "START_DATE" + } + }, + { + "Criteria": { + "ConditionOccurrence": { + "Age": { + "Value": 64, + "Op": "lt" + } + } + }, + "StartWindow": { + "Start": { + "TimeUnitValue": 0, + "TimeUnit": "day", + "Coeff": -1 + }, + "End": { + "Coeff": 1 + }, + "UseEventEnd": false + }, + "Occurrence": { + "IsDistinct": true, + "Type": 2, + "Count": 1, + "CountColumn": "VISIT_ID" + } + } + ], + "DemographicCriteriaList": [], + "Groups": [] + }, + "CodesetId": 0 + } + } + ], + "ObservationWindow": { + "PriorDays": 0, + "PostDays": 0 + }, + "PrimaryCriteriaLimit": { + "Type": "First" + } + }, + "QualifiedLimit": { + "Type": "First" + }, + "ExpressionLimit": { + "Type": "First" + }, + "InclusionRules": [], + "CensoringCriteria": [], + "CollapseSettings": { + "CollapseType": "ERA", + "EraPadUnit": "day", + "EraPadUnitValue": 0 + }, + "CensorWindow": {}, + "cdmVersionRange": ">=5.0.0" +} \ No newline at end of file diff --git a/src/test/resources/printfriendly/customEraExit.json b/src/test/resources/printfriendly/customEraExit.json index 06015aa7..999ba2f7 100644 --- a/src/test/resources/printfriendly/customEraExit.json +++ b/src/test/resources/printfriendly/customEraExit.json @@ -43,7 +43,11 @@ "CustomEra": { "DrugCodesetId": 0, "GapDays": 14, + "GapUnit": "day", + "GapUnitValue": 14, "Offset": 1, + "OffsetUnit": "day", + "OffsetUnitValue": 1, "DaysSupplyOverride": 7 } }, diff --git a/src/test/resources/printfriendly/customEraExitHour.json b/src/test/resources/printfriendly/customEraExitHour.json new file mode 100644 index 00000000..adc2d8c4 --- /dev/null +++ b/src/test/resources/printfriendly/customEraExitHour.json @@ -0,0 +1,60 @@ +{ + "ConceptSets": [ + { + "id": 0, + "name": "Concept Set 1", + "expression": { + "items": [] + } + }, + { + "id": 1, + "name": "Concept Set 2", + "expression": { + "items": [] + } + }, + { + "id": 2, + "name": "Concept Set 3", + "expression": { + "items": [] + } + } + ], + "PrimaryCriteria": { + "CriteriaList": [], + "ObservationWindow": { + "PriorDays": 0, + "PostDays": 0 + }, + "PrimaryCriteriaLimit": { + "Type": "First" + } + }, + "QualifiedLimit": { + "Type": "First" + }, + "ExpressionLimit": { + "Type": "First" + }, + "InclusionRules": [], + "EndStrategy": { + "CustomEra": { + "DrugCodesetId": 0, + "GapUnitValue": 336, + "GapUnit": "hour", + "OffsetUnitValue": 24, + "OffsetUnit": "hour", + "DaysSupplyOverride": 168 + } + }, + "CensoringCriteria": [], + "CollapseSettings": { + "CollapseType": "ERA", + "EraPadUnit": "hour", + "EraPadUnitValue": 0 + }, + "CensorWindow": {}, + "cdmVersionRange": ">=5.0.0" +} \ No newline at end of file diff --git a/src/test/resources/printfriendly/customEraExitMinute.json b/src/test/resources/printfriendly/customEraExitMinute.json new file mode 100644 index 00000000..55001076 --- /dev/null +++ b/src/test/resources/printfriendly/customEraExitMinute.json @@ -0,0 +1,60 @@ +{ + "ConceptSets": [ + { + "id": 0, + "name": "Concept Set 1", + "expression": { + "items": [] + } + }, + { + "id": 1, + "name": "Concept Set 2", + "expression": { + "items": [] + } + }, + { + "id": 2, + "name": "Concept Set 3", + "expression": { + "items": [] + } + } + ], + "PrimaryCriteria": { + "CriteriaList": [], + "ObservationWindow": { + "PriorDays": 0, + "PostDays": 0 + }, + "PrimaryCriteriaLimit": { + "Type": "First" + } + }, + "QualifiedLimit": { + "Type": "First" + }, + "ExpressionLimit": { + "Type": "First" + }, + "InclusionRules": [], + "EndStrategy": { + "CustomEra": { + "DrugCodesetId": 0, + "GapUnitValue": 20160, + "GapUnit": "minute", + "OffsetUnitValue": 1440, + "OffsetUnit": "minute", + "DaysSupplyOverride": 10080 + } + }, + "CensoringCriteria": [], + "CollapseSettings": { + "CollapseType": "ERA", + "EraPadUnit": "minute", + "EraPadUnitValue": 0 + }, + "CensorWindow": {}, + "cdmVersionRange": ">=5.0.0" +} \ No newline at end of file diff --git a/src/test/resources/printfriendly/customEraExitSecond.json b/src/test/resources/printfriendly/customEraExitSecond.json new file mode 100644 index 00000000..6817a66b --- /dev/null +++ b/src/test/resources/printfriendly/customEraExitSecond.json @@ -0,0 +1,60 @@ +{ + "ConceptSets": [ + { + "id": 0, + "name": "Concept Set 1", + "expression": { + "items": [] + } + }, + { + "id": 1, + "name": "Concept Set 2", + "expression": { + "items": [] + } + }, + { + "id": 2, + "name": "Concept Set 3", + "expression": { + "items": [] + } + } + ], + "PrimaryCriteria": { + "CriteriaList": [], + "ObservationWindow": { + "PriorDays": 0, + "PostDays": 0 + }, + "PrimaryCriteriaLimit": { + "Type": "First" + } + }, + "QualifiedLimit": { + "Type": "First" + }, + "ExpressionLimit": { + "Type": "First" + }, + "InclusionRules": [], + "EndStrategy": { + "CustomEra": { + "DrugCodesetId": 0, + "GapUnitValue": 1209600, + "GapUnit": "second", + "OffsetUnitValue": 86400, + "OffsetUnit": "second", + "DaysSupplyOverride": 604800 + } + }, + "CensoringCriteria": [], + "CollapseSettings": { + "CollapseType": "ERA", + "EraPadUnit": "second", + "EraPadUnitValue": 0 + }, + "CensorWindow": {}, + "cdmVersionRange": ">=5.0.0" +} \ No newline at end of file diff --git a/src/test/resources/windowcriteria/windowConditionOccurrence_PREP.json b/src/test/resources/windowcriteria/windowConditionOccurrence_PREP.json index 9878b393..2b315b06 100644 --- a/src/test/resources/windowcriteria/windowConditionOccurrence_PREP.json +++ b/src/test/resources/windowcriteria/windowConditionOccurrence_PREP.json @@ -14,6 +14,7 @@ "person_id":1, "condition_concept_id":2, "condition_start_date":"2000-01-01", + "condition_start_datetime":"2000-01-01 00:00:00", "condition_type_concept_id":0 }, { @@ -21,6 +22,7 @@ "person_id":1, "condition_concept_id":2, "condition_start_date":"2005-01-01", + "condition_start_datetime":"2005-01-01 00:00:00", "condition_type_concept_id":0 }, { @@ -28,6 +30,7 @@ "person_id":1, "condition_concept_id":2, "condition_start_date":"2005-02-01", + "condition_start_datetime":"2005-02-01 00:00:00", "condition_type_concept_id":0 }, { @@ -35,6 +38,7 @@ "person_id":1, "condition_concept_id":2, "condition_start_date":"2005-03-01", + "condition_start_datetime":"2005-03-01 00:00:00", "condition_type_concept_id":0 }, { @@ -42,6 +46,7 @@ "person_id":1, "condition_concept_id":2, "condition_start_date":"2005-04-01", + "condition_start_datetime":"2005-04-01 00:00:00", "condition_type_concept_id":0 } ], diff --git a/src/test/resources/windowcriteria/windowDeathTimeInterval_PREP.json b/src/test/resources/windowcriteria/windowDeathTimeInterval_PREP.json new file mode 100644 index 00000000..fda75392 --- /dev/null +++ b/src/test/resources/windowcriteria/windowDeathTimeInterval_PREP.json @@ -0,0 +1,65 @@ +{ + "cdm.person": [ + { + "person_id": 1, + "gender_concept_id": 0, + "year_of_birth": 0, + "race_concept_id": 0, + "ethnicity_concept_id": 0 + } + ], + "cdm.death": [ + { + "person_id": 1, + "cause_concept_id": 2, + "death_type_concept_id": 0, + "death_date": "2000-01-01", + "death_datetime": "2000-01-01 00:00:00" + }, + { + "person_id": 1, + "cause_concept_id": 2, + "death_type_concept_id": 0, + "death_date": "2005-01-01", + "death_datetime": "2005-01-01 00:00:00" + }, + { + "person_id": 1, + "cause_concept_id": 2, + "death_type_concept_id": 0, + "death_date": "2005-02-01", + "death_datetime": "2005-02-01 00:00:00" + }, + { + "person_id": 1, + "cause_concept_id": 2, + "death_type_concept_id": 0, + "death_date": "2005-03-01", + "death_datetime": "2005-03-01 00:00:00" + }, + { + "person_id": 1, + "cause_concept_id": 2, + "death_type_concept_id": 0, + "death_date": "2005-04-01", + "death_datetime": "2005-04-01 00:00:00" + } + ], + "cdm.observation_period": [ + { + "observation_period_id": 1, + "person_id": 1, + "observation_period_start_date": "2000-01-01", + "observation_period_end_date": "2010-01-01", + "period_type_concept_id": 0 + } + ], + "window_death.cohort": [ + { + "cohort_definition_id": 1, + "subject_id": 1, + "cohort_start_date": "2006-01-01", + "cohort_end_date": "2006-01-01" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/windowcriteria/windowDeathTimeInterval_VERIFY.json b/src/test/resources/windowcriteria/windowDeathTimeInterval_VERIFY.json new file mode 100644 index 00000000..d3212c46 --- /dev/null +++ b/src/test/resources/windowcriteria/windowDeathTimeInterval_VERIFY.json @@ -0,0 +1,54 @@ +{ + "window_death.no_columns": [ + { + "person_id": 1, + "event_id": 1 + }, + { + "person_id": 1, + "event_id": 1 + }, + { + "person_id": 1, + "event_id": 1 + }, + { + "person_id": 1, + "event_id": 1 + } + ], + "window_death.add_columns": [ + { + "person_id": 1, + "event_id": 1, + "start_date": "2005-01-01", + "end_date": "2005-01-01 00:00:00.0", + "domain_concept_id": 2, + "duration": 1 + }, + { + "person_id": 1, + "event_id": 1, + "start_date": "2005-02-01", + "end_date": "2005-02-01 00:00:00.0", + "domain_concept_id": 2, + "duration": 1 + }, + { + "person_id": 1, + "event_id": 1, + "start_date": "2005-03-01", + "end_date": "2005-03-01 00:00:00.0", + "domain_concept_id": 2, + "duration": 1 + }, + { + "person_id": 1, + "event_id": 1, + "start_date": "2005-04-01", + "end_date": "2005-04-01 00:00:00.0", + "domain_concept_id": 2, + "duration": 1 + } + ] +} \ No newline at end of file diff --git a/src/test/resources/windowcriteria/windowDeviceExposureTimeInterval_PREP.json b/src/test/resources/windowcriteria/windowDeviceExposureTimeInterval_PREP.json new file mode 100644 index 00000000..aa89f07c --- /dev/null +++ b/src/test/resources/windowcriteria/windowDeviceExposureTimeInterval_PREP.json @@ -0,0 +1,79 @@ +{ + "cdm.person": [ + { + "person_id": 1, + "gender_concept_id": 0, + "year_of_birth": 0, + "race_concept_id": 0, + "ethnicity_concept_id": 0 + } + ], + "cdm.device_exposure": [ + { + "device_exposure_id": 1, + "person_id": 1, + "device_concept_id": 2, + "device_type_concept_id": 0, + "device_exposure_start_date": "2000-01-01", + "device_exposure_start_datetime": "2000-01-01 00:00:00", + "quantity": 1 + }, + { + "device_exposure_id": 2, + "person_id": 1, + "device_concept_id": 2, + "device_type_concept_id": 0, + "device_exposure_start_date": "2005-01-01", + "device_exposure_start_datetime": "2005-01-01 00:00:00", + "device_exposure_end_datetime": "2005-01-01 00:30:00", + "quantity": 2 + }, + { + "device_exposure_id": 3, + "person_id": 1, + "device_concept_id": 2, + "device_type_concept_id": 0, + "device_exposure_start_date": "2005-02-01", + "device_exposure_start_datetime": "2005-02-01 00:00:00", + "device_exposure_end_datetime": "2005-02-01 01:00:00", + "quantity": 3 + }, + { + "device_exposure_id": 4, + "person_id": 1, + "device_concept_id": 2, + "device_type_concept_id": 0, + "device_exposure_start_date": "2005-03-01", + "device_exposure_start_datetime": "2005-03-01 00:00:00", + "device_exposure_end_datetime": "2005-03-02 00:00:00", + "quantity": 4 + }, + { + "device_exposure_id": 5, + "person_id": 1, + "device_concept_id": 2, + "device_type_concept_id": 0, + "device_exposure_start_date": "2005-04-01", + "device_exposure_start_datetime": "2005-04-01 00:00:00", + "device_exposure_end_datetime": "2005-05-01 00:00:00", + "quantity": 5 + } + ], + "cdm.observation_period": [ + { + "observation_period_id": 1, + "person_id": 1, + "observation_period_start_date": "2000-01-01", + "observation_period_end_date": "2010-01-01", + "period_type_concept_id": 0 + } + ], + "window_device_exposure.cohort": [ + { + "cohort_definition_id": 1, + "subject_id": 1, + "cohort_start_date": "2006-01-01", + "cohort_end_date": "2006-01-01" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/windowcriteria/windowDeviceExposureTimeInterval_VERIFY.json b/src/test/resources/windowcriteria/windowDeviceExposureTimeInterval_VERIFY.json new file mode 100644 index 00000000..910656fb --- /dev/null +++ b/src/test/resources/windowcriteria/windowDeviceExposureTimeInterval_VERIFY.json @@ -0,0 +1,58 @@ +{ + "window_device_exposure.no_columns": [ + { + "person_id": 1, + "event_id": 1 + }, + { + "person_id": 1, + "event_id": 1 + }, + { + "person_id": 1, + "event_id": 1 + }, + { + "person_id": 1, + "event_id": 1 + } + ], + "window_device_exposure.add_columns": [ + { + "person_id": 1, + "event_id": 1, + "start_date": "2005-01-01 00:00:00", + "end_date": "2005-01-01 00:30:00", + "domain_concept_id": 2, + "quantity": 2, + "duration": 0.5 + }, + { + "person_id": 1, + "event_id": 1, + "start_date": "2005-02-01 00:00:00", + "end_date": "2005-02-01 01:00:00", + "domain_concept_id": 2, + "quantity": 3, + "duration": 1 + }, + { + "person_id": 1, + "event_id": 1, + "start_date": "2005-03-01", + "end_date": "2005-03-02", + "domain_concept_id": 2, + "quantity": 4, + "duration": 24 + }, + { + "person_id": 1, + "event_id": 1, + "start_date": "2005-04-01 00:00:00", + "end_date": "2005-05-01 00:00:00", + "domain_concept_id": 2, + "quantity": 5, + "duration": 720 + } + ] +} \ No newline at end of file diff --git a/src/test/resources/windowcriteria/windowDrugExposureTimeInterval_PREP.json b/src/test/resources/windowcriteria/windowDrugExposureTimeInterval_PREP.json new file mode 100644 index 00000000..dd430dfe --- /dev/null +++ b/src/test/resources/windowcriteria/windowDrugExposureTimeInterval_PREP.json @@ -0,0 +1,95 @@ +{ + "cdm.person": [ + { + "person_id": 1, + "gender_concept_id": 0, + "year_of_birth": 0, + "race_concept_id": 0, + "ethnicity_concept_id": 0 + } + ], + "cdm.drug_exposure": [ + { + "drug_exposure_id": 1, + "person_id": 1, + "drug_concept_id": 2, + "drug_type_concept_id": 0, + "refills": 1, + "quantity": 10, + "days_supply": 3, + "drug_exposure_start_date": "2000-01-01", + "drug_exposure_start_datetime": "2000-01-01 00:00:00", + "drug_exposure_end_date": "2000-01-01", + "drug_exposure_end_datetime": "2000-01-01 10:00:00" + }, + { + "drug_exposure_id": 2, + "person_id": 1, + "drug_concept_id": 2, + "drug_type_concept_id": 0, + "refills": 2, + "quantity": 11, + "days_supply": 6, + "drug_exposure_start_date": "2005-01-01", + "drug_exposure_start_datetime": "2005-01-01 00:00:00", + "drug_exposure_end_date": "2005-01-07", + "drug_exposure_end_datetime": "2005-01-01 00:10:00" + }, + { + "drug_exposure_id": 3, + "person_id": 1, + "drug_concept_id": 2, + "drug_type_concept_id": 0, + "refills": 3, + "quantity": 12, + "days_supply": 9, + "drug_exposure_start_date": "2005-02-01", + "drug_exposure_start_datetime": "2005-02-01 00:00:00", + "drug_exposure_end_date": "2005-02-07", + "drug_exposure_end_datetime": "2005-02-02 23:00:00" + }, + { + "drug_exposure_id": 4, + "person_id": 1, + "drug_concept_id": 2, + "drug_type_concept_id": 0, + "refills": 4, + "quantity": 13, + "days_supply": 12, + "drug_exposure_start_date": "2005-03-01", + "drug_exposure_start_datetime": "2005-03-01 00:00:00", + "drug_exposure_end_date": "2005-03-07", + "drug_exposure_end_datetime": "2005-03-01 01:00:00" + }, + { + "drug_exposure_id": 5, + "person_id": 1, + "drug_concept_id": 2, + "drug_type_concept_id": 0, + "refills": 5, + "quantity": 14, + "days_supply": 15, + "drug_exposure_start_date": "2005-04-01", + "drug_exposure_start_datetime": "2005-04-01 00:00:00", + "drug_exposure_end_date": "2005-04-07", + "drug_exposure_end_datetime": "2005-04-07 00:00:00" + } + ], + "cdm.observation_period": [ + { + "observation_period_id": 1, + "person_id": 1, + "observation_period_start_date": "2000-01-01", + "observation_period_end_date": "2010-01-01", + "period_type_concept_id": 0 + } + ], + "window_drug_exposure.cohort": [ + { + "cohort_definition_id": 1, + "subject_id": 1, + "cohort_start_date": "2006-01-01", + "cohort_end_date": "2006-01-01" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/windowcriteria/windowDrugExposureTimeInterval_VERIFY.json b/src/test/resources/windowcriteria/windowDrugExposureTimeInterval_VERIFY.json new file mode 100644 index 00000000..0af1f199 --- /dev/null +++ b/src/test/resources/windowcriteria/windowDrugExposureTimeInterval_VERIFY.json @@ -0,0 +1,66 @@ +{ + "window_drug_exposure.no_columns": [ + { + "person_id": 1, + "event_id": 1 + }, + { + "person_id": 1, + "event_id": 1 + }, + { + "person_id": 1, + "event_id": 1 + }, + { + "person_id": 1, + "event_id": 1 + } + ], + "window_drug_exposure.add_columns": [ + { + "person_id": 1, + "event_id": 1, + "start_date": "2005-01-01 00:00:00", + "end_date": "2005-01-01 00:10:00", + "domain_concept_id": 2, + "refills": 2, + "quantity": 11, + "days_supply": 6, + "duration": 10 + }, + { + "person_id": 1, + "event_id": 1, + "start_date": "2005-02-01 00:00:00", + "end_date": "2005-02-02 23:00:00", + "domain_concept_id": 2, + "refills": 3, + "quantity": 12, + "days_supply": 9, + "duration": 2820 + }, + { + "person_id": 1, + "event_id": 1, + "start_date": "2005-03-01 00:00:00", + "end_date": "2005-03-01 01:00:00", + "domain_concept_id": 2, + "refills": 4, + "quantity": 13, + "days_supply": 12, + "duration": 60 + }, + { + "person_id": 1, + "event_id": 1, + "start_date": "2005-04-01 00:00:00", + "end_date": "2005-04-07 00:00:00", + "domain_concept_id": 2, + "refills": 5, + "quantity": 14, + "days_supply": 15, + "duration": 8640 + } + ] +} \ No newline at end of file diff --git a/src/test/resources/windowcriteria/windowVisitDetailTimeInterval_PREP.json b/src/test/resources/windowcriteria/windowVisitDetailTimeInterval_PREP.json new file mode 100644 index 00000000..99c37539 --- /dev/null +++ b/src/test/resources/windowcriteria/windowVisitDetailTimeInterval_PREP.json @@ -0,0 +1,125 @@ +{ + "cdm.person": [ + { + "person_id":1, + "gender_concept_id":0, + "year_of_birth":0, + "race_concept_id":0, + "ethnicity_concept_id":0 + } + ], + "cdm.visit_occurrence": [ + { + "visit_occurrence_id": 1, + "person_id":1, + "visit_concept_id":2, + "visit_type_concept_id":0, + "visit_start_date":"2000-01-01", + "visit_end_date":"2000-01-07" + }, + { + "visit_occurrence_id": 2, + "person_id":1, + "visit_concept_id":2, + "visit_type_concept_id":0, + "visit_start_date":"2005-01-01", + "visit_end_date":"2005-01-07" + }, + { + "visit_occurrence_id": 3, + "person_id":1, + "visit_concept_id":2, + "visit_type_concept_id":0, + "visit_start_date":"2005-02-01", + "visit_end_date":"2005-02-07" + }, + { + "visit_occurrence_id": 4, + "person_id":1, + "visit_concept_id":2, + "visit_type_concept_id":0, + "visit_start_date":"2005-03-01", + "visit_end_date":"2005-03-07" + }, + { + "visit_occurrence_id": 5, + "person_id":1, + "visit_concept_id":2, + "visit_type_concept_id":0, + "visit_start_date":"2005-04-01", + "visit_end_date":"2005-04-07" + } + ], + "cdm.visit_detail": [ + { + "visit_detail_id": 1, + "person_id":1, + "visit_detail_concept_id":2, + "visit_detail_type_concept_id":0, + "visit_detail_start_date":"2000-01-01", + "visit_detail_end_date":"2000-01-07", + "visit_occurrence_id": 1 + }, + { + "visit_detail_id": 2, + "person_id":1, + "visit_detail_concept_id":2, + "visit_detail_type_concept_id":0, + "visit_detail_start_date":"2005-01-01", + "visit_detail_start_datetime":"2005-01-01 00:00:00", + "visit_detail_end_date":"2005-01-07", + "visit_detail_end_datetime":"2005-01-01 00:00:05", + "visit_occurrence_id": 2 + }, + { + "visit_detail_id": 3, + "person_id":1, + "visit_detail_concept_id":2, + "visit_detail_type_concept_id":0, + "visit_detail_start_date":"2005-02-01", + "visit_detail_start_datetime":"2005-02-01 00:00:00", + "visit_detail_end_date":"2005-02-07", + "visit_detail_end_datetime":"2005-02-01 00:05:00", + "visit_occurrence_id": 3 + }, + { + "visit_detail_id": 4, + "person_id":1, + "visit_detail_concept_id":2, + "visit_detail_type_concept_id":0, + "visit_detail_start_date":"2005-03-01", + "visit_detail_start_datetime":"2005-03-01 00:00:00", + "visit_detail_end_date":"2005-03-07", + "visit_detail_end_datetime":"2005-03-01 05:00:00", + "visit_occurrence_id": 4 + }, + { + "visit_detail_id": 5, + "person_id":1, + "visit_detail_concept_id":2, + "visit_detail_type_concept_id":0, + "visit_detail_start_date":"2005-04-01", + "visit_detail_start_datetime":"2005-04-01 00:00:00", + "visit_detail_end_date":"2005-04-07", + "visit_detail_end_datetime":"2005-04-05 00:00:00", + "visit_occurrence_id": 5 + } + ], + "cdm.observation_period" : [ + { + "observation_period_id": 1, + "person_id":1, + "observation_period_start_date":"2000-01-01", + "observation_period_end_date":"2010-01-01", + "period_type_concept_id": 0 + } + ], + "window_visit_detail.cohort" : [ + { + "cohort_definition_id": 1, + "subject_id":1, + "cohort_start_date":"2006-01-01", + "cohort_end_date":"2006-01-01" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/windowcriteria/windowVisitDetailTimeInterval_VERIFY.json b/src/test/resources/windowcriteria/windowVisitDetailTimeInterval_VERIFY.json new file mode 100644 index 00000000..14ce5d32 --- /dev/null +++ b/src/test/resources/windowcriteria/windowVisitDetailTimeInterval_VERIFY.json @@ -0,0 +1,54 @@ +{ + "window_visit_detail.no_columns": [ + { + "person_id": 1, + "event_id": 1 + }, + { + "person_id": 1, + "event_id": 1 + }, + { + "person_id": 1, + "event_id": 1 + }, + { + "person_id": 1, + "event_id": 1 + } + ], + "window_visit_detail.add_columns": [ + { + "person_id": 1, + "event_id": 1, + "start_date": "2005-01-01 00:00:00", + "end_date": "2005-01-01 00:00:05", + "domain_concept_id": 2, + "duration": 5 + }, + { + "person_id": 1, + "event_id": 1, + "start_date": "2005-02-01 00:00:00", + "end_date": "2005-02-01 00:05:00", + "domain_concept_id": 2, + "duration": 300 + }, + { + "person_id": 1, + "event_id": 1, + "start_date": "2005-03-01 00:00:00", + "end_date": "2005-03-01 05:00:00", + "domain_concept_id": 2, + "duration": 18000 + }, + { + "person_id": 1, + "event_id": 1, + "start_date": "2005-04-01 00:00:00", + "end_date": "2005-04-05 00:00:00", + "domain_concept_id": 2, + "duration": 345600 + } + ] +} \ No newline at end of file