Skip to content

Commit 30297b3

Browse files
Merge pull request #8 from datashield/v6.1-dev
V6.1 dev
2 parents 6774613 + a73bf6e commit 30297b3

91 files changed

Lines changed: 3136 additions & 20932 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

NAMESPACE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export(ds.densityGrid)
2929
export(ds.dim)
3030
export(ds.exists)
3131
export(ds.exp)
32+
export(ds.getWGSR)
3233
export(ds.glm)
3334
export(ds.glmPredict)
3435
export(ds.glmSLMA)

R/ds.getWGSR.R

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
#'
2+
#' @title Computes the WHO Growth Reference z-scores of anthropometric data
3+
#' @description Calculate WHO Growth Reference z-score for a given anthropometric measurement
4+
#' This function is similar to R function \code{getWGSR} from the \code{zscorer} package.
5+
#' @details The function calls the server-side function \code{getWGSRDS} that computes the WHO
6+
#' Growth Reference z-scores of anthropometric data for weight, height or length, MUAC (middle
7+
#' upper arm circumference), head circumference, sub-scapular skinfold and triceps skinfold.
8+
#' Note that the function might fail or return NAs when the variables are outside the ranges
9+
#' given in the WGS (WHO Child Growth Standards) reference (i.e. 45 to 120 cm for height and
10+
#' 0 to 60 months for age). It is up to the user to check the ranges and the units of their
11+
#' data.
12+
#' @param sex the name of the binary variable that indicates the sex of the subject. This must
13+
#' be coded as 1 = male and 2 = female. If in your project the variable sex has different
14+
#' levels, you should recode the levels to 1 for males and 2 for females using the
15+
#' \code{ds.recodeValues} DataSHIELD function before the use of the \code{ds.getWGSR}.
16+
#' @param firstPart Name of variable specifying:\cr
17+
#' Weight (kg) for BMI/A, W/A, W/H, or W/L\cr
18+
#' Head circumference (cm) for HC/A\cr
19+
#' Height (cm) for H/A\cr
20+
#' Length (cm) for L/A\cr
21+
#' MUAC (cm) for MUAC/A\cr
22+
#' Sub-scapular skinfold (mm) for SSF/A\cr
23+
#' Triceps skinfold (mm) for TSF/A\cr
24+
#' Give a quoted variable name as in (e.g.) "weight". Be careful with units (weight in kg;
25+
#' height, length, head circumference, and MUAC in cm; skinfolds in mm).
26+
#' @param secondPart Name of variable specifying:\cr
27+
#' Age (days) for H/A, HC/A, L/A, MUAC/A, SSF/A, or TSF/A\cr
28+
#' Height (cm) for BMI/A, or W/H\cr
29+
#' Length (cm) for W/L\cr
30+
#' Give a quoted variable name as in (e.g.) "age". Be careful with units (age in days;
31+
#' height and length in cm).
32+
#' @param index The index to be calculated and added to data. One of:\cr
33+
#' bfa BMI for age\cr
34+
#' hca Head circumference for age\cr
35+
#' hfa Height for age\cr
36+
#' lfa Length for age\cr
37+
#' mfa MUAC for age\cr
38+
#' ssa Sub-scapular skinfold for age\cr
39+
#' tsa Triceps skinfold for age\cr
40+
#' wfa Weight for age\cr
41+
#' wfh Weight for height\cr
42+
#' wfl Weight for length\cr
43+
#' Give a quoted index name as in (e.g.) "wfh".
44+
#' @param standing Variable specifying how stature was measured. If NA (default) then age (for "hfa"
45+
#' or "lfa") or height rules (for "wfh" or "wfl") will be applied. This must be coded as
46+
#' 1 = Standing; 2 = Supine; 3 = Unknown. Missing values will be recoded to 3 = Unknown.
47+
#' Give a single value (e.g."1"). If no value is specified then height and age rules will be applied.
48+
#' @param thirdPart Name of variable specifying age (in days) for BMI/A. Give a quoted variable
49+
#' name as in (e.g.) "age". Be careful with units (age in days). If age is given in different units
50+
#' you should convert it in age in days using the \code{ds.make} DataSHIELD function before the use
51+
#' of the \code{ds.getWGSR}. For example if age is given in months then the transformation is given
52+
#' by the formula $age_days=age_months*(365.25/12)$.
53+
#' @param newobj a character string that provides the name for the output variable
54+
#' that is stored on the data servers. Defaults \code{getWGSR.newobj}.
55+
#' @param datasources a list of \code{\link{DSConnection-class}} objects obtained after login.
56+
#' If the \code{datasources} argument is not specified the default set of connections will be
57+
#' used: see \code{\link{datashield.connections_default}}.
58+
#' @return \code{ds.getWGSR} assigns a vector for each study that includes the z-scores for the
59+
#' specified index. The created vectors are stored in the servers.
60+
#' @author Demetris Avraam for DataSHIELD Development Team
61+
#' @export
62+
#' @examples
63+
#' \dontrun{
64+
#'
65+
#' # Connecting to the Opal servers
66+
#'
67+
#' require('DSI')
68+
#' require('DSOpal')
69+
#' require('dsBaseClient')
70+
#'
71+
#' builder <- DSI::newDSLoginBuilder()
72+
#' builder$append(server = "study1",
73+
#' url = "http://192.168.56.100:8080/",
74+
#' user = "administrator", password = "datashield_test&",
75+
#' table = "ANTHRO.anthro1", driver = "OpalDriver")
76+
#' builder$append(server = "study2",
77+
#' url = "http://192.168.56.100:8080/",
78+
#' user = "administrator", password = "datashield_test&",
79+
#' table = "ANTHRO.anthro2", driver = "OpalDriver")
80+
#' builder$append(server = "study3",
81+
#' url = "http://192.168.56.100:8080/",
82+
#' user = "administrator", password = "datashield_test&",
83+
#' table = "ANTHRO.anthro3", driver = "OpalDriver")
84+
#'
85+
#' logindata <- builder$build()
86+
#'
87+
#' # Log onto the remote Opal training servers
88+
#' connections <- DSI::datashield.login(logins = logindata, assign = TRUE, symbol = "D")
89+
#'
90+
#' # Example 1: Generate the weight-for-height (wfh) index
91+
#' ds.getWGSR(sex = "D$sex", firstPart = "D$weight", secondPart = "D$height",
92+
#' index = "wfh", newobj = "wfh_index", datasources = connections)
93+
#'
94+
#' # Example 2: Generate the BMI for age (bfa) index
95+
#' ds.getWGSR(sex = "D$sex", firstPart = "D$weight", secondPart = "D$height",
96+
#' index = "bfa", thirdPart = "D$age", newobj = "bfa_index", datasources = connections)
97+
#'
98+
#' # clear the Datashield R sessions and logout
99+
#' datashield.logout(connections)
100+
#'
101+
#' }
102+
#'
103+
ds.getWGSR <- function(sex=NULL, firstPart=NULL, secondPart=NULL, index=NULL, standing=NA, thirdPart=NA, newobj=NULL, datasources=NULL){
104+
105+
# look for DS connections
106+
if(is.null(datasources)){
107+
datasources <- datashield.connections_find()
108+
}
109+
110+
if(is.null(sex)){
111+
stop("Please provide the column name of the 'sex' variable!", call.=FALSE)
112+
}
113+
114+
if(is.null(firstPart)){
115+
stop("Please provide the column name of the 'firstPart' variable!", call.=FALSE)
116+
}
117+
118+
if(is.null(secondPart)){
119+
stop("Please provide the column name of the 'secondPart' variable!", call.=FALSE)
120+
}
121+
122+
# if 'firstPart' or 'secondPart' are not numeric return an error message
123+
typ.firstPart <- checkClass(datasources, firstPart)
124+
typ.secondPart <- checkClass(datasources, secondPart)
125+
if(!('numeric' %in% typ.firstPart)){
126+
stop("The 'firstPart' variable must be a 'numeric' variable!", call.=FALSE)
127+
}
128+
if(!('numeric' %in% typ.secondPart)){
129+
stop("The 'secondPart' variable must be a 'numeric' variable!", call.=FALSE)
130+
}
131+
132+
if(!(index %in% c("bfa", "hca", "hfa", "lfa", "mfa", "ssa", "tsa", "wfa", "wfh", "wfl"))){
133+
stop("Please provide a correct abbreviation for the index!", call.=FALSE)
134+
}
135+
136+
# If 'thirdPart' (age) is missing for BMI-for-age return an error message
137+
if(index == "bfa" & is.na(thirdPart)) {
138+
stop("'thirdPart' variable should not be missing for index 'bfa'", call.=FALSE)
139+
}
140+
141+
# If 'thirdPart' (age) is not numeric for BMI-for-age return an error message
142+
if(index == "bfa"){
143+
typ.thirdPart <- checkClass(datasources, thirdPart)
144+
if(!('numeric' %in% typ.firstPart)){
145+
stop("The 'thirdPart' variable must be a 'numeric' variable!", call.=FALSE)
146+
}
147+
}
148+
149+
# If 'standing' is not a value either 1, 2, 3, or NA return an error message
150+
if(!(standing %in% c(NA, 1, 2, 3))) {
151+
stop("The 'standing' variable must be a numeric value either 1, 2, or 3!", call.=FALSE)
152+
}
153+
154+
# check newobj not actively declared as null
155+
if(is.null(newobj)){
156+
newobj <- "getWGSR.newobj"
157+
}
158+
159+
cally <- call("getWGSRDS", sex, firstPart, secondPart, index, standing, thirdPart)
160+
DSI::datashield.assign(datasources, newobj, cally)
161+
162+
#############################################################################################################
163+
# DataSHIELD CLIENTSIDE MODULE: CHECK KEY DATA OBJECTS SUCCESSFULLY CREATED
164+
165+
# SET APPROPRIATE PARAMETERS FOR THIS PARTICULAR FUNCTION
166+
test.obj.name <- newobj
167+
168+
# CALL SEVERSIDE FUNCTION
169+
calltext <- call("testObjExistsDS", test.obj.name)
170+
object.info <- DSI::datashield.aggregate(datasources, calltext)
171+
172+
# CHECK IN EACH SOURCE WHETHER OBJECT NAME EXISTS
173+
# AND WHETHER OBJECT PHYSICALLY EXISTS WITH A NON-NULL CLASS
174+
num.datasources <- length(object.info)
175+
176+
obj.name.exists.in.all.sources <- TRUE
177+
obj.non.null.in.all.sources <- TRUE
178+
179+
for(j in 1:num.datasources){
180+
if(!object.info[[j]]$test.obj.exists){
181+
obj.name.exists.in.all.sources <- FALSE
182+
}
183+
if(is.null(object.info[[j]]$test.obj.class) || object.info[[j]]$test.obj.class=="ABSENT"){
184+
obj.non.null.in.all.sources <- FALSE
185+
}
186+
}
187+
188+
if(obj.name.exists.in.all.sources && obj.non.null.in.all.sources){
189+
return.message <- paste0("A data object <", test.obj.name, "> has been created in all specified data sources")
190+
}else{
191+
return.message.1 <- paste0("Error: A valid data object <", test.obj.name, "> does NOT exist in ALL specified data sources")
192+
return.message.2 <- paste0("It is either ABSENT and/or has no valid content/class,see return.info above")
193+
return.message.3 <- paste0("Please use ds.ls() to identify where missing")
194+
return.message <- list(return.message.1,return.message.2,return.message.3)
195+
}
196+
197+
calltext <- call("messageDS", test.obj.name)
198+
studyside.message <- DSI::datashield.aggregate(datasources, calltext)
199+
no.errors <- TRUE
200+
for(nd in 1:num.datasources){
201+
if(studyside.message[[nd]]!="ALL OK: there are no studysideMessage(s) on this datasource"){
202+
no.errors <- FALSE
203+
}
204+
}
205+
206+
if(no.errors){
207+
validity.check <- paste0("<",test.obj.name, "> appears valid in all sources")
208+
return(list(is.object.created=return.message,validity.check=validity.check))
209+
}
210+
211+
if(!no.errors){
212+
validity.check <- paste0("<",test.obj.name,"> invalid in at least one source. See studyside.messages:")
213+
return(list(is.object.created=return.message,validity.check=validity.check,
214+
studyside.messages=studyside.message))
215+
}
216+
217+
# END OF CHECK OBJECT CREATED CORECTLY MODULE
218+
#######################################################################################################
219+
220+
}

