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 .Rbuildignore
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,6 +15,7 @@
^docker-compose_opal\.yml$
^docker-compose\.yml$
^R/secure.global.ranking.md$
^PULL_REQUEST_TEMPLATE\.md\.R$
^_pkgdown\.yml$
^docs$
^dsBase_7.0.0\.tar\.gz$
Expand Down
4 changes: 3 additions & 1 deletion DESCRIPTION
Original file line numberDiff line numberDiff line change
Expand Up@@ -69,6 +69,7 @@ Imports:
gridExtra,
data.table,
methods,
cli,
dplyr
Suggests:
lme4,
Expand All@@ -80,7 +81,8 @@ Suggests:
DescTools,
DSOpal,
DSMolgenisArmadillo,
DSLite
DSLite,
mockery
RoxygenNote: 7.3.3
Encoding: UTF-8
Language: en-GB
2 changes: 2 additions & 0 deletions NAMESPACE
Original file line numberDiff line numberDiff line change
Expand Up@@ -119,6 +119,8 @@ export(ds.var)
export(ds.vectorCalc)
import(DSI)
import(data.table)
importFrom(DSI,datashield.connections_find)
importFrom(cli,cli_abort)
importFrom(stats,as.formula)
importFrom(stats,na.omit)
importFrom(stats,ts)
Expand Down
27 changes: 27 additions & 0 deletions PULL_REQUEST_TEMPLATE.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
## Instructions & checklist for PR author

### Description of changes
[Add a description of what they have changed]

### Refactor instructions
- [ ] Removed `exists` and `isDefined` from clientside function and add appropriate checks on server-side function
- [ ] Removed any client-side code checking whether an object has been successfully created
- [ ] Reviewed code to determine if additional refactoring could reduce calls to server-side package
- [ ] Replaced check relating to datashield connections object with `.set_datasources()` (defined in `utils.r`)
- [ ] If relevant, replaced argument check that dataset name has been provided to `.check_df_name_provided()` (defined in `utils.r`)

### Testing instructions
- [ ] Writen client-side unit tests for unhappy flow
- [ ] Run `devtools::test(filter = "smk-|disc|arg")` and check it passes
- [ ] Run `devtools::check(args = '--no-tests')` and check it passes
- [ ] Run `devtools::build()` and check it builds without errors
- [ ] Check that the continuous integration checks pass on the pull request branch. Note that the performance test relating to your function may fail, as failure is also triggered by a dramatic improval in performance!

## Instructions & checklist for PR reviewers
- [ ] Checkout this branch as well as the corresponding branch of dsBase
- [ ] Review the code and suggest any changes
- [ ] Install the dsBase branch on an Armadillo or Opal test server
- [ ] Run `devtools::test(filter = "smk-|disc|arg")` and check it passes
- [ ] Run `devtools::check(args = '--no-tests')` and check it passes (we run tests separately to skip performance checks)
- [ ] Run `devtools::build()` and check it builds without errors
- [ ] Check that the continuous integration checks pass on the pull request branch (see above note on performance checks)
71 changes: 24 additions & 47 deletions R/ds.colnames.R
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,61 @@
#'
#' @title Produces column names of the R object in the server-side
#' @description Retrieves column names of an R object on the server-side.
#' @description Retrieves column names of an R object on the server-side.
#' This function is similar to R function \code{colnames}.
#' @details The input is restricted to the object of type \code{data.frame} or \code{matrix}.
#'
#' @details The input is restricted to the object of type \code{data.frame} or \code{matrix}.
#'
#' Server function called: \code{colnamesDS}
#' @param x a character string providing the name of the input data frame or matrix.
#' @param datasources a list of \code{\link[DSI]{DSConnection-class}} objects obtained after login.
#' @param datasources a list of \code{\link[DSI]{DSConnection-class}} objects obtained after login.
#' If the \code{datasources} argument is not specified
#' the default set of connections will be used: see \code{\link[DSI]{datashield.connections_default}}.
#' @return \code{ds.colnames} returns the column names of
#' the specified server-side data frame or matrix.
#' @return \code{ds.colnames} returns the column names of
#' the specified server-side data frame or matrix.
#' @author DataSHIELD Development Team
#' @seealso \code{\link{ds.dim}} to obtain the dimensions of a matrix or a data frame.
#' @examples
#' @examples
#' \dontrun{
#'
#'
#' ## Version 6, for version 5 see the Wiki
#' # Connecting to the Opal servers
#'
#'
#' require('DSI')
#' require('DSOpal')
#' require('dsBaseClient')
#'
#'
#' builder <- DSI::newDSLoginBuilder()
#' builder$append(server = "study1",
#' url = "http://192.168.56.100:8080/",
#' user = "administrator", password = "datashield_test&",
#' builder$append(server = "study1",
#' url = "http://192.168.56.100:8080/",
#' user = "administrator", password = "datashield_test&",
#' table = "CNSIM.CNSIM1", driver = "OpalDriver")
#' builder$append(server = "study2",
#' url = "http://192.168.56.100:8080/",
#' user = "administrator", password = "datashield_test&",
#' builder$append(server = "study2",
#' url = "http://192.168.56.100:8080/",
#' user = "administrator", password = "datashield_test&",
#' table = "CNSIM.CNSIM2", driver = "OpalDriver")
#' builder$append(server = "study3",
#' url = "http://192.168.56.100:8080/",
#' user = "administrator", password = "datashield_test&",
#' url = "http://192.168.56.100:8080/",
#' user = "administrator", password = "datashield_test&",
#' table = "CNSIM.CNSIM3", driver = "OpalDriver")
#' logindata <- builder$build()
#'
#'
#' # Log onto the remote Opal training servers
#' connections <- DSI::datashield.login(logins = logindata, assign = TRUE, symbol = "D")
#'
#' connections <- DSI::datashield.login(logins = logindata, assign = TRUE, symbol = "D")
#'
#' # Getting column names of the R objects stored in the server-side
#' ds.colnames(x = "D",
#' datasources = connections[1]) #only the first server ("study1") is used
#' # Clear the Datashield R sessions and logout
#' datashield.logout(connections)
#' datashield.logout(connections)
#' }
#' @export
#'
ds.colnames <- function(x=NULL, datasources=NULL) {

# look for DS connections
if(is.null(datasources)){
datasources <- datashield.connections_find()
}

# ensure datasources is a list of DSConnection-class
if(!(is.list(datasources) && all(unlist(lapply(datasources, function(d) {methods::is(d,"DSConnection")}))))){
stop("The 'datasources' were expected to be a list of DSConnection-class objects", call.=FALSE)
}

if(is.null(x)){
stop("Please provide the name of a data.frame or matrix!", call.=FALSE)
}

# check if the input object(s) is(are) defined in all the studies
defined <- isDefined(datasources, x)

# call the internal function that checks the input object is of the same class in all studies.
typ <- checkClass(datasources, x)

# if the input object is not a matrix or a dataframe stop
if(!('data.frame' %in% typ) & !('matrix' %in% typ)){
stop("The input vector must be of type 'data.frame' or a 'matrix'!", call.=FALSE)
}
datasources <- .set_datasources(datasources)
.check_df_name_provided(x)

cally <- call("colnamesDS", x)
column_names <- DSI::datashield.aggregate(datasources, cally)

return(column_names)

}
51 changes: 51 additions & 0 deletions R/utils.R
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
#' Retrieve datasources if not specified
#'
#' @param datasources An optional list of data sources. If not provided, the function will attempt
#' to find available data sources.
#' @importFrom DSI datashield.connections_find
#' @return A list of data sources.
#' @noRd
.get_datasources <- function(datasources) {
if (is.null(datasources)) {
datasources <- datashield.connections_find()
}
return(datasources)
}

#' Verify that the provided data sources are of class 'DSConnection'.
#'
#' @param datasources A list of data sources.
#' @importFrom cli cli_abort
#' @noRd
.verify_datasources <- function(datasources) {
is_connection_class <- sapply(datasources, function(x) inherits(unlist(x), "DSConnection"))
if (!all(is_connection_class)) {
cli_abort("The 'datasources' were expected to be a list of DSConnection-class objects")
}
}

#' Set and verify data sources.
#'
#' @param datasources An optional list of data sources. If not provided, the function will attempt
#' to find available data sources.
#' @return A list of verified data sources.
#' @noRd
.set_datasources <- function(datasources) {
datasources <- .get_datasources(datasources)
.verify_datasources(datasources)
return(datasources)
}

