Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ Suggests:
markdown,
shiny,
ggplot2,
RSQLite,
Eunomia (>= 2.0.0),
R.utils,
devtools
Expand Down
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# DataQualityDashboard (unreleased)
This release adds support for OMOP CDM v5.5, drops support for OMOP CDM v5.2, consolidates the concept level threshold files, and refreshes the CDM documentation columns in the threshold files.

- Added `8647` (per microliter, `/uL`) to the plausible units for blood neutrophil count concept `3017732` (LOINC `26499-4`), fixing false unit violations (#698).

### Removed CDM v5.2 support

**`cdmVersion = "5.2"` is no longer accepted, and the `OMOP_CDMv5.2_Table_Level.csv`, `OMOP_CDMv5.2_Field_Level.csv`, and `OMOP_CDMv5.2_Check_Descriptions.csv` files have been removed.** CDM v5.2 is no longer published in the [CommonDataModel](https://github.com/OHDSI/CommonDataModel) repository, so its threshold files could not be kept in step with the specification, and the package was never tested against it. Sites still on v5.2 should either remain on an earlier release of this package or pass their own threshold files via `tableCheckThresholdLoc` and `fieldCheckThresholdLoc`.
Expand Down
2 changes: 1 addition & 1 deletion inst/csv/OMOP_CDM_Concept_Level.csv
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@ MEASUREMENT,MEASUREMENT_CONCEPT_ID,3013429,Basophils [#/volume] in Blood by Auto
MEASUREMENT,MEASUREMENT_CONCEPT_ID,3023599,MCV [Entitic volume] by Automated count,,,,,,,,8583,5,
MEASUREMENT,MEASUREMENT_CONCEPT_ID,3036588,Neutrophil cytoplasmic Ab.perinuclear [Presence] in Serum,,,,,,,,"8525,-1",5,
MEASUREMENT,MEASUREMENT_CONCEPT_ID,4298431,White blood cell count,,,,,,,,"8848,8961,9444,8784,8785",5,
MEASUREMENT,MEASUREMENT_CONCEPT_ID,3017732,Neutrophils [#/volume] in Blood,,,,,,,,"8848,8961,9444",5,
MEASUREMENT,MEASUREMENT_CONCEPT_ID,3017732,Neutrophils [#/volume] in Blood,,,,,,,,"8848,8961,9444,8647",5,
MEASUREMENT,MEASUREMENT_CONCEPT_ID,3024561,Albumin [Mass/volume] in Serum or Plasma,,,,,,,,"8636,8713",5,
MEASUREMENT,MEASUREMENT_CONCEPT_ID,3034639,Hemoglobin A1c [Mass/volume] in Blood,,,,,,,,"8713,8840,9579,8923",5,
MEASUREMENT,MEASUREMENT_CONCEPT_ID,3013650,Neutrophils [#/volume] in Blood by Automated count,,,,,,,,"8784,8848,8961,9444,8647,8785",5,
Expand Down
115 changes: 115 additions & 0 deletions tests/testthat/test-plausibleUnitConceptIds.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
library(testthat)

plausibleUnitThresholdFiles <- list.files(
system.file("csv", package = "DataQualityDashboard"),
pattern = "^OMOP_CDM(v5\\.[2-5])?_Concept_Level\\.csv$"
)

runNeutrophilUnitCheck <- function(thresholdFile, measurementConceptIds, unitConceptIds) {
conceptChecks <- .readThresholdFile("default", thresholdFile)
check <- conceptChecks[
conceptChecks$cdmTableName == "MEASUREMENT" &
conceptChecks$cdmFieldName == "MEASUREMENT_CONCEPT_ID" &
conceptChecks$conceptId == 3017732,
]
expect_equal(nrow(check), 1, info = thresholdFile)

databaseFile <- tempfile(fileext = ".sqlite")
on.exit(unlink(databaseFile))
connectionDetails <- DatabaseConnector::createConnectionDetails(
dbms = "sqlite",
server = databaseFile
)
connection <- DatabaseConnector::connect(connectionDetails)
on.exit({
DatabaseConnector::disconnect(connection)
unlink(databaseFile)
})
DatabaseConnector::executeSql(connection, "
CREATE TABLE measurement (
measurement_concept_id INTEGER,
unit_concept_id INTEGER
);
")
if (length(measurementConceptIds) > 0) {
values <- sprintf(
"(%i, %s)",
measurementConceptIds,
ifelse(is.na(unitConceptIds), "NULL", unitConceptIds)
)
DatabaseConnector::executeSql(
connection,
paste0("INSERT INTO measurement VALUES ", paste(values, collapse = ", "), ";")
)
}

sql <- SqlRender::loadRenderTranslateSql(
sqlFilename = "concept_plausible_unit_concept_ids.sql",
packageName = "DataQualityDashboard",
dbms = "sqlite",
warnOnMissingParameters = FALSE,
cdmDatabaseSchema = "main",
cdmTableName = check$cdmTableName,
cdmFieldName = check$cdmFieldName,
conceptId = check$conceptId,
plausibleUnitConceptIds = check$plausibleUnitConceptIds,
cohort = FALSE
)
DatabaseConnector::querySql(connection, sql, snakeCaseToCamelCase = TRUE)
}

test_that("blood neutrophil counts accept /uL and reject unrelated units (#698)", {
skip_if_not_installed("RSQLite")
expect_gt(length(plausibleUnitThresholdFiles), 0)
for (thresholdFile in plausibleUnitThresholdFiles) {
result <- runNeutrophilUnitCheck(thresholdFile, 3017732L, 8647L)
expect_equal(result$numViolatedRows, 0, info = thresholdFile)
expect_equal(result$numDenominatorRows, 1, info = thresholdFile)
expect_equal(result$pctViolatedRows, 0, info = thresholdFile)

result <- runNeutrophilUnitCheck(
thresholdFile,
rep(3017732L, 4),
c(8848L, 8961L, 9444L, 8554L)
)
expect_equal(result$numViolatedRows, 1, info = thresholdFile)
expect_equal(result$numDenominatorRows, 4, info = thresholdFile)
expect_equal(result$pctViolatedRows, 1 / 4, info = thresholdFile)
}
})

test_that("neutrophil unit checks preserve zero and NULL denominator behavior", {
skip_if_not_installed("RSQLite")
expect_gt(length(plausibleUnitThresholdFiles), 0)
for (thresholdFile in plausibleUnitThresholdFiles) {
# A different measurement concept must not affect either count.
result <- runNeutrophilUnitCheck(
thresholdFile,
c(3017732L, 3000000L),
c(0L, 8554L)
)
expect_equal(result$numViolatedRows, 0, info = thresholdFile)
expect_equal(result$numDenominatorRows, 0, info = thresholdFile)
expect_equal(result$pctViolatedRows, 0, info = thresholdFile)

result <- runNeutrophilUnitCheck(
thresholdFile,
c(3017732L, 3000000L),
c(NA_integer_, 8554L)
)
expect_equal(result$numViolatedRows, 0, info = thresholdFile)
expect_equal(result$numDenominatorRows, 1, info = thresholdFile)
expect_equal(result$pctViolatedRows, 0, info = thresholdFile)
}
})

test_that("neutrophil unit checks return zero for an empty measurement table", {
skip_if_not_installed("RSQLite")
expect_gt(length(plausibleUnitThresholdFiles), 0)
for (thresholdFile in plausibleUnitThresholdFiles) {
result <- runNeutrophilUnitCheck(thresholdFile, integer(), integer())
expect_equal(result$numViolatedRows, 0, info = thresholdFile)
expect_equal(result$numDenominatorRows, 0, info = thresholdFile)
expect_equal(result$pctViolatedRows, 0, info = thresholdFile)
}
})