R/ds.glmPredict.R

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -229,13 +229,13 @@ if(obj.name.exists.in.all.sources && obj.non.null.in.all.sources){ #
229229
#
230230
if(no.errors){ #
231231
validity.check<-paste0("<",test.obj.name, "> appears valid in all sources") #
232-
print(list(is.object.created=return.message,validity.check=validity.check)) #
232+
# print(list(is.object.created=return.message,validity.check=validity.check)) #
233233
} #
234234
#
235235
if(!no.errors){ #
236236
validity.check<-paste0("<",test.obj.name,"> invalid in at least one source. See studyside.messages:") #
237-
print(list(is.object.created=return.message,validity.check=validity.check, #
238-
studyside.messages=studyside.message)) #
237+
# print(list(is.object.created=return.message,validity.check=validity.check, #
238+
# studyside.messages=studyside.message)) #
239239
} #
240240
#
241241
#END OF CHECK OBJECT CREATED CORECTLY MODULE #

R/ds.recodeValues.R

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
#' One particular use of
88
#' \code{ds.recodeValues} is to convert NAs to an explicit value or vice-versa.
99
#'
10-
#' The argument \code{force.output.formata} can be specified in 3 ways: \cr
11-
#' (1) \code{force.output.formata = "numeric"} the output
10+
#' The argument \code{force.output.format} can be specified in 3 ways: \cr
11+
#' (1) \code{force.output.format = "numeric"} the output
1212
#' vector will be of type numeric and any non-numeric values in
1313
#' \code{new.values.vector} will appear as \code{NaN} in the recoded vector.\cr
1414
#' (2) \code{force.output.format = "character"} all values

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,4 @@ Detailed instructions on how to install DataSHIELD are at https://www.datashield
3232
| obiba CRAN | Where you probably should install DataSHIELD from. |
3333
| releases | Stable releases. |
3434
| master branch | Mostly in sync with the latest release, changes rarely. |
35-
| v6.0-dev branch (or similar) | Bleeding edge development. We make no claims that this branch is fully functional at any given time. |
35+
| v6.1-dev branch (or similar) | Bleeding edge development. We make no claims that this branch is fully functional at any given time. |

azure-pipelines.yml

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ variables:
2929
projectName: 'dsBaseClient'
3030
branchName: $(Build.SourceBranchName)
3131
test_filter: '*'
32+
_r_check_system_clock_: 0
3233

3334

3435

@@ -400,6 +401,59 @@ jobs:
400401
condition: succeededOrFailed()
401402
402403
404+
#####################################################################################
405+
# Essentially run devtools::test() on the checked out code for bug reporting.
406+
# The actual command is vary convoluted as it had to do some things
407+
# which are not default behaviour: output the results to a JUnit xml file, not stop
408+
# when a small number of errors have happened, run through the code coverage tool.
409+
# TODO: Tidy up variable names - use timestamps here.
410+
- bash: |
411+
412+
# junit reporter output is to test_results_bug.xml
413+
sudo R -q -e '
414+
library(testthat);
415+
output_file <- file("test_console_output_bug.txt");
416+
sink(output_file);
417+
sink(output_file, type = "message");
418+
junit_rep <- JunitReporter$new(file = "test_results_bug.xml");
419+
progress_rep <- ProgressReporter$new(max_failures = 999999);
420+
multi_rep <- MultiReporter$new(reporters = list(progress_rep, junit_rep));
421+
testthat::test_package("$(projectName)", filter = "__bug-|datachk_bug-|smk_bug-|arg_bug-|disc_bug-|smk_expt_bug-|expt_bug-|math_bug-", reporter = multi_rep, stop_on_failure = FALSE)'
422+
423+
cat test_console_output_bug.txt
424+
425+
mv test_results_bug.xml $(Pipeline.Workspace)/logs
426+
427+
workingDirectory: $(Pipeline.Workspace)/dsBaseClient
428+
displayName: 'Bug report output'
429+
condition: succeededOrFailed()
430+
431+
432+
#####################################################################################
433+
# Parse the JUnit file to see if there are any errors/warnings. If there are then
434+
# echo them so finding bugs should be easier.
435+
# This should run even if previous steps have failed.
436+
- bash: |
437+
438+
# Strip out when error and failure = 0 and count the number of times it does not.
439+
issue_count=$(sed 's/failures="0" errors="0"//' test_results_bug.xml | sed 's/errors="0" failures="0"//' | grep --count errors=)
440+
echo "Number of testsuites with issues: "$issue_count
441+
echo "Testsuites with issues:"
442+
sed 's/failures="0" errors="0"//' test_results_bug.xml | sed 's/errors="0" failures="0"//' | grep errors= > issues.log
443+
cat issues.log
444+
no_issue_count=$(sed 's/failures="0" errors="0"//' test_results_bug.xml | sed 's/errors="0" failures="0"//' | grep -v --count errors=)
445+
echo
446+
echo "Number of testsuites with no issues: "$no_issue_count
447+
echo "Testsuites with issues:"
448+
sed 's/failures="0" errors="0"//' test_results_bug.xml | sed 's/errors="0" failures="0"//' | grep "<testsuite " | grep -v errors= > no_issues.log
449+
cat no_issues.log
450+
exit 0
451+
452+
workingDirectory: $(Pipeline.Workspace)/logs
453+
displayName: 'Bug summary report output'
454+
condition: succeededOrFailed()
455+
456+
403457
#####################################################################################
404458
# Output some important version numbers to file. This gets added to the testStatus
405459
# commit so it can be parsed and used on the status table.

0 commit comments

Comments
 (0)