diff --git a/R/ds.asList.R b/R/ds.asList.R index fb3fbdbc..516d51e3 100644 --- a/R/ds.asList.R +++ b/R/ds.asList.R @@ -67,7 +67,6 @@ ds.asList <- function(x.name=NULL, newobj=NULL, datasources=NULL){ newobj <- "aslist.newobj" } - calltext <- call("asListDS", x.name, newobj) - out.message <- DSI::datashield.aggregate(datasources, calltext) + datashield.assign(datasources, newobj, call("asListDS", x.name)) } diff --git a/R/ds.changeRefGroup.R b/R/ds.changeRefGroup.R index ae06bd6b..142ba930 100644 --- a/R/ds.changeRefGroup.R +++ b/R/ds.changeRefGroup.R @@ -130,7 +130,7 @@ ds.changeRefGroup <- function(x=NULL, ref=NULL, newobj=NULL, reorderByRef=FALSE, } # call the server side function that will recode the levels - calltext <- call("changeRefGroupDS", x, ref, reorderByRef) + calltext <- call("changeRefGroupDS", x, as.character(ref), reorderByRef) DSI::datashield.assign(datasources, newobj, calltext) } diff --git a/R/ds.cor.R b/R/ds.cor.R index 8d068beb..6043a511 100644 --- a/R/ds.cor.R +++ b/R/ds.cor.R @@ -100,6 +100,10 @@ ds.cor <- function(x=NULL, y=NULL, type="split", datasources=NULL, classConsiste .checkClassConsistency(output) } + if(!is.null(y) && any(output[[1]]$class %in% c("matrix", "data.frame"))){ + warning("x is a matrix or a dataframe; y will be ignored and a correlation matrix computed for x!") + } + if (type=="split"){ covariance <- list() sqrt.diag <- list() diff --git a/R/ds.cov.R b/R/ds.cov.R index 1fdf57f2..a35c9097 100644 --- a/R/ds.cov.R +++ b/R/ds.cov.R @@ -117,6 +117,10 @@ ds.cov <- function(x=NULL, y=NULL, naAction='pairwise.complete', type="split", d .checkClassConsistency(output) } + if(!is.null(y) && any(output[[1]]$class %in% c("matrix", "data.frame"))){ + warning("x is a matrix or a dataframe; y will be ignored and a covariance matrix computed for x!") + } + if (type=="split"){ covariance <- list() results <- list() diff --git a/R/ds.dim.R b/R/ds.dim.R index 6778a646..efafd586 100644 --- a/R/ds.dim.R +++ b/R/ds.dim.R @@ -64,14 +64,14 @@ #' # Calculate the dimension #' ds.dim(x="D", #' type="combine", #global dimension -#'#' datasources = connections)#all servers are used +#' datasources = connections)#all servers are used #' ds.dim(x="D", #' type = "both",#separate dimension for each study #' #and the pooled dimension (default) -#'#' datasources = connections)#all servers are used +#' datasources = connections)#all servers are used #' ds.dim(x="D", #' type="split", #separate dimension for each study -#'#' datasources = connections[1])#only the first server is used ("study1") +#' datasources = connections[1])#only the first server is used ("study1") #' #' # clear the Datashield R sessions and logout #' datashield.logout(connections) diff --git a/R/ds.glmSummary.R b/R/ds.glmSummary.R index 22ae6a96..07747584 100644 --- a/R/ds.glmSummary.R +++ b/R/ds.glmSummary.R @@ -95,7 +95,6 @@ ds.glmSummary <- function(x.name, newobj=NULL, datasources=NULL) { calltext1 <- call("glmSummaryDS.as", x.name) DSI::datashield.assign(datasources,newobj,calltext1) - # LOOK BELOW CLIENTSIDE MODULE FOR NEXT BLOCK OF CODE # PREPARE AND CALL THE SECOND ASSIGN FUNCTION TO PREPARE AN ABBREVIATED # summary_glm OBJECT ON THE SERVERSIDE THAT CAN SAFELY BE RETURNED TO CLIENT diff --git a/R/ds.mean.R b/R/ds.mean.R index 501c3773..accbc2c3 100644 --- a/R/ds.mean.R +++ b/R/ds.mean.R @@ -20,8 +20,7 @@ #' are saved in the server-side. #' Once the estimated means and \code{Nvalids} #' are written into the server-side R environments, they can be used directly to centralize -#' the variable of interest around its global mean or its study-specific means. Finally, -#' the \code{isDefined} internal function checks whether the key variables have been created. +#' the variable of interest around its global mean or its study-specific means. #' #' Server function called: \code{meanDS} #' @param x a character specifying the name of a numerical vector. diff --git a/R/ds.recodeValues.R b/R/ds.recodeValues.R index 81c352fb..4038c324 100644 --- a/R/ds.recodeValues.R +++ b/R/ds.recodeValues.R @@ -67,7 +67,7 @@ #' values2replace.vector = c(1,2), #' new.values.vector = c(1,2), #' missing = 99, -#' newobj = 'bmi_recoded' +#' newobj = 'bmi_recoded', #' datasources = connections) #' #' # Clear the Datashield R sessions and logout diff --git a/R/ds.table.R b/R/ds.table.R index ba6497d0..328925aa 100644 --- a/R/ds.table.R +++ b/R/ds.table.R @@ -1521,7 +1521,6 @@ if(num.table.dims==1) }#END second dim=1 loop - ################################ #NOW UNDERTAKE CHISQUARED TESTS# ################################ @@ -1532,17 +1531,13 @@ if(report.chisq.tests) #TABLES WITH 3 DIMENSIONS# ########################## -#Suppress.chisq.warnings by default -if(suppress.chisq.warnings) -{ -options(warn=-1) -} ################## #Combined studies# ################## if(num.table.dims==3){ +withCallingHandlers({ numtests<-dim(combine.array.all.sources)[num.table.dims] @@ -1593,11 +1588,7 @@ for(ns in numsources:1) eval(parse(text=chisq.list.text)) -#If warnings suppressed now return to default -if(suppress.chisq.warnings) -{ -options(warn=0) -} +}, warning = function(w) if (suppress.chisq.warnings) invokeRestart("muffleWarning")) return.list.second<-list(output.list=return.list.first,chisq.tests=chisq.tests,validity.message=validity.message) @@ -1616,17 +1607,13 @@ options(warn=0) ########################## #TABLES WITH 2 DIMENSIONS# ########################## -#Suppress.chisq.warnings by default -if(suppress.chisq.warnings) -{ -options(warn=-1) -} ################## #Combined studies# ################## if(num.table.dims==2){ +withCallingHandlers({ chisq.list.temp<-")" @@ -1664,11 +1651,7 @@ for(ns in numsources:1) }#END ns loop -#If warnings suppressed now return to default -if(suppress.chisq.warnings) -{ -options(warn=0) -} +}, warning = function(w) if (suppress.chisq.warnings) invokeRestart("muffleWarning")) eval(parse(text=chisq.list.text)) @@ -1687,13 +1670,9 @@ options(warn=0) ######################### #TABLES WITH 1 DIMENSION# ######################### -#Suppress.chisq.warnings by default -if(suppress.chisq.warnings) -{ -options(warn=-1) -} if(num.table.dims==1){ +withCallingHandlers({ #combined.studies.not.possible.as.one.column.only @@ -1748,11 +1727,7 @@ for(ns in numsources:1) } -#If warnings suppressed now return to default -if(suppress.chisq.warnings) -{ -options(warn=0) -} +}, warning = function(w) if (suppress.chisq.warnings) invokeRestart("muffleWarning")) }#END third dim=1 loop diff --git a/R/glmChecks.R b/R/glmChecks.R index 152b80bf..2610a630 100644 --- a/R/glmChecks.R +++ b/R/glmChecks.R @@ -56,7 +56,7 @@ glmChecks <- function(formula, data, offset, weights, datasources){ # check that each variable is defined and not empty and each study. Stop the process if any check fails stdnames <- names(datasources) for(i in 1:length(elts)){ - if(is.na(as.numeric(elts[i], options(warn=-1)))){ # making sure an eventual intercept term is not included in the checks + if(is.na(suppressWarnings(as.numeric(elts[i])))){ # making sure an eventual intercept term is not included in the checks message(paste0(" ", elts[i], "...")) for(j in 1: length(datasources)){ # check if the variable is defined on the server site diff --git a/man/ds.dim.Rd b/man/ds.dim.Rd index dacda6a6..74c53eff 100644 --- a/man/ds.dim.Rd +++ b/man/ds.dim.Rd @@ -78,14 +78,14 @@ Server function called: \code{dimDS} # Calculate the dimension ds.dim(x="D", type="combine", #global dimension -#' datasources = connections)#all servers are used + datasources = connections)#all servers are used ds.dim(x="D", type = "both",#separate dimension for each study #and the pooled dimension (default) -#' datasources = connections)#all servers are used + datasources = connections)#all servers are used ds.dim(x="D", type="split", #separate dimension for each study -#' datasources = connections[1])#only the first server is used ("study1") + datasources = connections[1])#only the first server is used ("study1") # clear the Datashield R sessions and logout datashield.logout(connections) diff --git a/man/ds.mean.Rd b/man/ds.mean.Rd index 2e6cc9f3..9817fec5 100644 --- a/man/ds.mean.Rd +++ b/man/ds.mean.Rd @@ -70,8 +70,7 @@ as well as the global equivalents across all studies combined are saved in the server-side. Once the estimated means and \code{Nvalids} are written into the server-side R environments, they can be used directly to centralize -the variable of interest around its global mean or its study-specific means. Finally, -the \code{isDefined} internal function checks whether the key variables have been created. +the variable of interest around its global mean or its study-specific means. Server function called: \code{meanDS} } diff --git a/man/ds.recodeValues.Rd b/man/ds.recodeValues.Rd index 7a602591..c1c85fa9 100644 --- a/man/ds.recodeValues.Rd +++ b/man/ds.recodeValues.Rd @@ -94,7 +94,7 @@ Server function called: \code{recodeValuesDS} values2replace.vector = c(1,2), new.values.vector = c(1,2), missing = 99, - newobj = 'bmi_recoded' + newobj = 'bmi_recoded', datasources = connections) # Clear the Datashield R sessions and logout diff --git a/tests/testthat/test-smk-ds.asList.R b/tests/testthat/test-smk-ds.asList.R index 9c359abf..efd24457 100644 --- a/tests/testthat/test-smk-ds.asList.R +++ b/tests/testthat/test-smk-ds.asList.R @@ -35,6 +35,44 @@ test_that("simple test", { expect_equal(res.class$sim3, "list") }) +test_that("no data is returned to the client", { + res <- ds.asList(x.name = "D$GENDER", newobj = "gender.list") + + expect_null(res) +}) + +test_that("a data.frame is written to the server as a named list of its columns", { + ds.asList(x.name = "D", newobj = "df.list") + + res.class <- ds.class("df.list") + for (study.class in res.class) { + expect_equal(study.class, "list") + } + + res.length <- ds.length("df.list", type = "split") + for (study.length in res.length) { + expect_equal(study.length, 1) + } + + res.names <- ds.names("df.list") + for (study.names in res.names) { + expect_equal(study.names, "GENDER") + } +}) + +test_that("fails if the object does not exist", { + expect_error( + ds.asList(x.name = "nonexistent_object"), + "There are some DataSHIELD errors, list them with datashield.errors()", + fixed = TRUE + ) + + res.errors <- DSI::datashield.errors() + for (study.errors in res.errors) { + expect_match(study.errors, "The server-side object 'nonexistent_object' does not exist") + } +}) + # # Done # @@ -42,7 +80,7 @@ test_that("simple test", { # context("ds.asList::smk::shutdown") test_that("stutdown", { - ds_expect_variables(c("D", "aslist.newobj")) + ds_expect_variables(c("D", "aslist.newobj", "gender.list", "df.list")) }) disconnect.studies.dataset.cnsim() diff --git a/tests/testthat/test-smk-ds.changeRefGroup.R b/tests/testthat/test-smk-ds.changeRefGroup.R index 416ed448..5c0964df 100644 --- a/tests/testthat/test-smk-ds.changeRefGroup.R +++ b/tests/testthat/test-smk-ds.changeRefGroup.R @@ -65,10 +65,22 @@ test_that("simple changeRefGroup", { # Done # +test_that("a numeric ref is a level name, not a position", { + ds.recodeValues(var.name = "D$PM_BMI_CATEGORICAL", values2replace.vector = c(1, 2, 3), new.values.vector = c(0, 2, 5), newobj = "bmi_codes") + ds.asFactorSimple(input.var.name = "bmi_codes", newobj.name = "bmi_codes_f") + + ds.changeRefGroup(x = "bmi_codes_f", ref = 5, newobj = "bmi_ref5") + + res.levels <- ds.levels(x = "bmi_ref5") + for (study.levels in res.levels) { + expect_equal(study.levels$Levels[1], "5") + } +}) + # context("ds.changeRefGroup::smk::shutdown") test_that("shutdown", { - ds_expect_variables(c("D", "bmi_new", "bmi_ob")) + ds_expect_variables(c("D", "bmi_new", "bmi_ob", "bmi_codes", "bmi_codes_f", "bmi_ref5")) }) disconnect.studies.dataset.cnsim() diff --git a/tests/testthat/test-smk-ds.cor.R b/tests/testthat/test-smk-ds.cor.R index a5c442fb..47a8b501 100644 --- a/tests/testthat/test-smk-ds.cor.R +++ b/tests/testthat/test-smk-ds.cor.R @@ -103,6 +103,17 @@ test_that("error, x numeric vector requires y", { expect_match(res.errors[[1]], "If x is a numeric vector, y must also be a numeric vector") }) +test_that("ds.cor ignores y with a warning when x is a data.frame", { + res.x <- ds.cor(x = "D", type = "split") + + expect_warning( + res.xy <- ds.cor(x = "D", y = "D$survtime", type = "split"), + "y will be ignored", fixed = TRUE + ) + + expect_equal(res.xy, res.x) +}) + test_that("shutdown", { ds_expect_variables(c("D")) }) diff --git a/tests/testthat/test-smk-ds.cov.R b/tests/testthat/test-smk-ds.cov.R index b11eac64..e468051b 100644 --- a/tests/testthat/test-smk-ds.cov.R +++ b/tests/testthat/test-smk-ds.cov.R @@ -68,6 +68,17 @@ test_that("simple test, combine", { # Done # +test_that("ds.cov ignores y with a warning when x is a data.frame", { + res.x <- ds.cov(x = "D", type = "split") + + expect_warning( + res.xy <- ds.cov(x = "D", y = "D$survtime", type = "split"), + "y will be ignored", fixed = TRUE + ) + + expect_equal(res.xy, res.x) +}) + # context("ds.cov::smk::shutdown") test_that("error, x numeric vector requires y", { diff --git a/tests/testthat/test-smk-ds.dim.R b/tests/testthat/test-smk-ds.dim.R index 1ce6f250..8ba7266a 100644 --- a/tests/testthat/test-smk-ds.dim.R +++ b/tests/testthat/test-smk-ds.dim.R @@ -70,10 +70,12 @@ test_that("simple dim, combine", { expect_equal(dim.res$`dimensions of D in combined studies`[[2]], 1) }) -test_that("dim, wrong input class returns a server error", { - expect_error(ds.dim("D$LAB_TSC"), "There are some DataSHIELD errors, list them with datashield.errors()", fixed=TRUE) - res.errors <- DSI::datashield.errors() - expect_match(res.errors[[1]], "must be of type data.frame or matrix") +test_that("dim of a vector is NULL, as in R", { + res <- ds.dim("D$LAB_TSC", type = "split") + + for (study.dim in res) { + expect_null(study.dim) + } }) # diff --git a/tests/testthat/test-smk-ds.glm-binomial.R b/tests/testthat/test-smk-ds.glm-binomial.R index 666188ef..f1291d93 100644 --- a/tests/testthat/test-smk-ds.glm-binomial.R +++ b/tests/testthat/test-smk-ds.glm-binomial.R @@ -53,7 +53,7 @@ test_that("glm_binomial", { # context("ds.glm::smk::binomial, with check") test_that("glm_binomial, with check", { - expect_warning(res <- ds.glm('D$DIS_DIAB~D$GENDER*D$PM_BMI_CONTINUOUS+D$LAB_HDL', family="binomial", checks=TRUE), "NAs introduced by coercion") + expect_no_warning(res <- ds.glm('D$DIS_DIAB~D$GENDER*D$PM_BMI_CONTINUOUS+D$LAB_HDL', family="binomial", checks=TRUE), message = "NAs introduced by coercion") expect_length(res, 13) expect_equal(res$Nvalid, 7485) diff --git a/tests/testthat/test-smk-ds.glm-gaussian.R b/tests/testthat/test-smk-ds.glm-gaussian.R index 569fbdd9..2ac603aa 100644 --- a/tests/testthat/test-smk-ds.glm-gaussian.R +++ b/tests/testthat/test-smk-ds.glm-gaussian.R @@ -53,7 +53,7 @@ test_that("glm_gaussian", { # context("ds.glm::smk::gaussian, with check") test_that("glm_gaussian, with check", { - expect_warning(res <- ds.glm('D$LAB_TSC~D$LAB_TRIG', family="gaussian", checks=TRUE), "NAs introduced by coercion") + expect_no_warning(res <- ds.glm('D$LAB_TSC~D$LAB_TRIG', family="gaussian", checks=TRUE), message = "NAs introduced by coercion") expect_length(res, 13) expect_equal(res$Nvalid, 7800) diff --git a/tests/testthat/test-smk-ds.glm-poisson.R b/tests/testthat/test-smk-ds.glm-poisson.R index 19c56ebc..89a05dc3 100644 --- a/tests/testthat/test-smk-ds.glm-poisson.R +++ b/tests/testthat/test-smk-ds.glm-poisson.R @@ -53,7 +53,7 @@ test_that("glm_gaussian", { # context("ds.glm::smk::poisson, with check") test_that("glm_gaussian, which check", { - expect_warning(res <- ds.glm("D$survtime~1+D$time.id+D$female", family="poisson", checks=TRUE), "NAs introduced by coercion") + expect_no_warning(res <- ds.glm("D$survtime~1+D$time.id+D$female", family="poisson", checks=TRUE), message = "NAs introduced by coercion") expect_length(res, 13) expect_equal(res$Nvalid, 6299) diff --git a/tests/testthat/test-smk-ds.listServersideFunctions.R b/tests/testthat/test-smk-ds.listServersideFunctions.R index df0d5fe4..9d48474c 100644 --- a/tests/testthat/test-smk-ds.listServersideFunctions.R +++ b/tests/testthat/test-smk-ds.listServersideFunctions.R @@ -35,7 +35,7 @@ test_that("check results", { "tableDS.assign", "tan", "tapplyDS.assign", "unlist", "unListDS", "vectorDS" )) aggregate.functions <- factor(c( - "NROW", "asFactorDS1", "asListDS", "aucDS", + "NROW", "asFactorDS1", "aucDS", "checkNegValueDS", "classDS", "colnamesDS", "corDS", "corTestDS", "covDS", "dataFrameSubsetDS1", "densityGridDS", "dimDS", "gamlssDS", diff --git a/tests/testthat/test-smk-ds.mean.R b/tests/testthat/test-smk-ds.mean.R index e4590464..48dffa6f 100644 --- a/tests/testthat/test-smk-ds.mean.R +++ b/tests/testthat/test-smk-ds.mean.R @@ -258,17 +258,27 @@ test_that("mean values [both]", { # Done # +test_that("mean of a logical variable is the proportion TRUE", { + ds.asLogical(x.name = "D$LAB_TSC", newobj = "tsc.logical") + + res <- ds.mean(x = "tsc.logical", type = "split") + + for (i in seq_len(nrow(res$Mean.by.Study))) { + expect_equal(res$Mean.by.Study[i, "EstimatedMean"], 1) + } +}) + # context("ds.mean::smk::shutdown") test_that("error, input must be numeric or integer", { ds.asCharacter(x='D$LAB_TSC', newobj="not_a_numeric") expect_error(ds.mean(x='not_a_numeric'), "There are some DataSHIELD errors, list them with datashield.errors()", fixed=TRUE) res.errors <- DSI::datashield.errors() - expect_match(res.errors[[1]], "must be of type numeric or integer") + expect_match(res.errors[[1]], "must be of type numeric, integer or logical") }) test_that("shutdown", { - ds_expect_variables(c("D", "mean.all.studies", "mean.study.specific", "Nvalid.all.studies", "Nvalid.study.specific", "not_a_numeric")) + ds_expect_variables(c("D", "mean.all.studies", "mean.study.specific", "Nvalid.all.studies", "Nvalid.study.specific", "not_a_numeric", "tsc.logical")) }) disconnect.studies.dataset.cnsim() diff --git a/tests/testthat/test-smk-ds.var.R b/tests/testthat/test-smk-ds.var.R index 65128b19..63ea16ff 100644 --- a/tests/testthat/test-smk-ds.var.R +++ b/tests/testthat/test-smk-ds.var.R @@ -89,17 +89,23 @@ test_that("simple var, both", { # Done # +test_that("var of a logical variable is computed", { + ds.asLogical(x.name = "D$LAB_TSC", newobj = "tsc.logical") + + expect_no_error(ds.var(x = "tsc.logical", type = "split")) +}) + # context("ds.var::smk::shutdown") test_that("error, input must be numeric or integer", { ds.asCharacter(x='D$LAB_TSC', newobj="not_a_numeric") expect_error(ds.var(x='not_a_numeric'), "There are some DataSHIELD errors, list them with datashield.errors()", fixed=TRUE) res.errors <- DSI::datashield.errors() - expect_match(res.errors[[1]], "must be of type numeric or integer") + expect_match(res.errors[[1]], "must be of type numeric, integer or logical") }) test_that("shutdown", { - ds_expect_variables(c("D", "not_a_numeric")) + ds_expect_variables(c("D", "not_a_numeric", "tsc.logical")) }) disconnect.studies.dataset.cnsim()