#' Check That a Data Frame Name Is Provided
#'
#' Internal helper that checks whether a data frame or matrix object
#' has been provided. If `NULL`, it aborts with a user-friendly error.
#'
#' @param df A data.frame or matrix.
#' @return Invisibly returns `NULL`. Called for its side effect (error checking).
#' @noRd
.check_df_name_provided <- function(df) {
if(is.null(df)){
cli_abort("Please provide the name of a data.frame or matrix!", call.=FALSE)
}
}
30 changes: 15 additions & 15 deletions man/ds.colnames.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 13 additions & 13 deletions tests/testthat/perf_files/default_perf_profile.csv
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
"refer_name","rate","lower_tolerance","upper_tolerance"
"conndisconn::perf::simple0","0.2725","0.5","2"
"ds.abs::perf::0","2.677","0.5","2"
"ds.asInteger::perf:0","2.294","0.5","2"
"ds.asList::perf:0","4.587","0.5","2"
"ds.asNumeric::perf:0","2.185","0.5","2"
"ds.assign::perf::0","5.490","0.5","2"
"ds.class::perf::combine:0","4.760","0.5","2"
"ds.colnames::perf:0","4.218","0.5","2"
"ds.exists::perf::combine:0","11.09","0.5","2"
"ds.length::perf::combine:0","9.479","0.5","2"
"ds.mean::perf::combine:0","9.650","0.5","2"
"ds.mean::perf::split:0","11.26","0.5","2"
"void::perf::void::0","46250.0","0.5","2"
"conndisconn::perf::simple0","0.2725","0.5","10"
"ds.abs::perf::0","2.677","0.5","10"
"ds.asInteger::perf:0","2.294","0.5","10"
"ds.asList::perf:0","4.587","0.5","10"
"ds.asNumeric::perf:0","2.185","0.5","10"
"ds.assign::perf::0","5.490","0.5","10"
"ds.class::perf::combine:0","4.760","0.5","10"
"ds.colnames::perf:0","4.218","0.5","10"
"ds.exists::perf::combine:0","11.09","0.5","10"
"ds.length::perf::combine:0","9.479","0.5","10"
"ds.mean::perf::combine:0","9.650","0.5","10"
"ds.mean::perf::split:0","11.26","0.5","10"
"void::perf::void::0","46250.0","0.5","10"
3 changes: 2 additions & 1 deletion tests/testthat/test-smk-ds.colnames.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,7 +47,8 @@ test_that("simple colnames", {
test_that("fails if the object does not exist", {
expect_error(
ds.colnames("non_existing_df"),
regexp = "The input object non_existing_df is not defined in sim1, sim2, sim3!",
# regexp = "object 'non_existing_df' not found",
regexp = "There are some DataSHIELD errors, list them with datashield.errors()",
ignore.case = TRUE
)
})
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all \u003cpre\u003e\u003ccode\u003e blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks"); } } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); } })(); (function(){ try { var __m = "github.com"; var __re = new RegExp('^' + "github\\.com" + '
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 .Rbuildignore
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,6 +15,7 @@
^docker-compose_opal\.yml$
^docker-compose\.yml$
^R/secure.global.ranking.md$
^PULL_REQUEST_TEMPLATE\.md\.R$
^_pkgdown\.yml$
^docs$
^dsBase_7.0.0\.tar\.gz$
Expand Down
4 changes: 3 additions & 1 deletion DESCRIPTION
Original file line numberDiff line numberDiff line change
Expand Up@@ -69,6 +69,7 @@ Imports:
gridExtra,
data.table,
methods,
cli,
dplyr
Suggests:
lme4,
Expand All@@ -80,7 +81,8 @@ Suggests:
DescTools,
DSOpal,
DSMolgenisArmadillo,
DSLite
DSLite,
mockery
RoxygenNote: 7.3.3
Encoding: UTF-8
Language: en-GB
2 changes: 2 additions & 0 deletions NAMESPACE
Original file line numberDiff line numberDiff line change
Expand Up@@ -119,6 +119,8 @@ export(ds.var)
export(ds.vectorCalc)
import(DSI)
import(data.table)
importFrom(DSI,datashield.connections_find)
importFrom(cli,cli_abort)
importFrom(stats,as.formula)
importFrom(stats,na.omit)
importFrom(stats,ts)
Expand Down
27 changes: 27 additions & 0 deletions PULL_REQUEST_TEMPLATE.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
## Instructions & checklist for PR author

### Description of changes
[Add a description of what they have changed]

### Refactor instructions
- [ ] Removed `exists` and `isDefined` from clientside function and add appropriate checks on server-side function
- [ ] Removed any client-side code checking whether an object has been successfully created
- [ ] Reviewed code to determine if additional refactoring could reduce calls to server-side package
- [ ] Replaced check relating to datashield connections object with `.set_datasources()` (defined in `utils.r`)
- [ ] If relevant, replaced argument check that dataset name has been provided to `.check_df_name_provided()` (defined in `utils.r`)

### Testing instructions
- [ ] Writen client-side unit tests for unhappy flow
- [ ] Run `devtools::test(filter = "smk-|disc|arg")` and check it passes
- [ ] Run `devtools::check(args = '--no-tests')` and check it passes
- [ ] Run `devtools::build()` and check it builds without errors
- [ ] Check that the continuous integration checks pass on the pull request branch. Note that the performance test relating to your function may fail, as failure is also triggered by a dramatic improval in performance!

## Instructions & checklist for PR reviewers
- [ ] Checkout this branch as well as the corresponding branch of dsBase
- [ ] Review the code and suggest any changes
- [ ] Install the dsBase branch on an Armadillo or Opal test server
- [ ] Run `devtools::test(filter = "smk-|disc|arg")` and check it passes
- [ ] Run `devtools::check(args = '--no-tests')` and check it passes (we run tests separately to skip performance checks)
- [ ] Run `devtools::build()` and check it builds without errors
- [ ] Check that the continuous integration checks pass on the pull request branch (see above note on performance checks)
71 changes: 24 additions & 47 deletions R/ds.colnames.R
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,61 @@
#'
#' @title Produces column names of the R object in the server-side
#' @description Retrieves column names of an R object on the server-side.
#' @description Retrieves column names of an R object on the server-side.
#' This function is similar to R function \code{colnames}.
#' @details The input is restricted to the object of type \code{data.frame} or \code{matrix}.
#'
#' @details The input is restricted to the object of type \code{data.frame} or \code{matrix}.
#'
#' Server function called: \code{colnamesDS}
#' @param x a character string providing the name of the input data frame or matrix.
#' @param datasources a list of \code{\link[DSI]{DSConnection-class}} objects obtained after login.
#' @param datasources a list of \code{\link[DSI]{DSConnection-class}} objects obtained after login.
#' If the \code{datasources} argument is not specified
#' the default set of connections will be used: see \code{\link[DSI]{datashield.connections_default}}.
#' @return \code{ds.colnames} returns the column names of
#' the specified server-side data frame or matrix.
#' @return \code{ds.colnames} returns the column names of
#' the specified server-side data frame or matrix.
#' @author DataSHIELD Development Team
#' @seealso \code{\link{ds.dim}} to obtain the dimensions of a matrix or a data frame.
#' @examples
#' @examples
#' \dontrun{
#'
#'
#' ## Version 6, for version 5 see the Wiki
#' # Connecting to the Opal servers
#'
#'
#' require('DSI')
#' require('DSOpal')
#' require('dsBaseClient')
#'
#'
#' builder <- DSI::newDSLoginBuilder()
#' builder$append(server = "study1",
#' url = "http://192.168.56.100:8080/",
#' user = "administrator", password = "datashield_test&",
#' builder$append(server = "study1",
#' url = "http://192.168.56.100:8080/",
#' user = "administrator", password = "datashield_test&",
#' table = "CNSIM.CNSIM1", driver = "OpalDriver")
#' builder$append(server = "study2",
#' url = "http://192.168.56.100:8080/",
#' user = "administrator", password = "datashield_test&",
#' builder$append(server = "study2",
#' url = "http://192.168.56.100:8080/",
#' user = "administrator", password = "datashield_test&",
#' table = "CNSIM.CNSIM2", driver = "OpalDriver")
#' builder$append(server = "study3",
#' url = "http://192.168.56.100:8080/",
#' user = "administrator", password = "datashield_test&",
#' url = "http://192.168.56.100:8080/",
#' user = "administrator", password = "datashield_test&",
#' table = "CNSIM.CNSIM3", driver = "OpalDriver")
#' logindata <- builder$build()
#'
#'
#' # Log onto the remote Opal training servers
#' connections <- DSI::datashield.login(logins = logindata, assign = TRUE, symbol = "D")
#'
#' connections <- DSI::datashield.login(logins = logindata, assign = TRUE, symbol = "D")
#'
#' # Getting column names of the R objects stored in the server-side
#' ds.colnames(x = "D",
#' datasources = connections[1]) #only the first server ("study1") is used
#' # Clear the Datashield R sessions and logout
#' datashield.logout(connections)
#' datashield.logout(connections)
#' }
#' @export
#'
ds.colnames <- function(x=NULL, datasources=NULL) {

# look for DS connections
if(is.null(datasources)){
datasources <- datashield.connections_find()
}

# ensure datasources is a list of DSConnection-class
if(!(is.list(datasources) && all(unlist(lapply(datasources, function(d) {methods::is(d,"DSConnection")}))))){
stop("The 'datasources' were expected to be a list of DSConnection-class objects", call.=FALSE)
}

if(is.null(x)){
stop("Please provide the name of a data.frame or matrix!", call.=FALSE)
}

# check if the input object(s) is(are) defined in all the studies
defined <- isDefined(datasources, x)

# call the internal function that checks the input object is of the same class in all studies.
typ <- checkClass(datasources, x)

# if the input object is not a matrix or a dataframe stop
if(!('data.frame' %in% typ) & !('matrix' %in% typ)){
stop("The input vector must be of type 'data.frame' or a 'matrix'!", call.=FALSE)
}
datasources <- .set_datasources(datasources)
.check_df_name_provided(x)

cally <- call("colnamesDS", x)
column_names <- DSI::datashield.aggregate(datasources, cally)

return(column_names)

}
51 changes: 51 additions & 0 deletions R/utils.R
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
#' Retrieve datasources if not specified
#'
#' @param datasources An optional list of data sources. If not provided, the function will attempt
#' to find available data sources.
#' @importFrom DSI datashield.connections_find
#' @return A list of data sources.
#' @noRd
.get_datasources <- function(datasources) {
if (is.null(datasources)) {
datasources <- datashield.connections_find()
}
return(datasources)
}

#' Verify that the provided data sources are of class 'DSConnection'.
#'
#' @param datasources A list of data sources.
#' @importFrom cli cli_abort
#' @noRd
.verify_datasources <- function(datasources) {
is_connection_class <- sapply(datasources, function(x) inherits(unlist(x), "DSConnection"))
if (!all(is_connection_class)) {
cli_abort("The 'datasources' were expected to be a list of DSConnection-class objects")
}
}

#' Set and verify data sources.
#'
#' @param datasources An optional list of data sources. If not provided, the function will attempt
#' to find available data sources.
#' @return A list of verified data sources.
#' @noRd
.set_datasources <- function(datasources) {
datasources <- .get_datasources(datasources)
.verify_datasources(datasources)
return(datasources)
}

#' Check That a Data Frame Name Is Provided
#'
#' Internal helper that checks whether a data frame or matrix object
#' has been provided. If `NULL`, it aborts with a user-friendly error.
#'
#' @param df A data.frame or matrix.
#' @return Invisibly returns `NULL`. Called for its side effect (error checking).
#' @noRd
.check_df_name_provided <- function(df) {
if(is.null(df)){
cli_abort("Please provide the name of a data.frame or matrix!", call.=FALSE)
}
}
30 changes: 15 additions & 15 deletions man/ds.colnames.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 13 additions & 13 deletions tests/testthat/perf_files/default_perf_profile.csv
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
"refer_name","rate","lower_tolerance","upper_tolerance"
"conndisconn::perf::simple0","0.2725","0.5","2"
"ds.abs::perf::0","2.677","0.5","2"
"ds.asInteger::perf:0","2.294","0.5","2"
"ds.asList::perf:0","4.587","0.5","2"
"ds.asNumeric::perf:0","2.185","0.5","2"
"ds.assign::perf::0","5.490","0.5","2"
"ds.class::perf::combine:0","4.760","0.5","2"
"ds.colnames::perf:0","4.218","0.5","2"
"ds.exists::perf::combine:0","11.09","0.5","2"
"ds.length::perf::combine:0","9.479","0.5","2"
"ds.mean::perf::combine:0","9.650","0.5","2"
"ds.mean::perf::split:0","11.26","0.5","2"
"void::perf::void::0","46250.0","0.5","2"
"conndisconn::perf::simple0","0.2725","0.5","10"
"ds.abs::perf::0","2.677","0.5","10"
"ds.asInteger::perf:0","2.294","0.5","10"
"ds.asList::perf:0","4.587","0.5","10"
"ds.asNumeric::perf:0","2.185","0.5","10"
"ds.assign::perf::0","5.490","0.5","10"
"ds.class::perf::combine:0","4.760","0.5","10"
"ds.colnames::perf:0","4.218","0.5","10"
"ds.exists::perf::combine:0","11.09","0.5","10"
"ds.length::perf::combine:0","9.479","0.5","10"
"ds.mean::perf::combine:0","9.650","0.5","10"
"ds.mean::perf::split:0","11.26","0.5","10"
"void::perf::void::0","46250.0","0.5","10"
3 changes: 2 additions & 1 deletion tests/testthat/test-smk-ds.colnames.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,7 +47,8 @@ test_that("simple colnames", {
test_that("fails if the object does not exist", {
expect_error(
ds.colnames("non_existing_df"),
regexp = "The input object non_existing_df is not defined in sim1, sim2, sim3!",
# regexp = "object 'non_existing_df' not found",
regexp = "There are some DataSHIELD errors, list them with datashield.errors()",
ignore.case = TRUE
)
})
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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 .Rbuildignore
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,6 +15,7 @@
^docker-compose_opal\.yml$
^docker-compose\.yml$
^R/secure.global.ranking.md$
^PULL_REQUEST_TEMPLATE\.md\.R$
^_pkgdown\.yml$
^docs$
^dsBase_7.0.0\.tar\.gz$
Expand Down
4 changes: 3 additions & 1 deletion DESCRIPTION
Original file line numberDiff line numberDiff line change
Expand Up@@ -69,6 +69,7 @@ Imports:
gridExtra,
data.table,
methods,
cli,
dplyr
Suggests:
lme4,
Expand All@@ -80,7 +81,8 @@ Suggests:
DescTools,
DSOpal,
DSMolgenisArmadillo,
DSLite
DSLite,
mockery
RoxygenNote: 7.3.3
Encoding: UTF-8
Language: en-GB
2 changes: 2 additions & 0 deletions NAMESPACE
Original file line numberDiff line numberDiff line change
Expand Up@@ -119,6 +119,8 @@ export(ds.var)
export(ds.vectorCalc)
import(DSI)
import(data.table)
importFrom(DSI,datashield.connections_find)
importFrom(cli,cli_abort)
importFrom(stats,as.formula)
importFrom(stats,na.omit)
importFrom(stats,ts)
Expand Down
27 changes: 27 additions & 0 deletions PULL_REQUEST_TEMPLATE.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
## Instructions & checklist for PR author

### Description of changes
[Add a description of what they have changed]

### Refactor instructions
- [ ] Removed `exists` and `isDefined` from clientside function and add appropriate checks on server-side function
- [ ] Removed any client-side code checking whether an object has been successfully created
- [ ] Reviewed code to determine if additional refactoring could reduce calls to server-side package
- [ ] Replaced check relating to datashield connections object with `.set_datasources()` (defined in `utils.r`)
- [ ] If relevant, replaced argument check that dataset name has been provided to `.check_df_name_provided()` (defined in `utils.r`)

### Testing instructions
- [ ] Writen client-side unit tests for unhappy flow
- [ ] Run `devtools::test(filter = "smk-|disc|arg")` and check it passes
- [ ] Run `devtools::check(args = '--no-tests')` and check it passes
- [ ] Run `devtools::build()` and check it builds without errors
- [ ] Check that the continuous integration checks pass on the pull request branch. Note that the performance test relating to your function may fail, as failure is also triggered by a dramatic improval in performance!

## Instructions & checklist for PR reviewers
- [ ] Checkout this branch as well as the corresponding branch of dsBase
- [ ] Review the code and suggest any changes
- [ ] Install the dsBase branch on an Armadillo or Opal test server
- [ ] Run `devtools::test(filter = "smk-|disc|arg")` and check it passes
- [ ] Run `devtools::check(args = '--no-tests')` and check it passes (we run tests separately to skip performance checks)
- [ ] Run `devtools::build()` and check it builds without errors
- [ ] Check that the continuous integration checks pass on the pull request branch (see above note on performance checks)
71 changes: 24 additions & 47 deletions R/ds.colnames.R
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,61 @@
#'
#' @title Produces column names of the R object in the server-side
#' @description Retrieves column names of an R object on the server-side.
#' @description Retrieves column names of an R object on the server-side.
#' This function is similar to R function \code{colnames}.
#' @details The input is restricted to the object of type \code{data.frame} or \code{matrix}.
#'
#' @details The input is restricted to the object of type \code{data.frame} or \code{matrix}.
#'
#' Server function called: \code{colnamesDS}
#' @param x a character string providing the name of the input data frame or matrix.
#' @param datasources a list of \code{\link[DSI]{DSConnection-class}} objects obtained after login.
#' @param datasources a list of \code{\link[DSI]{DSConnection-class}} objects obtained after login.
#' If the \code{datasources} argument is not specified
#' the default set of connections will be used: see \code{\link[DSI]{datashield.connections_default}}.
#' @return \code{ds.colnames} returns the column names of
#' the specified server-side data frame or matrix.
#' @return \code{ds.colnames} returns the column names of
#' the specified server-side data frame or matrix.
#' @author DataSHIELD Development Team
#' @seealso \code{\link{ds.dim}} to obtain the dimensions of a matrix or a data frame.
#' @examples
#' @examples
#' \dontrun{
#'
#'
#' ## Version 6, for version 5 see the Wiki
#' # Connecting to the Opal servers
#'
#'
#' require('DSI')
#' require('DSOpal')
#' require('dsBaseClient')
#'
#'
#' builder <- DSI::newDSLoginBuilder()
#' builder$append(server = "study1",
#' url = "http://192.168.56.100:8080/",
#' user = "administrator", password = "datashield_test&",
#' builder$append(server = "study1",
#' url = "http://192.168.56.100:8080/",
#' user = "administrator", password = "datashield_test&",
#' table = "CNSIM.CNSIM1", driver = "OpalDriver")
#' builder$append(server = "study2",
#' url = "http://192.168.56.100:8080/",
#' user = "administrator", password = "datashield_test&",
#' builder$append(server = "study2",
#' url = "http://192.168.56.100:8080/",
#' user = "administrator", password = "datashield_test&",
#' table = "CNSIM.CNSIM2", driver = "OpalDriver")
#' builder$append(server = "study3",
#' url = "http://192.168.56.100:8080/",
#' user = "administrator", password = "datashield_test&",
#' url = "http://192.168.56.100:8080/",
#' user = "administrator", password = "datashield_test&",
#' table = "CNSIM.CNSIM3", driver = "OpalDriver")
#' logindata <- builder$build()
#'
#'
#' # Log onto the remote Opal training servers
#' connections <- DSI::datashield.login(logins = logindata, assign = TRUE, symbol = "D")
#'
#' connections <- DSI::datashield.login(logins = logindata, assign = TRUE, symbol = "D")
#'
#' # Getting column names of the R objects stored in the server-side
#' ds.colnames(x = "D",
#' datasources = connections[1]) #only the first server ("study1") is used
#' # Clear the Datashield R sessions and logout
#' datashield.logout(connections)
#' datashield.logout(connections)
#' }
#' @export
#'
ds.colnames <- function(x=NULL, datasources=NULL) {

# look for DS connections
if(is.null(datasources)){
datasources <- datashield.connections_find()
}

# ensure datasources is a list of DSConnection-class
if(!(is.list(datasources) && all(unlist(lapply(datasources, function(d) {methods::is(d,"DSConnection")}))))){
stop("The 'datasources' were expected to be a list of DSConnection-class objects", call.=FALSE)
}

if(is.null(x)){
stop("Please provide the name of a data.frame or matrix!", call.=FALSE)
}

# check if the input object(s) is(are) defined in all the studies
defined <- isDefined(datasources, x)

# call the internal function that checks the input object is of the same class in all studies.
typ <- checkClass(datasources, x)

# if the input object is not a matrix or a dataframe stop
if(!('data.frame' %in% typ) & !('matrix' %in% typ)){
stop("The input vector must be of type 'data.frame' or a 'matrix'!", call.=FALSE)
}
datasources <- .set_datasources(datasources)
.check_df_name_provided(x)

cally <- call("colnamesDS", x)
column_names <- DSI::datashield.aggregate(datasources, cally)

return(column_names)

}
51 changes: 51 additions & 0 deletions R/utils.R
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
#' Retrieve datasources if not specified
#'
#' @param datasources An optional list of data sources. If not provided, the function will attempt
#' to find available data sources.
#' @importFrom DSI datashield.connections_find
#' @return A list of data sources.
#' @noRd
.get_datasources <- function(datasources) {
if (is.null(datasources)) {
datasources <- datashield.connections_find()
}
return(datasources)
}

#' Verify that the provided data sources are of class 'DSConnection'.
#'
#' @param datasources A list of data sources.
#' @importFrom cli cli_abort
#' @noRd
.verify_datasources <- function(datasources) {
is_connection_class <- sapply(datasources, function(x) inherits(unlist(x), "DSConnection"))
if (!all(is_connection_class)) {
cli_abort("The 'datasources' were expected to be a list of DSConnection-class objects")
}
}

#' Set and verify data sources.
#'
#' @param datasources An optional list of data sources. If not provided, the function will attempt
#' to find available data sources.
#' @return A list of verified data sources.
#' @noRd
.set_datasources <- function(datasources) {
datasources <- .get_datasources(datasources)
.verify_datasources(datasources)
return(datasources)
}

#' Check That a Data Frame Name Is Provided
#'
#' Internal helper that checks whether a data frame or matrix object
#' has been provided. If `NULL`, it aborts with a user-friendly error.
#'
#' @param df A data.frame or matrix.
#' @return Invisibly returns `NULL`. Called for its side effect (error checking).
#' @noRd
.check_df_name_provided <- function(df) {
if(is.null(df)){
cli_abort("Please provide the name of a data.frame or matrix!", call.=FALSE)
}
}
30 changes: 15 additions & 15 deletions man/ds.colnames.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 13 additions & 13 deletions tests/testthat/perf_files/default_perf_profile.csv
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
"refer_name","rate","lower_tolerance","upper_tolerance"
"conndisconn::perf::simple0","0.2725","0.5","2"
"ds.abs::perf::0","2.677","0.5","2"
"ds.asInteger::perf:0","2.294","0.5","2"
"ds.asList::perf:0","4.587","0.5","2"
"ds.asNumeric::perf:0","2.185","0.5","2"
"ds.assign::perf::0","5.490","0.5","2"
"ds.class::perf::combine:0","4.760","0.5","2"
"ds.colnames::perf:0","4.218","0.5","2"
"ds.exists::perf::combine:0","11.09","0.5","2"
"ds.length::perf::combine:0","9.479","0.5","2"
"ds.mean::perf::combine:0","9.650","0.5","2"
"ds.mean::perf::split:0","11.26","0.5","2"
"void::perf::void::0","46250.0","0.5","2"
"conndisconn::perf::simple0","0.2725","0.5","10"
"ds.abs::perf::0","2.677","0.5","10"
"ds.asInteger::perf:0","2.294","0.5","10"
"ds.asList::perf:0","4.587","0.5","10"
"ds.asNumeric::perf:0","2.185","0.5","10"
"ds.assign::perf::0","5.490","0.5","10"
"ds.class::perf::combine:0","4.760","0.5","10"
"ds.colnames::perf:0","4.218","0.5","10"
"ds.exists::perf::combine:0","11.09","0.5","10"
"ds.length::perf::combine:0","9.479","0.5","10"
"ds.mean::perf::combine:0","9.650","0.5","10"
"ds.mean::perf::split:0","11.26","0.5","10"
"void::perf::void::0","46250.0","0.5","10"
3 changes: 2 additions & 1 deletion tests/testthat/test-smk-ds.colnames.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,7 +47,8 @@ test_that("simple colnames", {
test_that("fails if the object does not exist", {
expect_error(
ds.colnames("non_existing_df"),
regexp = "The input object non_existing_df is not defined in sim1, sim2, sim3!",
# regexp = "object 'non_existing_df' not found",
regexp = "There are some DataSHIELD errors, list them with datashield.errors()",
ignore.case = TRUE
)
})
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length \u003e 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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 .Rbuildignore
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,6 +15,7 @@
^docker-compose_opal\.yml$
^docker-compose\.yml$
^R/secure.global.ranking.md$
^PULL_REQUEST_TEMPLATE\.md\.R$
^_pkgdown\.yml$
^docs$
^dsBase_7.0.0\.tar\.gz$
Expand Down
4 changes: 3 additions & 1 deletion DESCRIPTION
Original file line numberDiff line numberDiff line change
Expand Up@@ -69,6 +69,7 @@ Imports:
gridExtra,
data.table,
methods,
cli,
dplyr
Suggests:
lme4,
Expand All@@ -80,7 +81,8 @@ Suggests:
DescTools,
DSOpal,
DSMolgenisArmadillo,
DSLite
DSLite,
mockery
RoxygenNote: 7.3.3
Encoding: UTF-8
Language: en-GB
2 changes: 2 additions & 0 deletions NAMESPACE
Original file line numberDiff line numberDiff line change
Expand Up@@ -119,6 +119,8 @@ export(ds.var)
export(ds.vectorCalc)
import(DSI)
import(data.table)
importFrom(DSI,datashield.connections_find)
importFrom(cli,cli_abort)
importFrom(stats,as.formula)
importFrom(stats,na.omit)
importFrom(stats,ts)
Expand Down
27 changes: 27 additions & 0 deletions PULL_REQUEST_TEMPLATE.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
## Instructions & checklist for PR author

### Description of changes
[Add a description of what they have changed]

### Refactor instructions
- [ ] Removed `exists` and `isDefined` from clientside function and add appropriate checks on server-side function
- [ ] Removed any client-side code checking whether an object has been successfully created
- [ ] Reviewed code to determine if additional refactoring could reduce calls to server-side package
- [ ] Replaced check relating to datashield connections object with `.set_datasources()` (defined in `utils.r`)
- [ ] If relevant, replaced argument check that dataset name has been provided to `.check_df_name_provided()` (defined in `utils.r`)

### Testing instructions
- [ ] Writen client-side unit tests for unhappy flow
- [ ] Run `devtools::test(filter = "smk-|disc|arg")` and check it passes
- [ ] Run `devtools::check(args = '--no-tests')` and check it passes
- [ ] Run `devtools::build()` and check it builds without errors
- [ ] Check that the continuous integration checks pass on the pull request branch. Note that the performance test relating to your function may fail, as failure is also triggered by a dramatic improval in performance!

## Instructions & checklist for PR reviewers
- [ ] Checkout this branch as well as the corresponding branch of dsBase
- [ ] Review the code and suggest any changes
- [ ] Install the dsBase branch on an Armadillo or Opal test server
- [ ] Run `devtools::test(filter = "smk-|disc|arg")` and check it passes
- [ ] Run `devtools::check(args = '--no-tests')` and check it passes (we run tests separately to skip performance checks)
- [ ] Run `devtools::build()` and check it builds without errors
- [ ] Check that the continuous integration checks pass on the pull request branch (see above note on performance checks)
71 changes: 24 additions & 47 deletions R/ds.colnames.R
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,61 @@
#'
#' @title Produces column names of the R object in the server-side
#' @description Retrieves column names of an R object on the server-side.
#' @description Retrieves column names of an R object on the server-side.
#' This function is similar to R function \code{colnames}.
#' @details The input is restricted to the object of type \code{data.frame} or \code{matrix}.
#'
#' @details The input is restricted to the object of type \code{data.frame} or \code{matrix}.
#'
#' Server function called: \code{colnamesDS}
#' @param x a character string providing the name of the input data frame or matrix.
#' @param datasources a list of \code{\link[DSI]{DSConnection-class}} objects obtained after login.
#' @param datasources a list of \code{\link[DSI]{DSConnection-class}} objects obtained after login.
#' If the \code{datasources} argument is not specified
#' the default set of connections will be used: see \code{\link[DSI]{datashield.connections_default}}.
#' @return \code{ds.colnames} returns the column names of
#' the specified server-side data frame or matrix.
#' @return \code{ds.colnames} returns the column names of
#' the specified server-side data frame or matrix.
#' @author DataSHIELD Development Team
#' @seealso \code{\link{ds.dim}} to obtain the dimensions of a matrix or a data frame.
#' @examples
#' @examples
#' \dontrun{
#'
#'
#' ## Version 6, for version 5 see the Wiki
#' # Connecting to the Opal servers
#'
#'
#' require('DSI')
#' require('DSOpal')
#' require('dsBaseClient')
#'
#'
#' builder <- DSI::newDSLoginBuilder()
#' builder$append(server = "study1",
#' url = "http://192.168.56.100:8080/",
#' user = "administrator", password = "datashield_test&",
#' builder$append(server = "study1",
#' url = "http://192.168.56.100:8080/",
#' user = "administrator", password = "datashield_test&",
#' table = "CNSIM.CNSIM1", driver = "OpalDriver")
#' builder$append(server = "study2",
#' url = "http://192.168.56.100:8080/",
#' user = "administrator", password = "datashield_test&",
#' builder$append(server = "study2",
#' url = "http://192.168.56.100:8080/",
#' user = "administrator", password = "datashield_test&",
#' table = "CNSIM.CNSIM2", driver = "OpalDriver")
#' builder$append(server = "study3",
#' url = "http://192.168.56.100:8080/",
#' user = "administrator", password = "datashield_test&",
#' url = "http://192.168.56.100:8080/",
#' user = "administrator", password = "datashield_test&",
#' table = "CNSIM.CNSIM3", driver = "OpalDriver")
#' logindata <- builder$build()
#'
#'
#' # Log onto the remote Opal training servers
#' connections <- DSI::datashield.login(logins = logindata, assign = TRUE, symbol = "D")
#'
#' connections <- DSI::datashield.login(logins = logindata, assign = TRUE, symbol = "D")
#'
#' # Getting column names of the R objects stored in the server-side
#' ds.colnames(x = "D",
#' datasources = connections[1]) #only the first server ("study1") is used
#' # Clear the Datashield R sessions and logout
#' datashield.logout(connections)
#' datashield.logout(connections)
#' }
#' @export
#'
ds.colnames <- function(x=NULL, datasources=NULL) {

# look for DS connections
if(is.null(datasources)){
datasources <- datashield.connections_find()
}

# ensure datasources is a list of DSConnection-class
if(!(is.list(datasources) && all(unlist(lapply(datasources, function(d) {methods::is(d,"DSConnection")}))))){
stop("The 'datasources' were expected to be a list of DSConnection-class objects", call.=FALSE)
}

if(is.null(x)){
stop("Please provide the name of a data.frame or matrix!", call.=FALSE)
}

# check if the input object(s) is(are) defined in all the studies
defined <- isDefined(datasources, x)

# call the internal function that checks the input object is of the same class in all studies.
typ <- checkClass(datasources, x)

# if the input object is not a matrix or a dataframe stop
if(!('data.frame' %in% typ) & !('matrix' %in% typ)){
stop("The input vector must be of type 'data.frame' or a 'matrix'!", call.=FALSE)
}
datasources <- .set_datasources(datasources)
.check_df_name_provided(x)

cally <- call("colnamesDS", x)
column_names <- DSI::datashield.aggregate(datasources, cally)

return(column_names)

}
51 changes: 51 additions & 0 deletions R/utils.R
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
#' Retrieve datasources if not specified
#'
#' @param datasources An optional list of data sources. If not provided, the function will attempt
#' to find available data sources.
#' @importFrom DSI datashield.connections_find
#' @return A list of data sources.
#' @noRd
.get_datasources <- function(datasources) {
if (is.null(datasources)) {
datasources <- datashield.connections_find()
}
return(datasources)
}

#' Verify that the provided data sources are of class 'DSConnection'.
#'
#' @param datasources A list of data sources.
#' @importFrom cli cli_abort
#' @noRd
.verify_datasources <- function(datasources) {
is_connection_class <- sapply(datasources, function(x) inherits(unlist(x), "DSConnection"))
if (!all(is_connection_class)) {
cli_abort("The 'datasources' were expected to be a list of DSConnection-class objects")
}
}

#' Set and verify data sources.
#'
#' @param datasources An optional list of data sources. If not provided, the function will attempt
#' to find available data sources.
#' @return A list of verified data sources.
#' @noRd
.set_datasources <- function(datasources) {
datasources <- .get_datasources(datasources)
.verify_datasources(datasources)
return(datasources)
}

#' Check That a Data Frame Name Is Provided
#'
#' Internal helper that checks whether a data frame or matrix object
#' has been provided. If `NULL`, it aborts with a user-friendly error.
#'
#' @param df A data.frame or matrix.
#' @return Invisibly returns `NULL`. Called for its side effect (error checking).
#' @noRd
.check_df_name_provided <- function(df) {
if(is.null(df)){
cli_abort("Please provide the name of a data.frame or matrix!", call.=FALSE)
}
}
30 changes: 15 additions & 15 deletions man/ds.colnames.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 13 additions & 13 deletions tests/testthat/perf_files/default_perf_profile.csv
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
"refer_name","rate","lower_tolerance","upper_tolerance"
"conndisconn::perf::simple0","0.2725","0.5","2"
"ds.abs::perf::0","2.677","0.5","2"
"ds.asInteger::perf:0","2.294","0.5","2"
"ds.asList::perf:0","4.587","0.5","2"
"ds.asNumeric::perf:0","2.185","0.5","2"
"ds.assign::perf::0","5.490","0.5","2"
"ds.class::perf::combine:0","4.760","0.5","2"
"ds.colnames::perf:0","4.218","0.5","2"
"ds.exists::perf::combine:0","11.09","0.5","2"
"ds.length::perf::combine:0","9.479","0.5","2"
"ds.mean::perf::combine:0","9.650","0.5","2"
"ds.mean::perf::split:0","11.26","0.5","2"
"void::perf::void::0","46250.0","0.5","2"
"conndisconn::perf::simple0","0.2725","0.5","10"
"ds.abs::perf::0","2.677","0.5","10"
"ds.asInteger::perf:0","2.294","0.5","10"
"ds.asList::perf:0","4.587","0.5","10"
"ds.asNumeric::perf:0","2.185","0.5","10"
"ds.assign::perf::0","5.490","0.5","10"
"ds.class::perf::combine:0","4.760","0.5","10"
"ds.colnames::perf:0","4.218","0.5","10"
"ds.exists::perf::combine:0","11.09","0.5","10"
"ds.length::perf::combine:0","9.479","0.5","10"
"ds.mean::perf::combine:0","9.650","0.5","10"
"ds.mean::perf::split:0","11.26","0.5","10"
"void::perf::void::0","46250.0","0.5","10"
3 changes: 2 additions & 1 deletion tests/testthat/test-smk-ds.colnames.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,7 +47,8 @@ test_that("simple colnames", {
test_that("fails if the object does not exist", {
expect_error(
ds.colnames("non_existing_df"),
regexp = "The input object non_existing_df is not defined in sim1, sim2, sim3!",
# regexp = "object 'non_existing_df' not found",
regexp = "There are some DataSHIELD errors, list them with datashield.errors()",
ignore.case = TRUE
)
})
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
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 .Rbuildignore
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,6 +15,7 @@
^docker-compose_opal\.yml$
^docker-compose\.yml$
^R/secure.global.ranking.md$
^PULL_REQUEST_TEMPLATE\.md\.R$
^_pkgdown\.yml$
^docs$
^dsBase_7.0.0\.tar\.gz$
Expand Down
4 changes: 3 additions & 1 deletion DESCRIPTION
Original file line numberDiff line numberDiff line change
Expand Up@@ -69,6 +69,7 @@ Imports:
gridExtra,
data.table,
methods,
cli,
dplyr
Suggests:
lme4,
Expand All@@ -80,7 +81,8 @@ Suggests:
DescTools,
DSOpal,
DSMolgenisArmadillo,
DSLite
DSLite,
mockery
RoxygenNote: 7.3.3
Encoding: UTF-8
Language: en-GB
2 changes: 2 additions & 0 deletions NAMESPACE
Original file line numberDiff line numberDiff line change
Expand Up@@ -119,6 +119,8 @@ export(ds.var)
export(ds.vectorCalc)
import(DSI)
import(data.table)
importFrom(DSI,datashield.connections_find)
importFrom(cli,cli_abort)
importFrom(stats,as.formula)
importFrom(stats,na.omit)
importFrom(stats,ts)
Expand Down
27 changes: 27 additions & 0 deletions PULL_REQUEST_TEMPLATE.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
## Instructions & checklist for PR author

### Description of changes
[Add a description of what they have changed]

### Refactor instructions
- [ ] Removed `exists` and `isDefined` from clientside function and add appropriate checks on server-side function
- [ ] Removed any client-side code checking whether an object has been successfully created
- [ ] Reviewed code to determine if additional refactoring could reduce calls to server-side package
- [ ] Replaced check relating to datashield connections object with `.set_datasources()` (defined in `utils.r`)
- [ ] If relevant, replaced argument check that dataset name has been provided to `.check_df_name_provided()` (defined in `utils.r`)

### Testing instructions
- [ ] Writen client-side unit tests for unhappy flow
- [ ] Run `devtools::test(filter = "smk-|disc|arg")` and check it passes
- [ ] Run `devtools::check(args = '--no-tests')` and check it passes
- [ ] Run `devtools::build()` and check it builds without errors
- [ ] Check that the continuous integration checks pass on the pull request branch. Note that the performance test relating to your function may fail, as failure is also triggered by a dramatic improval in performance!

## Instructions & checklist for PR reviewers
- [ ] Checkout this branch as well as the corresponding branch of dsBase
- [ ] Review the code and suggest any changes
- [ ] Install the dsBase branch on an Armadillo or Opal test server
- [ ] Run `devtools::test(filter = "smk-|disc|arg")` and check it passes
- [ ] Run `devtools::check(args = '--no-tests')` and check it passes (we run tests separately to skip performance checks)
- [ ] Run `devtools::build()` and check it builds without errors
- [ ] Check that the continuous integration checks pass on the pull request branch (see above note on performance checks)
71 changes: 24 additions & 47 deletions R/ds.colnames.R
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,61 @@
#'
#' @title Produces column names of the R object in the server-side
#' @description Retrieves column names of an R object on the server-side.
#' @description Retrieves column names of an R object on the server-side.
#' This function is similar to R function \code{colnames}.
#' @details The input is restricted to the object of type \code{data.frame} or \code{matrix}.
#'
#' @details The input is restricted to the object of type \code{data.frame} or \code{matrix}.
#'
#' Server function called: \code{colnamesDS}
#' @param x a character string providing the name of the input data frame or matrix.
#' @param datasources a list of \code{\link[DSI]{DSConnection-class}} objects obtained after login.
#' @param datasources a list of \code{\link[DSI]{DSConnection-class}} objects obtained after login.
#' If the \code{datasources} argument is not specified
#' the default set of connections will be used: see \code{\link[DSI]{datashield.connections_default}}.
#' @return \code{ds.colnames} returns the column names of
#' the specified server-side data frame or matrix.
#' @return \code{ds.colnames} returns the column names of
#' the specified server-side data frame or matrix.
#' @author DataSHIELD Development Team
#' @seealso \code{\link{ds.dim}} to obtain the dimensions of a matrix or a data frame.
#' @examples
#' @examples
#' \dontrun{
#'
#'
#' ## Version 6, for version 5 see the Wiki
#' # Connecting to the Opal servers
#'
#'
#' require('DSI')
#' require('DSOpal')
#' require('dsBaseClient')
#'
#'
#' builder <- DSI::newDSLoginBuilder()
#' builder$append(server = "study1",
#' url = "http://192.168.56.100:8080/",
#' user = "administrator", password = "datashield_test&",
#' builder$append(server = "study1",
#' url = "http://192.168.56.100:8080/",
#' user = "administrator", password = "datashield_test&",
#' table = "CNSIM.CNSIM1", driver = "OpalDriver")
#' builder$append(server = "study2",
#' url = "http://192.168.56.100:8080/",
#' user = "administrator", password = "datashield_test&",
#' builder$append(server = "study2",
#' url = "http://192.168.56.100:8080/",
#' user = "administrator", password = "datashield_test&",
#' table = "CNSIM.CNSIM2", driver = "OpalDriver")
#' builder$append(server = "study3",
#' url = "http://192.168.56.100:8080/",
#' user = "administrator", password = "datashield_test&",
#' url = "http://192.168.56.100:8080/",
#' user = "administrator", password = "datashield_test&",
#' table = "CNSIM.CNSIM3", driver = "OpalDriver")
#' logindata <- builder$build()
#'
#'
#' # Log onto the remote Opal training servers
#' connections <- DSI::datashield.login(logins = logindata, assign = TRUE, symbol = "D")
#'
#' connections <- DSI::datashield.login(logins = logindata, assign = TRUE, symbol = "D")
#'
#' # Getting column names of the R objects stored in the server-side
#' ds.colnames(x = "D",
#' datasources = connections[1]) #only the first server ("study1") is used
#' # Clear the Datashield R sessions and logout
#' datashield.logout(connections)
#' datashield.logout(connections)
#' }
#' @export
#'
ds.colnames <- function(x=NULL, datasources=NULL) {

# look for DS connections
if(is.null(datasources)){
datasources <- datashield.connections_find()
}

# ensure datasources is a list of DSConnection-class
if(!(is.list(datasources) && all(unlist(lapply(datasources, function(d) {methods::is(d,"DSConnection")}))))){
stop("The 'datasources' were expected to be a list of DSConnection-class objects", call.=FALSE)
}

if(is.null(x)){
stop("Please provide the name of a data.frame or matrix!", call.=FALSE)
}

# check if the input object(s) is(are) defined in all the studies
defined <- isDefined(datasources, x)

# call the internal function that checks the input object is of the same class in all studies.
typ <- checkClass(datasources, x)

# if the input object is not a matrix or a dataframe stop
if(!('data.frame' %in% typ) & !('matrix' %in% typ)){
stop("The input vector must be of type 'data.frame' or a 'matrix'!", call.=FALSE)
}
datasources <- .set_datasources(datasources)
.check_df_name_provided(x)

cally <- call("colnamesDS", x)
column_names <- DSI::datashield.aggregate(datasources, cally)

return(column_names)

}
51 changes: 51 additions & 0 deletions R/utils.R
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
#' Retrieve datasources if not specified
#'
#' @param datasources An optional list of data sources. If not provided, the function will attempt
#' to find available data sources.
#' @importFrom DSI datashield.connections_find
#' @return A list of data sources.
#' @noRd
.get_datasources <- function(datasources) {
if (is.null(datasources)) {
datasources <- datashield.connections_find()
}
return(datasources)
}

#' Verify that the provided data sources are of class 'DSConnection'.
#'
#' @param datasources A list of data sources.
#' @importFrom cli cli_abort
#' @noRd
.verify_datasources <- function(datasources) {
is_connection_class <- sapply(datasources, function(x) inherits(unlist(x), "DSConnection"))
if (!all(is_connection_class)) {
cli_abort("The 'datasources' were expected to be a list of DSConnection-class objects")
}
}

#' Set and verify data sources.
#'
#' @param datasources An optional list of data sources. If not provided, the function will attempt
#' to find available data sources.
#' @return A list of verified data sources.
#' @noRd
.set_datasources <- function(datasources) {
datasources <- .get_datasources(datasources)
.verify_datasources(datasources)
return(datasources)
}

#' Check That a Data Frame Name Is Provided
#'
#' Internal helper that checks whether a data frame or matrix object
#' has been provided. If `NULL`, it aborts with a user-friendly error.
#'
#' @param df A data.frame or matrix.
#' @return Invisibly returns `NULL`. Called for its side effect (error checking).
#' @noRd
.check_df_name_provided <- function(df) {
if(is.null(df)){
cli_abort("Please provide the name of a data.frame or matrix!", call.=FALSE)
}
}
30 changes: 15 additions & 15 deletions man/ds.colnames.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 13 additions & 13 deletions tests/testthat/perf_files/default_perf_profile.csv
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
"refer_name","rate","lower_tolerance","upper_tolerance"
"conndisconn::perf::simple0","0.2725","0.5","2"
"ds.abs::perf::0","2.677","0.5","2"
"ds.asInteger::perf:0","2.294","0.5","2"
"ds.asList::perf:0","4.587","0.5","2"
"ds.asNumeric::perf:0","2.185","0.5","2"
"ds.assign::perf::0","5.490","0.5","2"
"ds.class::perf::combine:0","4.760","0.5","2"
"ds.colnames::perf:0","4.218","0.5","2"
"ds.exists::perf::combine:0","11.09","0.5","2"
"ds.length::perf::combine:0","9.479","0.5","2"
"ds.mean::perf::combine:0","9.650","0.5","2"
"ds.mean::perf::split:0","11.26","0.5","2"
"void::perf::void::0","46250.0","0.5","2"
"conndisconn::perf::simple0","0.2725","0.5","10"
"ds.abs::perf::0","2.677","0.5","10"
"ds.asInteger::perf:0","2.294","0.5","10"
"ds.asList::perf:0","4.587","0.5","10"
"ds.asNumeric::perf:0","2.185","0.5","10"
"ds.assign::perf::0","5.490","0.5","10"
"ds.class::perf::combine:0","4.760","0.5","10"
"ds.colnames::perf:0","4.218","0.5","10"
"ds.exists::perf::combine:0","11.09","0.5","10"
"ds.length::perf::combine:0","9.479","0.5","10"
"ds.mean::perf::combine:0","9.650","0.5","10"
"ds.mean::perf::split:0","11.26","0.5","10"
"void::perf::void::0","46250.0","0.5","10"
3 changes: 2 additions & 1 deletion tests/testthat/test-smk-ds.colnames.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,7 +47,8 @@ test_that("simple colnames", {
test_that("fails if the object does not exist", {
expect_error(
ds.colnames("non_existing_df"),
regexp = "The input object non_existing_df is not defined in sim1, sim2, sim3!",
# regexp = "object 'non_existing_df' not found",
regexp = "There are some DataSHIELD errors, list them with datashield.errors()",
ignore.case = TRUE
)
})
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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 .Rbuildignore
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,6 +15,7 @@
^docker-compose_opal\.yml$
^docker-compose\.yml$
^R/secure.global.ranking.md$
^PULL_REQUEST_TEMPLATE\.md\.R$
^_pkgdown\.yml$
^docs$
^dsBase_7.0.0\.tar\.gz$
Expand Down
4 changes: 3 additions & 1 deletion DESCRIPTION
Original file line numberDiff line numberDiff line change
Expand Up@@ -69,6 +69,7 @@ Imports:
gridExtra,
data.table,
methods,
cli,
dplyr
Suggests:
lme4,
Expand All@@ -80,7 +81,8 @@ Suggests:
DescTools,
DSOpal,
DSMolgenisArmadillo,
DSLite
DSLite,
mockery
RoxygenNote: 7.3.3
Encoding: UTF-8
Language: en-GB
2 changes: 2 additions & 0 deletions NAMESPACE
Original file line numberDiff line numberDiff line change
Expand Up@@ -119,6 +119,8 @@ export(ds.var)
export(ds.vectorCalc)
import(DSI)
import(data.table)
importFrom(DSI,datashield.connections_find)
importFrom(cli,cli_abort)
importFrom(stats,as.formula)
importFrom(stats,na.omit)
importFrom(stats,ts)
Expand Down
27 changes: 27 additions & 0 deletions PULL_REQUEST_TEMPLATE.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
## Instructions & checklist for PR author

### Description of changes
[Add a description of what they have changed]

### Refactor instructions
- [ ] Removed `exists` and `isDefined` from clientside function and add appropriate checks on server-side function
- [ ] Removed any client-side code checking whether an object has been successfully created
- [ ] Reviewed code to determine if additional refactoring could reduce calls to server-side package
- [ ] Replaced check relating to datashield connections object with `.set_datasources()` (defined in `utils.r`)
- [ ] If relevant, replaced argument check that dataset name has been provided to `.check_df_name_provided()` (defined in `utils.r`)

### Testing instructions
- [ ] Writen client-side unit tests for unhappy flow
- [ ] Run `devtools::test(filter = "smk-|disc|arg")` and check it passes
- [ ] Run `devtools::check(args = '--no-tests')` and check it passes
- [ ] Run `devtools::build()` and check it builds without errors
- [ ] Check that the continuous integration checks pass on the pull request branch. Note that the performance test relating to your function may fail, as failure is also triggered by a dramatic improval in performance!

## Instructions & checklist for PR reviewers
- [ ] Checkout this branch as well as the corresponding branch of dsBase
- [ ] Review the code and suggest any changes
- [ ] Install the dsBase branch on an Armadillo or Opal test server
- [ ] Run `devtools::test(filter = "smk-|disc|arg")` and check it passes
- [ ] Run `devtools::check(args = '--no-tests')` and check it passes (we run tests separately to skip performance checks)
- [ ] Run `devtools::build()` and check it builds without errors
- [ ] Check that the continuous integration checks pass on the pull request branch (see above note on performance checks)
71 changes: 24 additions & 47 deletions R/ds.colnames.R
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,61 @@
#'
#' @title Produces column names of the R object in the server-side
#' @description Retrieves column names of an R object on the server-side.
#' @description Retrieves column names of an R object on the server-side.
#' This function is similar to R function \code{colnames}.
#' @details The input is restricted to the object of type \code{data.frame} or \code{matrix}.
#'
#' @details The input is restricted to the object of type \code{data.frame} or \code{matrix}.
#'
#' Server function called: \code{colnamesDS}
#' @param x a character string providing the name of the input data frame or matrix.
#' @param datasources a list of \code{\link[DSI]{DSConnection-class}} objects obtained after login.
#' @param datasources a list of \code{\link[DSI]{DSConnection-class}} objects obtained after login.
#' If the \code{datasources} argument is not specified
#' the default set of connections will be used: see \code{\link[DSI]{datashield.connections_default}}.
#' @return \code{ds.colnames} returns the column names of
#' the specified server-side data frame or matrix.
#' @return \code{ds.colnames} returns the column names of
#' the specified server-side data frame or matrix.
#' @author DataSHIELD Development Team
#' @seealso \code{\link{ds.dim}} to obtain the dimensions of a matrix or a data frame.
#' @examples
#' @examples
#' \dontrun{
#'
#'
#' ## Version 6, for version 5 see the Wiki
#' # Connecting to the Opal servers
#'
#'
#' require('DSI')
#' require('DSOpal')
#' require('dsBaseClient')
#'
#'
#' builder <- DSI::newDSLoginBuilder()
#' builder$append(server = "study1",
#' url = "http://192.168.56.100:8080/",
#' user = "administrator", password = "datashield_test&",
#' builder$append(server = "study1",
#' url = "http://192.168.56.100:8080/",
#' user = "administrator", password = "datashield_test&",
#' table = "CNSIM.CNSIM1", driver = "OpalDriver")
#' builder$append(server = "study2",
#' url = "http://192.168.56.100:8080/",
#' user = "administrator", password = "datashield_test&",
#' builder$append(server = "study2",
#' url = "http://192.168.56.100:8080/",
#' user = "administrator", password = "datashield_test&",
#' table = "CNSIM.CNSIM2", driver = "OpalDriver")
#' builder$append(server = "study3",
#' url = "http://192.168.56.100:8080/",
#' user = "administrator", password = "datashield_test&",
#' url = "http://192.168.56.100:8080/",
#' user = "administrator", password = "datashield_test&",
#' table = "CNSIM.CNSIM3", driver = "OpalDriver")
#' logindata <- builder$build()
#'
#'
#' # Log onto the remote Opal training servers
#' connections <- DSI::datashield.login(logins = logindata, assign = TRUE, symbol = "D")
#'
#' connections <- DSI::datashield.login(logins = logindata, assign = TRUE, symbol = "D")
#'
#' # Getting column names of the R objects stored in the server-side
#' ds.colnames(x = "D",
#' datasources = connections[1]) #only the first server ("study1") is used
#' # Clear the Datashield R sessions and logout
#' datashield.logout(connections)
#' datashield.logout(connections)
#' }
#' @export
#'
ds.colnames <- function(x=NULL, datasources=NULL) {

# look for DS connections
if(is.null(datasources)){
datasources <- datashield.connections_find()
}

# ensure datasources is a list of DSConnection-class
if(!(is.list(datasources) && all(unlist(lapply(datasources, function(d) {methods::is(d,"DSConnection")}))))){
stop("The 'datasources' were expected to be a list of DSConnection-class objects", call.=FALSE)
}

if(is.null(x)){
stop("Please provide the name of a data.frame or matrix!", call.=FALSE)
}

# check if the input object(s) is(are) defined in all the studies
defined <- isDefined(datasources, x)

# call the internal function that checks the input object is of the same class in all studies.
typ <- checkClass(datasources, x)

# if the input object is not a matrix or a dataframe stop
if(!('data.frame' %in% typ) & !('matrix' %in% typ)){
stop("The input vector must be of type 'data.frame' or a 'matrix'!", call.=FALSE)
}
datasources <- .set_datasources(datasources)
.check_df_name_provided(x)

cally <- call("colnamesDS", x)
column_names <- DSI::datashield.aggregate(datasources, cally)

return(column_names)

}
51 changes: 51 additions & 0 deletions R/utils.R
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
#' Retrieve datasources if not specified
#'
#' @param datasources An optional list of data sources. If not provided, the function will attempt
#' to find available data sources.
#' @importFrom DSI datashield.connections_find
#' @return A list of data sources.
#' @noRd
.get_datasources <- function(datasources) {
if (is.null(datasources)) {
datasources <- datashield.connections_find()
}
return(datasources)
}

#' Verify that the provided data sources are of class 'DSConnection'.
#'
#' @param datasources A list of data sources.
#' @importFrom cli cli_abort
#' @noRd
.verify_datasources <- function(datasources) {
is_connection_class <- sapply(datasources, function(x) inherits(unlist(x), "DSConnection"))
if (!all(is_connection_class)) {
cli_abort("The 'datasources' were expected to be a list of DSConnection-class objects")
}
}

#' Set and verify data sources.
#'
#' @param datasources An optional list of data sources. If not provided, the function will attempt
#' to find available data sources.
#' @return A list of verified data sources.
#' @noRd
.set_datasources <- function(datasources) {
datasources <- .get_datasources(datasources)
.verify_datasources(datasources)
return(datasources)
}

#' Check That a Data Frame Name Is Provided
#'
#' Internal helper that checks whether a data frame or matrix object
#' has been provided. If `NULL`, it aborts with a user-friendly error.
#'
#' @param df A data.frame or matrix.
#' @return Invisibly returns `NULL`. Called for its side effect (error checking).
#' @noRd
.check_df_name_provided <- function(df) {
if(is.null(df)){
cli_abort("Please provide the name of a data.frame or matrix!", call.=FALSE)
}
}
30 changes: 15 additions & 15 deletions man/ds.colnames.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 13 additions & 13 deletions tests/testthat/perf_files/default_perf_profile.csv
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
"refer_name","rate","lower_tolerance","upper_tolerance"
"conndisconn::perf::simple0","0.2725","0.5","2"
"ds.abs::perf::0","2.677","0.5","2"
"ds.asInteger::perf:0","2.294","0.5","2"
"ds.asList::perf:0","4.587","0.5","2"
"ds.asNumeric::perf:0","2.185","0.5","2"
"ds.assign::perf::0","5.490","0.5","2"
"ds.class::perf::combine:0","4.760","0.5","2"
"ds.colnames::perf:0","4.218","0.5","2"
"ds.exists::perf::combine:0","11.09","0.5","2"
"ds.length::perf::combine:0","9.479","0.5","2"
"ds.mean::perf::combine:0","9.650","0.5","2"
"ds.mean::perf::split:0","11.26","0.5","2"
"void::perf::void::0","46250.0","0.5","2"
"conndisconn::perf::simple0","0.2725","0.5","10"
"ds.abs::perf::0","2.677","0.5","10"
"ds.asInteger::perf:0","2.294","0.5","10"
"ds.asList::perf:0","4.587","0.5","10"
"ds.asNumeric::perf:0","2.185","0.5","10"
"ds.assign::perf::0","5.490","0.5","10"
"ds.class::perf::combine:0","4.760","0.5","10"
"ds.colnames::perf:0","4.218","0.5","10"
"ds.exists::perf::combine:0","11.09","0.5","10"
"ds.length::perf::combine:0","9.479","0.5","10"
"ds.mean::perf::combine:0","9.650","0.5","10"
"ds.mean::perf::split:0","11.26","0.5","10"
"void::perf::void::0","46250.0","0.5","10"
3 changes: 2 additions & 1 deletion tests/testthat/test-smk-ds.colnames.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,7 +47,8 @@ test_that("simple colnames", {
test_that("fails if the object does not exist", {
expect_error(
ds.colnames("non_existing_df"),
regexp = "The input object non_existing_df is not defined in sim1, sim2, sim3!",
# regexp = "object 'non_existing_df' not found",
regexp = "There are some DataSHIELD errors, list them with datashield.errors()",
ignore.case = TRUE
)
})
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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 .Rbuildignore
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,6 +15,7 @@
^docker-compose_opal\.yml$
^docker-compose\.yml$
^R/secure.global.ranking.md$
^PULL_REQUEST_TEMPLATE\.md\.R$
^_pkgdown\.yml$
^docs$
^dsBase_7.0.0\.tar\.gz$
Expand Down
4 changes: 3 additions & 1 deletion DESCRIPTION
Original file line numberDiff line numberDiff line change
Expand Up@@ -69,6 +69,7 @@ Imports:
gridExtra,
data.table,
methods,
cli,
dplyr
Suggests:
lme4,
Expand All@@ -80,7 +81,8 @@ Suggests:
DescTools,
DSOpal,
DSMolgenisArmadillo,
DSLite
DSLite,
mockery
RoxygenNote: 7.3.3
Encoding: UTF-8
Language: en-GB
2 changes: 2 additions & 0 deletions NAMESPACE
Original file line numberDiff line numberDiff line change
Expand Up@@ -119,6 +119,8 @@ export(ds.var)
export(ds.vectorCalc)
import(DSI)
import(data.table)
importFrom(DSI,datashield.connections_find)
importFrom(cli,cli_abort)
importFrom(stats,as.formula)
importFrom(stats,na.omit)
importFrom(stats,ts)
Expand Down
27 changes: 27 additions & 0 deletions PULL_REQUEST_TEMPLATE.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
## Instructions & checklist for PR author

### Description of changes
[Add a description of what they have changed]

### Refactor instructions
- [ ] Removed `exists` and `isDefined` from clientside function and add appropriate checks on server-side function
- [ ] Removed any client-side code checking whether an object has been successfully created
- [ ] Reviewed code to determine if additional refactoring could reduce calls to server-side package
- [ ] Replaced check relating to datashield connections object with `.set_datasources()` (defined in `utils.r`)
- [ ] If relevant, replaced argument check that dataset name has been provided to `.check_df_name_provided()` (defined in `utils.r`)

### Testing instructions
- [ ] Writen client-side unit tests for unhappy flow
- [ ] Run `devtools::test(filter = "smk-|disc|arg")` and check it passes
- [ ] Run `devtools::check(args = '--no-tests')` and check it passes
- [ ] Run `devtools::build()` and check it builds without errors
- [ ] Check that the continuous integration checks pass on the pull request branch. Note that the performance test relating to your function may fail, as failure is also triggered by a dramatic improval in performance!

## Instructions & checklist for PR reviewers
- [ ] Checkout this branch as well as the corresponding branch of dsBase
- [ ] Review the code and suggest any changes
- [ ] Install the dsBase branch on an Armadillo or Opal test server
- [ ] Run `devtools::test(filter = "smk-|disc|arg")` and check it passes
- [ ] Run `devtools::check(args = '--no-tests')` and check it passes (we run tests separately to skip performance checks)
- [ ] Run `devtools::build()` and check it builds without errors
- [ ] Check that the continuous integration checks pass on the pull request branch (see above note on performance checks)
71 changes: 24 additions & 47 deletions R/ds.colnames.R
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,61 @@
#'
#' @title Produces column names of the R object in the server-side
#' @description Retrieves column names of an R object on the server-side.
#' @description Retrieves column names of an R object on the server-side.
#' This function is similar to R function \code{colnames}.
#' @details The input is restricted to the object of type \code{data.frame} or \code{matrix}.
#'
#' @details The input is restricted to the object of type \code{data.frame} or \code{matrix}.
#'
#' Server function called: \code{colnamesDS}
#' @param x a character string providing the name of the input data frame or matrix.
#' @param datasources a list of \code{\link[DSI]{DSConnection-class}} objects obtained after login.
#' @param datasources a list of \code{\link[DSI]{DSConnection-class}} objects obtained after login.
#' If the \code{datasources} argument is not specified
#' the default set of connections will be used: see \code{\link[DSI]{datashield.connections_default}}.
#' @return \code{ds.colnames} returns the column names of
#' the specified server-side data frame or matrix.
#' @return \code{ds.colnames} returns the column names of
#' the specified server-side data frame or matrix.
#' @author DataSHIELD Development Team
#' @seealso \code{\link{ds.dim}} to obtain the dimensions of a matrix or a data frame.
#' @examples
#' @examples
#' \dontrun{
#'
#'
#' ## Version 6, for version 5 see the Wiki
#' # Connecting to the Opal servers
#'
#'
#' require('DSI')
#' require('DSOpal')
#' require('dsBaseClient')
#'
#'
#' builder <- DSI::newDSLoginBuilder()
#' builder$append(server = "study1",
#' url = "http://192.168.56.100:8080/",
#' user = "administrator", password = "datashield_test&",
#' builder$append(server = "study1",
#' url = "http://192.168.56.100:8080/",
#' user = "administrator", password = "datashield_test&",
#' table = "CNSIM.CNSIM1", driver = "OpalDriver")
#' builder$append(server = "study2",
#' url = "http://192.168.56.100:8080/",
#' user = "administrator", password = "datashield_test&",
#' builder$append(server = "study2",
#' url = "http://192.168.56.100:8080/",
#' user = "administrator", password = "datashield_test&",
#' table = "CNSIM.CNSIM2", driver = "OpalDriver")
#' builder$append(server = "study3",
#' url = "http://192.168.56.100:8080/",
#' user = "administrator", password = "datashield_test&",
#' url = "http://192.168.56.100:8080/",
#' user = "administrator", password = "datashield_test&",
#' table = "CNSIM.CNSIM3", driver = "OpalDriver")
#' logindata <- builder$build()
#'
#'
#' # Log onto the remote Opal training servers
#' connections <- DSI::datashield.login(logins = logindata, assign = TRUE, symbol = "D")
#'
#' connections <- DSI::datashield.login(logins = logindata, assign = TRUE, symbol = "D")
#'
#' # Getting column names of the R objects stored in the server-side
#' ds.colnames(x = "D",
#' datasources = connections[1]) #only the first server ("study1") is used
#' # Clear the Datashield R sessions and logout
#' datashield.logout(connections)
#' datashield.logout(connections)
#' }
#' @export
#'
ds.colnames <- function(x=NULL, datasources=NULL) {

# look for DS connections
if(is.null(datasources)){
datasources <- datashield.connections_find()
}

# ensure datasources is a list of DSConnection-class
if(!(is.list(datasources) && all(unlist(lapply(datasources, function(d) {methods::is(d,"DSConnection")}))))){
stop("The 'datasources' were expected to be a list of DSConnection-class objects", call.=FALSE)
}

if(is.null(x)){
stop("Please provide the name of a data.frame or matrix!", call.=FALSE)
}

# check if the input object(s) is(are) defined in all the studies
defined <- isDefined(datasources, x)

# call the internal function that checks the input object is of the same class in all studies.
typ <- checkClass(datasources, x)

# if the input object is not a matrix or a dataframe stop
if(!('data.frame' %in% typ) & !('matrix' %in% typ)){
stop("The input vector must be of type 'data.frame' or a 'matrix'!", call.=FALSE)
}
datasources <- .set_datasources(datasources)
.check_df_name_provided(x)

cally <- call("colnamesDS", x)
column_names <- DSI::datashield.aggregate(datasources, cally)

return(column_names)

}
51 changes: 51 additions & 0 deletions R/utils.R
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
#' Retrieve datasources if not specified
#'
#' @param datasources An optional list of data sources. If not provided, the function will attempt
#' to find available data sources.
#' @importFrom DSI datashield.connections_find
#' @return A list of data sources.
#' @noRd
.get_datasources <- function(datasources) {
if (is.null(datasources)) {
datasources <- datashield.connections_find()
}
return(datasources)
}

#' Verify that the provided data sources are of class 'DSConnection'.
#'
#' @param datasources A list of data sources.
#' @importFrom cli cli_abort
#' @noRd
.verify_datasources <- function(datasources) {
is_connection_class <- sapply(datasources, function(x) inherits(unlist(x), "DSConnection"))
if (!all(is_connection_class)) {
cli_abort("The 'datasources' were expected to be a list of DSConnection-class objects")
}
}

#' Set and verify data sources.
#'
#' @param datasources An optional list of data sources. If not provided, the function will attempt
#' to find available data sources.
#' @return A list of verified data sources.
#' @noRd
.set_datasources <- function(datasources) {
datasources <- .get_datasources(datasources)
.verify_datasources(datasources)
return(datasources)
}

#' Check That a Data Frame Name Is Provided
#'
#' Internal helper that checks whether a data frame or matrix object
#' has been provided. If `NULL`, it aborts with a user-friendly error.
#'
#' @param df A data.frame or matrix.
#' @return Invisibly returns `NULL`. Called for its side effect (error checking).
#' @noRd
.check_df_name_provided <- function(df) {
if(is.null(df)){
cli_abort("Please provide the name of a data.frame or matrix!", call.=FALSE)
}
}
30 changes: 15 additions & 15 deletions man/ds.colnames.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 13 additions & 13 deletions tests/testthat/perf_files/default_perf_profile.csv
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
"refer_name","rate","lower_tolerance","upper_tolerance"
"conndisconn::perf::simple0","0.2725","0.5","2"
"ds.abs::perf::0","2.677","0.5","2"
"ds.asInteger::perf:0","2.294","0.5","2"
"ds.asList::perf:0","4.587","0.5","2"
"ds.asNumeric::perf:0","2.185","0.5","2"
"ds.assign::perf::0","5.490","0.5","2"
"ds.class::perf::combine:0","4.760","0.5","2"
"ds.colnames::perf:0","4.218","0.5","2"
"ds.exists::perf::combine:0","11.09","0.5","2"
"ds.length::perf::combine:0","9.479","0.5","2"
"ds.mean::perf::combine:0","9.650","0.5","2"
"ds.mean::perf::split:0","11.26","0.5","2"
"void::perf::void::0","46250.0","0.5","2"
"conndisconn::perf::simple0","0.2725","0.5","10"
"ds.abs::perf::0","2.677","0.5","10"
"ds.asInteger::perf:0","2.294","0.5","10"
"ds.asList::perf:0","4.587","0.5","10"
"ds.asNumeric::perf:0","2.185","0.5","10"
"ds.assign::perf::0","5.490","0.5","10"
"ds.class::perf::combine:0","4.760","0.5","10"
"ds.colnames::perf:0","4.218","0.5","10"
"ds.exists::perf::combine:0","11.09","0.5","10"
"ds.length::perf::combine:0","9.479","0.5","10"
"ds.mean::perf::combine:0","9.650","0.5","10"
"ds.mean::perf::split:0","11.26","0.5","10"
"void::perf::void::0","46250.0","0.5","10"
3 changes: 2 additions & 1 deletion tests/testthat/test-smk-ds.colnames.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,7 +47,8 @@ test_that("simple colnames", {
test_that("fails if the object does not exist", {
expect_error(
ds.colnames("non_existing_df"),
regexp = "The input object non_existing_df is not defined in sim1, sim2, sim3!",
# regexp = "object 'non_existing_df' not found",
regexp = "There are some DataSHIELD errors, list them with datashield.errors()",
ignore.case = TRUE
)
})
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
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 .Rbuildignore
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,6 +15,7 @@
^docker-compose_opal\.yml$
^docker-compose\.yml$
^R/secure.global.ranking.md$
^PULL_REQUEST_TEMPLATE\.md\.R$
^_pkgdown\.yml$
^docs$
^dsBase_7.0.0\.tar\.gz$
Expand Down
4 changes: 3 additions & 1 deletion DESCRIPTION
Original file line numberDiff line numberDiff line change
Expand Up@@ -69,6 +69,7 @@ Imports:
gridExtra,
data.table,
methods,
cli,
dplyr
Suggests:
lme4,
Expand All@@ -80,7 +81,8 @@ Suggests:
DescTools,
DSOpal,
DSMolgenisArmadillo,
DSLite
DSLite,
mockery
RoxygenNote: 7.3.3
Encoding: UTF-8
Language: en-GB
2 changes: 2 additions & 0 deletions NAMESPACE
Original file line numberDiff line numberDiff line change
Expand Up@@ -119,6 +119,8 @@ export(ds.var)
export(ds.vectorCalc)
import(DSI)
import(data.table)
importFrom(DSI,datashield.connections_find)
importFrom(cli,cli_abort)
importFrom(stats,as.formula)
importFrom(stats,na.omit)
importFrom(stats,ts)
Expand Down
27 changes: 27 additions & 0 deletions PULL_REQUEST_TEMPLATE.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
## Instructions & checklist for PR author

### Description of changes
[Add a description of what they have changed]

### Refactor instructions
- [ ] Removed `exists` and `isDefined` from clientside function and add appropriate checks on server-side function
- [ ] Removed any client-side code checking whether an object has been successfully created
- [ ] Reviewed code to determine if additional refactoring could reduce calls to server-side package
- [ ] Replaced check relating to datashield connections object with `.set_datasources()` (defined in `utils.r`)
- [ ] If relevant, replaced argument check that dataset name has been provided to `.check_df_name_provided()` (defined in `utils.r`)

### Testing instructions
- [ ] Writen client-side unit tests for unhappy flow
- [ ] Run `devtools::test(filter = "smk-|disc|arg")` and check it passes
- [ ] Run `devtools::check(args = '--no-tests')` and check it passes
- [ ] Run `devtools::build()` and check it builds without errors
- [ ] Check that the continuous integration checks pass on the pull request branch. Note that the performance test relating to your function may fail, as failure is also triggered by a dramatic improval in performance!

## Instructions & checklist for PR reviewers
- [ ] Checkout this branch as well as the corresponding branch of dsBase
- [ ] Review the code and suggest any changes
- [ ] Install the dsBase branch on an Armadillo or Opal test server
- [ ] Run `devtools::test(filter = "smk-|disc|arg")` and check it passes
- [ ] Run `devtools::check(args = '--no-tests')` and check it passes (we run tests separately to skip performance checks)
- [ ] Run `devtools::build()` and check it builds without errors
- [ ] Check that the continuous integration checks pass on the pull request branch (see above note on performance checks)
71 changes: 24 additions & 47 deletions R/ds.colnames.R
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,61 @@
#'
#' @title Produces column names of the R object in the server-side
#' @description Retrieves column names of an R object on the server-side.
#' @description Retrieves column names of an R object on the server-side.
#' This function is similar to R function \code{colnames}.
#' @details The input is restricted to the object of type \code{data.frame} or \code{matrix}.
#'
#' @details The input is restricted to the object of type \code{data.frame} or \code{matrix}.
#'
#' Server function called: \code{colnamesDS}
#' @param x a character string providing the name of the input data frame or matrix.
#' @param datasources a list of \code{\link[DSI]{DSConnection-class}} objects obtained after login.
#' @param datasources a list of \code{\link[DSI]{DSConnection-class}} objects obtained after login.
#' If the \code{datasources} argument is not specified
#' the default set of connections will be used: see \code{\link[DSI]{datashield.connections_default}}.
#' @return \code{ds.colnames} returns the column names of
#' the specified server-side data frame or matrix.
#' @return \code{ds.colnames} returns the column names of
#' the specified server-side data frame or matrix.
#' @author DataSHIELD Development Team
#' @seealso \code{\link{ds.dim}} to obtain the dimensions of a matrix or a data frame.
#' @examples
#' @examples
#' \dontrun{
#'
#'
#' ## Version 6, for version 5 see the Wiki
#' # Connecting to the Opal servers
#'
#'
#' require('DSI')
#' require('DSOpal')
#' require('dsBaseClient')
#'
#'
#' builder <- DSI::newDSLoginBuilder()
#' builder$append(server = "study1",
#' url = "http://192.168.56.100:8080/",
#' user = "administrator", password = "datashield_test&",
#' builder$append(server = "study1",
#' url = "http://192.168.56.100:8080/",
#' user = "administrator", password = "datashield_test&",
#' table = "CNSIM.CNSIM1", driver = "OpalDriver")
#' builder$append(server = "study2",
#' url = "http://192.168.56.100:8080/",
#' user = "administrator", password = "datashield_test&",
#' builder$append(server = "study2",
#' url = "http://192.168.56.100:8080/",
#' user = "administrator", password = "datashield_test&",
#' table = "CNSIM.CNSIM2", driver = "OpalDriver")
#' builder$append(server = "study3",
#' url = "http://192.168.56.100:8080/",
#' user = "administrator", password = "datashield_test&",
#' url = "http://192.168.56.100:8080/",
#' user = "administrator", password = "datashield_test&",
#' table = "CNSIM.CNSIM3", driver = "OpalDriver")
#' logindata <- builder$build()
#'
#'
#' # Log onto the remote Opal training servers
#' connections <- DSI::datashield.login(logins = logindata, assign = TRUE, symbol = "D")
#'
#' connections <- DSI::datashield.login(logins = logindata, assign = TRUE, symbol = "D")
#'
#' # Getting column names of the R objects stored in the server-side
#' ds.colnames(x = "D",
#' datasources = connections[1]) #only the first server ("study1") is used
#' # Clear the Datashield R sessions and logout
#' datashield.logout(connections)
#' datashield.logout(connections)
#' }
#' @export
#'
ds.colnames <- function(x=NULL, datasources=NULL) {

# look for DS connections
if(is.null(datasources)){
datasources <- datashield.connections_find()
}

# ensure datasources is a list of DSConnection-class
if(!(is.list(datasources) && all(unlist(lapply(datasources, function(d) {methods::is(d,"DSConnection")}))))){
stop("The 'datasources' were expected to be a list of DSConnection-class objects", call.=FALSE)
}

if(is.null(x)){
stop("Please provide the name of a data.frame or matrix!", call.=FALSE)
}

# check if the input object(s) is(are) defined in all the studies
defined <- isDefined(datasources, x)

# call the internal function that checks the input object is of the same class in all studies.
typ <- checkClass(datasources, x)

# if the input object is not a matrix or a dataframe stop
if(!('data.frame' %in% typ) & !('matrix' %in% typ)){
stop("The input vector must be of type 'data.frame' or a 'matrix'!", call.=FALSE)
}
datasources <- .set_datasources(datasources)
.check_df_name_provided(x)

cally <- call("colnamesDS", x)
column_names <- DSI::datashield.aggregate(datasources, cally)

return(column_names)

}
51 changes: 51 additions & 0 deletions R/utils.R
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
#' Retrieve datasources if not specified
#'
#' @param datasources An optional list of data sources. If not provided, the function will attempt
#' to find available data sources.
#' @importFrom DSI datashield.connections_find
#' @return A list of data sources.
#' @noRd
.get_datasources <- function(datasources) {
if (is.null(datasources)) {
datasources <- datashield.connections_find()
}
return(datasources)
}

#' Verify that the provided data sources are of class 'DSConnection'.
#'
#' @param datasources A list of data sources.
#' @importFrom cli cli_abort
#' @noRd
.verify_datasources <- function(datasources) {
is_connection_class <- sapply(datasources, function(x) inherits(unlist(x), "DSConnection"))
if (!all(is_connection_class)) {
cli_abort("The 'datasources' were expected to be a list of DSConnection-class objects")
}
}

#' Set and verify data sources.
#'
#' @param datasources An optional list of data sources. If not provided, the function will attempt
#' to find available data sources.
#' @return A list of verified data sources.
#' @noRd
.set_datasources <- function(datasources) {
datasources <- .get_datasources(datasources)
.verify_datasources(datasources)
return(datasources)
}

#' Check That a Data Frame Name Is Provided
#'
#' Internal helper that checks whether a data frame or matrix object
#' has been provided. If `NULL`, it aborts with a user-friendly error.
#'
#' @param df A data.frame or matrix.
#' @return Invisibly returns `NULL`. Called for its side effect (error checking).
#' @noRd
.check_df_name_provided <- function(df) {
if(is.null(df)){
cli_abort("Please provide the name of a data.frame or matrix!", call.=FALSE)
}
}
30 changes: 15 additions & 15 deletions man/ds.colnames.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 13 additions & 13 deletions tests/testthat/perf_files/default_perf_profile.csv
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
"refer_name","rate","lower_tolerance","upper_tolerance"
"conndisconn::perf::simple0","0.2725","0.5","2"
"ds.abs::perf::0","2.677","0.5","2"
"ds.asInteger::perf:0","2.294","0.5","2"
"ds.asList::perf:0","4.587","0.5","2"
"ds.asNumeric::perf:0","2.185","0.5","2"
"ds.assign::perf::0","5.490","0.5","2"
"ds.class::perf::combine:0","4.760","0.5","2"
"ds.colnames::perf:0","4.218","0.5","2"
"ds.exists::perf::combine:0","11.09","0.5","2"
"ds.length::perf::combine:0","9.479","0.5","2"
"ds.mean::perf::combine:0","9.650","0.5","2"
"ds.mean::perf::split:0","11.26","0.5","2"
"void::perf::void::0","46250.0","0.5","2"
"conndisconn::perf::simple0","0.2725","0.5","10"
"ds.abs::perf::0","2.677","0.5","10"
"ds.asInteger::perf:0","2.294","0.5","10"
"ds.asList::perf:0","4.587","0.5","10"
"ds.asNumeric::perf:0","2.185","0.5","10"
"ds.assign::perf::0","5.490","0.5","10"
"ds.class::perf::combine:0","4.760","0.5","10"
"ds.colnames::perf:0","4.218","0.5","10"
"ds.exists::perf::combine:0","11.09","0.5","10"
"ds.length::perf::combine:0","9.479","0.5","10"
"ds.mean::perf::combine:0","9.650","0.5","10"
"ds.mean::perf::split:0","11.26","0.5","10"
"void::perf::void::0","46250.0","0.5","10"
3 changes: 2 additions & 1 deletion tests/testthat/test-smk-ds.colnames.R
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,7 +47,8 @@ test_that("simple colnames", {
test_that("fails if the object does not exist", {
expect_error(
ds.colnames("non_existing_df"),
regexp = "The input object non_existing_df is not defined in sim1, sim2, sim3!",
# regexp = "object 'non_existing_df' not found",
regexp = "There are some DataSHIELD errors, list them with datashield.errors()",
ignore.case = TRUE
)
})
Expand Down
Loading
Loading