diff --git a/DESCRIPTION b/DESCRIPTION index 63bb2883..e7290850 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: clinsight Title: ClinSight -Version: 0.2.0.9005 +Version: 0.2.0.9006 Authors@R: c( person("Leonard Daniƫl", "Samson", , "lsamson@gcp-service.com", role = c("cre", "aut"), comment = c(ORCID = "0000-0002-6252-7639")), diff --git a/NEWS.md b/NEWS.md index b3dc70ab..54da0fe6 100644 --- a/NEWS.md +++ b/NEWS.md @@ -31,11 +31,17 @@ Here is `calculate_subject_status` a function with custom logic that calculates - Removed the requirement for common_forms to have a 'Name' column. In addition, a 'Name' column can be provided in study data tabs, indicating a common name per row, which will improve the query selector items (#207). - Improved branding with new ClinSight Logo, added favicon, and allowed for study logos, when available (#214). - Added options to review on form level. With form-level review, subject-level graphics and tiles will be hidden, and all data will be shown in the tables, and review is enable on all rows. All data in a form can be reviewed at once; if a user tries to do so, an additional confirmation will be requested (#198). +- Added path that updates db's `query_data` table with existing EDC queries upon initialization & update. ## Bug fixes - The event label order calculation is now calculated as intended in the rare cases where it needs to be estimated (for example when the order of occurrence of the events differs per patient) (#225). + + + + + # clinsight 0.2.0 ## Changed diff --git a/R/fct_SQLite.R b/R/fct_SQLite.R index 41f91247..edc8a10a 100644 --- a/R/fct_SQLite.R +++ b/R/fct_SQLite.R @@ -1,3 +1,54 @@ +#' Check Query Path +#' +#' Verify that the query_path supplied is in the correct format, and if it +#' isn't, fall back on the internal query_data_skeleton. If it is, then read +#' the rds file in as a data.frame +#' +#' @param query_path If it exists, provide a file.path to an RDS file, +#' containing data.frame with the same schema as `query_data_skeleton`, used +#' to populate a new database with existing queries from the EDC. +#' +#' @keywords internal +#' @return a data.frame containing EDC queries +#' +check_query_path <- function(query_path) { + + # verify that the query_path points at something that exists and is usable + query_df <- + if(is.null(query_path)) { + + # when null (the default): use skeleton + query_data_skeleton + + } else if(!file.exists(query_path)) { + + # when file doesn't exist: use skeleton + warning(paste0("Cannot find '", query_path,"'")) + query_data_skeleton + + } else if(tolower(tools::file_ext(query_path)) != "rds") { + + # when file exists, but it's not an .rds: use skeleton + warning("Invalid data format. Expecting a .rds format.") + query_data_skeleton + + } else { + + # read in the query data found at 'query_path' + readRDS(query_path) + + } + + # It query_df doesn't follow the same structure as the skeleton, dump it + qcols <- colnames(query_data_skeleton) + if(!identical(qcols, colnames(query_df))) { + warning(paste0("Data.frame read from '", query_path, + "'does match expected columns: ", paste(qcols, collapse = ", "))) + query_df <- query_data_skeleton + } + return(query_df) +} + #' Connect to database #' #' Small helper function to connect to database. This way, the connection @@ -59,6 +110,9 @@ db_temp_connect <- function(db_path, code, drv = RSQLite::SQLite()){ #' @param reviewer Character vector. Sets the reviewer in the review database. #' @param status Character vector. Sets the status in the review database. #' Defaults to `new`. +#' @param query_path If it exists, provide a file.path to an RDS file, +#' containing data.frame with the same schema as `query_data_skeleton`, used +#' to populate a new database with existing queries from the EDC. #' #' @return A database will be created. Nothing else will be returned. #' @export @@ -70,7 +124,8 @@ db_create <- function( db_path, reviewed = "No", reviewer = "", - status = "new" + status = "new", + query_path = NULL ){ stopifnot(!file.exists(db_path)) stopifnot(reviewed %in% c("Yes", "No", "")) @@ -93,9 +148,14 @@ db_create <- function( status = status ) + # verify that the query_path points at something that exists and is usable. If + # it passes all the checks, read in the data.frame to 'query_df'. If it + # doesn't, use the query_data_skeleton + query_df <- check_query_path(query_path = query_path) + new_pk_data <- list( "all_review_data" = df, - "query_data" = query_data_skeleton + "query_data" = query_df ) idx_pk_cols <- list( all_review_data = idx_cols @@ -230,6 +290,10 @@ db_add_log <- function(con, keys = c("id", idx_cols)) { #' @param common_vars A character vector containing the common key variables. #' @param edit_time_var A character vector with the column name of the edit-time #' variable. +#' @param query_path If it exists, provide a file.path to an RDS file, +#' containing data.frame with the same schema as `query_data_skeleton`, used +#' to populate a new database with existing queries from the EDC. +#' #' #' @return Nothing will be returned. #' @export @@ -239,7 +303,8 @@ db_update <- function( db_path, common_vars = c("subject_id", "event_name", "item_group", "form_repeat", "item_name"), - edit_time_var = "edit_date_time" + edit_time_var = "edit_date_time", + query_path = NULL ){ stopifnot(file.exists(db_path)) con <- get_db_connection(db_path) @@ -276,6 +341,20 @@ db_update <- function( data.frame("synch_time" = data_synch_time), overwrite = TRUE ) + + # If applicable, Dump old query table & inject it with fresh query info + query_df <- check_query_path(query_path = query_path) + + if(nrow(query_df) > 0){ + con <- get_db_connection(db_path) + rs <- DBI::dbSendStatement(con, "DELETE FROM query_data") + DBI::dbClearResult(rs) + DBI::dbAppendTable(con, "query_data", query_df) + cat("Finished updating 'query_data' table in user_db.\n\n") + } else { + cat("Did not update 'query_data' table in user_db.\n\n") + } + cat("Finished updating review data\n") } diff --git a/R/run_app.R b/R/run_app.R index eb6063ac..b25d9f89 100644 --- a/R/run_app.R +++ b/R/run_app.R @@ -27,6 +27,7 @@ run_app <- function( data <- get_golem_config("study_data") meta <- get_golem_config("meta_data") user_db <- get_golem_config("user_db") + query_path <- get_golem_config("query_data") use_shinymanager <- isTRUE(get_golem_config("user_identification") == "shinymanager") credentials_db <- get_golem_config("credentials_db") @@ -72,13 +73,13 @@ run_app <- function( is.character(user_db)) if(!file.exists(user_db)){ warning("No user database found. New database will be created") - db_create(get_review_data(data), db_path = user_db) + db_create(get_review_data(data), db_path = user_db, query_path = query_path) } else{ stopifnot("user_db version is not up to date" = identical(db_version, db_get_version(user_db))) # Skip if not needed for faster testing: if(isTRUE(get_golem_config("app_prod"))){ - db_update(get_review_data(data), db_path = user_db) + db_update(get_review_data(data), db_path = user_db, query_path = query_path) } } diff --git a/inst/golem-config.yml b/inst/golem-config.yml index 6d1748bc..997bdaa7 100644 --- a/inst/golem-config.yml +++ b/inst/golem-config.yml @@ -1,11 +1,12 @@ default: golem_name: clinsight - golem_version: 0.2.0.9005 + golem_version: 0.2.0.9006 app_prod: no user_identification: test_user study_data: !expr clinsight::clinsightful_data meta_data: !expr clinsight::metadata user_db: user_db.sqlite + query_data: null user_roles: Administrator: admin Medical Monitor: medical_monitor diff --git a/man/check_query_path.Rd b/man/check_query_path.Rd new file mode 100644 index 00000000..be00157d --- /dev/null +++ b/man/check_query_path.Rd @@ -0,0 +1,22 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/fct_SQLite.R +\name{check_query_path} +\alias{check_query_path} +\title{Check Query Path} +\usage{ +check_query_path(query_path) +} +\arguments{ +\item{query_path}{If it exists, provide a file.path to an RDS file, +containing data.frame with the same schema as \code{query_data_skeleton}, used +to populate a new database with existing queries from the EDC.} +} +\value{ +a data.frame containing EDC queries +} +\description{ +Verify that the query_path supplied is in the correct format, and if it +isn't, fall back on the internal query_data_skeleton. If it is, then read +the rds file in as a data.frame +} +\keyword{internal} diff --git a/man/db_create.Rd b/man/db_create.Rd index ec16e934..c2d1d2ed 100644 --- a/man/db_create.Rd +++ b/man/db_create.Rd @@ -4,7 +4,14 @@ \alias{db_create} \title{Create app database} \usage{ -db_create(data, db_path, reviewed = "No", reviewer = "", status = "new") +db_create( + data, + db_path, + reviewed = "No", + reviewer = "", + status = "new", + query_path = NULL +) } \arguments{ \item{data}{A data frame with review data (Usually created with @@ -20,6 +27,10 @@ database.} \item{status}{Character vector. Sets the status in the review database. Defaults to \code{new}.} + +\item{query_path}{If it exists, provide a file.path to an RDS file, +containing data.frame with the same schema as \code{query_data_skeleton}, used +to populate a new database with existing queries from the EDC.} } \value{ A database will be created. Nothing else will be returned. diff --git a/man/db_update.Rd b/man/db_update.Rd index 3236b601..19bf9c9f 100644 --- a/man/db_update.Rd +++ b/man/db_update.Rd @@ -8,7 +8,8 @@ db_update( data, db_path, common_vars = c("subject_id", "event_name", "item_group", "form_repeat", "item_name"), - edit_time_var = "edit_date_time" + edit_time_var = "edit_date_time", + query_path = NULL ) } \arguments{ @@ -20,6 +21,10 @@ db_update( \item{edit_time_var}{A character vector with the column name of the edit-time variable.} + +\item{query_path}{If it exists, provide a file.path to an RDS file, +containing data.frame with the same schema as \code{query_data_skeleton}, used +to populate a new database with existing queries from the EDC.} } \value{ Nothing will be returned.