From 3cfdb66a327a9c076b6064b891390af4fc936579 Mon Sep 17 00:00:00 2001 From: Andreas Gammelgaard Damsbo Date: Fri, 15 Nov 2024 20:42:25 +0100 Subject: [PATCH] included shiny app with package, bslib, documentation, fixing NA bug --- DESCRIPTION | 7 +- NAMESPACE | 2 - NEWS.md | 10 +- R/shiny_cast.R | 49 +- README.md | 6 + app/ui.R | 144 ------ data-raw/data-upload-examples.R | 8 + data-raw/redcapcast_data.R | 2 + data/redcapcast_data.csv | 26 ++ .../shinyapps.io/agdamsbo/redcapcast.dcf | 2 +- {app => inst/shiny-examples/casting}/server.R | 23 +- inst/shiny-examples/casting/ui.R | 104 +++++ .../shiny-examples/casting/www/SHINYCAST.html | 432 ++++++++++++++++++ inst/shiny-examples/casting/www/SHINYCAST.md | 65 +++ .../shiny-examples/casting}/www/logo.png | Bin man/REDCapCAST-package.Rd | 4 +- man/server_factory.Rd | 14 - man/shiny_cast.Rd | 7 +- man/ui_factory.Rd | 14 - renv.lock | 110 ++--- renv/activate.R | 10 +- 21 files changed, 768 insertions(+), 271 deletions(-) delete mode 100644 app/ui.R create mode 100644 data/redcapcast_data.csv rename {app => inst/shiny-examples/casting}/rsconnect/shinyapps.io/agdamsbo/redcapcast.dcf (91%) rename {app => inst/shiny-examples/casting}/server.R (80%) create mode 100644 inst/shiny-examples/casting/ui.R create mode 100644 inst/shiny-examples/casting/www/SHINYCAST.html create mode 100644 inst/shiny-examples/casting/www/SHINYCAST.md rename {app => inst/shiny-examples/casting}/www/logo.png (100%) delete mode 100644 man/server_factory.Rd delete mode 100644 man/ui_factory.Rd diff --git a/DESCRIPTION b/DESCRIPTION index 6eac03b..44fe2de 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: REDCapCAST -Title: REDCap Castellated Data Handling -Version: 24.10.3 +Title: REDCap Castellated Data Handling And Metadata Casting +Version: 24.11.1 Authors@R: c( person("Andreas Gammelgaard", "Damsbo", email = "agdamsbo@clin.au.dk", role = c("aut", "cre"),comment = c(ORCID = "0000-0002-7559-1154")), @@ -11,6 +11,7 @@ Description: Originally forked from the R part of 'REDCapRITS' by Paul Egeler. 'REDCap' database casting and handling of castellated data when using repeated instruments and longitudinal projects. Keeps a focused data export approach, by allowing to only export required data from the database. + Also for casting new REDCap databases based on datasets from other sources. 'REDCap' (Research Electronic Data Capture) is a secure, web-based software platform designed to support data capture for research studies, providing 1) an intuitive interface for validated data capture; 2) audit trails for @@ -36,7 +37,7 @@ Suggests: spelling, glue, rhub, - shinythemes + bslib License: GPL (>= 3) Encoding: UTF-8 LazyData: true diff --git a/NAMESPACE b/NAMESPACE index 12fa76a..36cc88d 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -30,11 +30,9 @@ export(read_redcap_instrument) export(read_redcap_tables) export(redcap_wider) export(sanitize_split) -export(server_factory) export(shiny_cast) export(split_non_repeating_forms) export(strsplitx) -export(ui_factory) importFrom(REDCapR,redcap_event_instruments) importFrom(REDCapR,redcap_metadata_read) importFrom(REDCapR,redcap_read) diff --git a/NEWS.md b/NEWS.md index 07d35a3..59311b4 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,17 +1,25 @@ -# REDCapCAST 24.10.4 +# REDCapCAST 24.11.1 Revised tests. +Documentation has been slightly updated to highlight the shiny app for casting REDCap metadata. I am working on hosting my own Shiny Server. + ### Functions: * Bug: 'form.name' specified to 'ds2dd_detailed()' was ignored. Corrected to only be ignored if 'form.sep' is specified. Added handling of re-occurring `form.sep` pattern. * New: `export_redcap_instrument()` is a new version of `create_instrument_meta()`, that will only export a single instrument. Multiple instrument export can be done with `lapply()` or `purrr::map()`. This allows for inclusion of this functionality in the Shiny implementation and is easier to handle. `create_instrument_meta()` is deprecated. +* Improved: `shiny_cast()` app has been updated to actually work if you install the package and not clones the whole repository. + ### Shiny: +* New: Major overhaul of the app interface with the introduction of `bslib` for building the page. Also Detailed documentation added for the app workflow. + * New: Export a REDCap instrument ready to add to your database based on an uploaded spreadsheet. This is thanks to the `export_redcap_instrument()` function. THis functionality is intended for projects in production and adding instruments should be handled manually and not by API upload. +* Bug: Export datadictionary with "" instead of "NA" for NAs. Upload to REDCap failed. Not anymore. + The shiny implementation is included with this package. Implementing in shinylive may be looked into again later. # REDCapCAST 24.10.3 diff --git a/R/shiny_cast.R b/R/shiny_cast.R index b0c9f9d..f0b0f15 100644 --- a/R/shiny_cast.R +++ b/R/shiny_cast.R @@ -1,40 +1,39 @@ -utils::globalVariables(c("server")) -#' Shiny server factory -#' -#' @return shiny server -#' @export -server_factory <- function() { - source(here::here("app/server.R")) - server -} - -#' UI factory for shiny app -#' -#' @return shiny ui -#' @export -ui_factory <- function() { - # require(ggplot2) - source(here::here("app/ui.R")) -} - #' Launch the included Shiny-app for database casting and upload #' +#' @description +#' Wraps shiny::runApp() +#' +#' @param ... Arguments passed to shiny::runApp() +#' #' @return shiny app #' @export #' #' @examples #' # shiny_cast() #' -shiny_cast <- function() { - # shiny::runApp(appDir = here::here("app/"), launch.browser = TRUE) +shiny_cast <- function(...) { + appDir <- system.file("shiny-examples", "casting", package = "REDCapCAST") + if (appDir == "") { + stop("Could not find example directory. Try re-installing `REDCapCAST`.", call. = FALSE) + } - shiny::shinyApp( - ui_factory(), - server_factory() - ) + shiny::runApp(appDir = appDir, ...) + + # This is from the VarSelLCM + # shiny_cast2 <- function(X){ + # check.results(X) + # G <- .GlobalEnv + # assign("resVSLC", X, envir=G) + # a=shiny::runApp(system.file(package="REDCapCAST"),launch.browser = TRUE) + # return(invisible(a)) + # } + + # shiny::runApp(appDir = here::here("app/"),...) + # ## Need adjustments to run anywhere } + #' Helper to import files correctly #' #' @param filenames file names diff --git a/README.md b/README.md index a2884ac..7aa3087 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,12 @@ require("remotes") remotes::install_github("agdamsbo/REDCapCAST") ``` +Launch the REDCapCAST app interface directly on your own machine: + +``` +REDCapCAST::shiny_cast() +``` + ## Code of Conduct Please note that the REDCapCAST project is released with a [Contributor Code of Conduct](https://agdamsbo.github.io/REDCapCAST/CODE_OF_CONDUCT.html). By contributing to this project, you agree to abide by its terms. diff --git a/app/ui.R b/app/ui.R deleted file mode 100644 index 636954e..0000000 --- a/app/ui.R +++ /dev/null @@ -1,144 +0,0 @@ -ui <- shiny::shinyUI( - shiny::fluidPage( - theme = shinythemes::shinytheme("flatly"), - - ## ----------------------------------------------------------------------------- - ## Application title - ## ----------------------------------------------------------------------------- - - - # customHeaderPanel(title = "REDCapCAST: data base creation and data upload from data set file", - # windowTitle = "REDCap database creator" - # ), - - shiny::titlePanel( - title = shiny::div( - shiny::a(shiny::img(src = "logo.png"), href = "https://agdamsbo.github.io/REDCapCAST"), - "Easy REDCap database creation" - ), - windowTitle = "REDCap database creator" - ), - shiny::h4( - "THese are the functionalities to create and migrate data from a spreadsheet to a REDCap database:", - shiny::br(), - "1) create a REDCap data dictionary or instrument based on a spreadsheet (.csv/.xls(x)/.dta/.ods) and", - shiny::br(), - "2) upload said database file on a given REDCap server and upload the dataset via API access or download for all manual upload." - ), - - - ## ----------------------------------------------------------------------------- - ## Side panel - ## ----------------------------------------------------------------------------- - - shiny::sidebarPanel( - shiny::h4("1) REDCap datadictionary and compatible dataset"), - shiny::fileInput("ds", "Choose data file", - multiple = FALSE, - accept = c( - ".csv", - ".xls", - ".xlsx", - ".dta", - ".ods" - ) - ), - shiny::h6("Below you can download the dataset formatted for upload and the - corresponding data dictionary for a new data base, if you want to upload manually."), - # Button - shiny::downloadButton("downloadData", "Download data"), - - # Button - shiny::downloadButton("downloadMeta", "Download data dictionary"), - - # Button - shiny::downloadButton("downloadInstrument", "Download as instrument"), - - - # Horizontal line ---- - shiny::tags$hr(), - shiny::h4("2) REDCap upload"), - shiny::h6("This tool is usable for now. Detailed instructions are coming."), - shiny::textInput( - inputId = "uri", - label = "URI", - value = "https://redcap.your.institution/api/" - ), - shiny::textInput( - inputId = "api", - label = "API key", - value = "" - ), - shiny::h6("An API key is an access key to the REDCap database. Please", shiny::a("see here for directions", href = "https://www.iths.org/news/redcap-tip/redcap-api-101/"), " to obtain an API key for your project."), - shiny::actionButton( - inputId = "upload.meta", - label = "Upload datadictionary", icon = shiny::icon("book-bookmark") - ), - shiny::h6("Please note, that before uploading any real data, put your project - into production mode."), - shiny::actionButton( - inputId = "upload.data", - label = "Upload data", icon = shiny::icon("upload") - ), - - # Horizontal line ---- - shiny::tags$hr() - ), - shiny::mainPanel( - shiny::tabsetPanel( - - ## ----------------------------------------------------------------------------- - ## Summary tab - ## ----------------------------------------------------------------------------- - shiny::tabPanel( - "Summary", - shiny::h3("Data overview (first 20)"), - shiny::htmlOutput("data.tbl", container = shiny::span), - shiny::h3("Dictionary overview"), - shiny::htmlOutput("meta.tbl", container = shiny::span) - ), - ## ----------------------------------------------------------------------------- - ## Upload tab - ## ----------------------------------------------------------------------------- - shiny::tabPanel( - "Upload", - shiny::h3("Meta upload overview"), - shiny::htmlOutput("upload.meta.print", container = shiny::span), - shiny::h3("Data upload overview"), - shiny::htmlOutput("upload.data.print", container = shiny::span) - ) - ) - ), - - - # close sidebarLayout - - shiny::br(), - shiny::br(), - shiny::br(), - shiny::br(), - shiny::hr(), - shiny::tags$footer(shiny::strong("Disclaimer: "), - "This tool is aimed at demonstrating use of REDCapCAST. The app can be run locally or on a hosted server (will save no data anywhere). No responsibility for data loss or any other problems will be taken. Please contact me for support.", - shiny::br(), - shiny::a("License: GPL-3+", href = "https://agdamsbo.github.io/REDCapCAST/LICENSE.html"), - "|", - shiny::a("agdamsbo/REDCapCAST", href = "https://agdamsbo.github.io/REDCapCAST"), - "|", - shiny::a("Source", href = "https://github.com/agdamsbo/REDCapCAST"), - "|", - shiny::a("Contact", href = "https://andreas.gdamsbo.dk"), - align = "center", - style = " - position:fixed; - bottom:40px; - width:100%; - height:20px; - color: black; - padding: 0px; - background-color: White; - z-index: 100; - " - ) - ) -) diff --git a/data-raw/data-upload-examples.R b/data-raw/data-upload-examples.R index ecb8964..3c94b48 100644 --- a/data-raw/data-upload-examples.R +++ b/data-raw/data-upload-examples.R @@ -9,3 +9,11 @@ mtcars_redcap |> write.csv(here::here("data/mtcars_redcap.csv"), row.names = FALSE) usethis::use_data(mtcars_redcap, overwrite = TRUE) + +gtsummary::trial|> + dplyr::mutate( + record_id = dplyr::row_number() + ) |> + dplyr::select(record_id, dplyr::everything())|> + write.csv(here::here("drafting/trials_redcap.csv"), row.names = FALSE) + diff --git a/data-raw/redcapcast_data.R b/data-raw/redcapcast_data.R index f924df5..d6b8a63 100644 --- a/data-raw/redcapcast_data.R +++ b/data-raw/redcapcast_data.R @@ -11,3 +11,5 @@ redcapcast_data <- REDCapR::redcap_read( # widen.data = FALSE) usethis::use_data(redcapcast_data, overwrite = TRUE) + +write.csv(redcapcast_data,here::here("data/redcapcast_data.csv"),row.names = FALSE) diff --git a/data/redcapcast_data.csv b/data/redcapcast_data.csv new file mode 100644 index 0000000..dfa5580 --- /dev/null +++ b/data/redcapcast_data.csv @@ -0,0 +1,26 @@ +"record_id","redcap_event_name","redcap_repeat_instrument","redcap_repeat_instance","cpr","inclusion","inclusion_time","dob","age","age_integer","sex","cohabitation","hypertension","diabetes","region","baseline_data_start_complete","mrs_assessed","mrs_date","mrs_score","mrs_complete","con_mrs","con_calc","consensus_complete","event_datetime","event_age","event_type","new_event_complete" +1,"inclusion",NA,NA,"1203401OB4",2023-03-13,12:38:49,1940-03-12,83.0023888238636,83,"female","Yes","No","Yes","East","Incomplete","Yes",2023-03-13,1,"Incomplete",NA,NA,NA,NA,NA,NA,NA +2,"inclusion",NA,NA,"0102342303",2023-03-01,10:38:57,1934-02-01,89.0778044723711,89,"male","Yes","No","No","South","Incomplete","Yes",2023-03-07,1,"Incomplete",NA,NA,NA,NA,NA,NA,NA +2,"follow1",NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,"Yes",2023-03-09,3,"Incomplete",NA,NA,"Incomplete",NA,NA,NA,NA +2,"follow1","New Event (?)",1,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,2024-01-18 12:49:42,NA,"TIA","Incomplete" +3,"inclusion",NA,NA,"2301569823",2022-03-08,12:01:07,1956-01-23,66.1231921257795,66,"male","No","Yes","Yes","North","Incomplete",NA,NA,NA,"Incomplete",NA,NA,NA,NA,NA,NA,NA +3,"follow1",NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,"Yes",2022-08-16,2,"Incomplete",NA,NA,"Incomplete",NA,NA,NA,NA +3,"follow2",NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,"Yes",2023-03-13,1,"Incomplete",NA,NA,"Incomplete",NA,NA,NA,NA +3,"follow1","New Event (?)",1,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,2024-01-18 12:49:58,NA,"AIS","Incomplete" +3,"follow1","New Event (?)",2,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,2024-01-18 12:50:01,NA,"ICH","Incomplete" +3,"follow2","New Event (?)",1,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,2024-01-18 12:50:05,NA,"ICH","Incomplete" +3,"follow2","New Event (?)",2,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,2024-01-18 12:50:07,NA,"TIA","Incomplete" +3,"follow2","New Event (?)",3,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,2024-01-18 12:50:09,NA,"AIS","Incomplete" +4,"inclusion",NA,NA,"0204051342",2023-03-14,20:39:19,1905-04-02,117.949033861065,117,"female",NA,NA,NA,NA,"Incomplete",NA,NA,NA,"Incomplete",NA,NA,NA,NA,NA,NA,NA +4,"follow1",NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,"Incomplete",NA,NA,"Incomplete",NA,NA,NA,NA +4,"follow2",NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,"Incomplete",NA,NA,"Incomplete",NA,NA,NA,NA +4,"follow1","New Event (?)",1,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,2001-04-11 08:39:05,96,"TIA","Complete" +4,"follow1","New Event (?)",2,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,2010-04-11 08:39:25,105,"TIA","Complete" +4,"follow2","New Event (?)",1,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,2024-01-18 12:50:19,118,"AIS","Complete" +4,"follow2","New Event (?)",2,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,2024-01-18 12:50:22,118,"ICH","Incomplete" +4,"follow2","New Event (?)",3,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,2024-01-18 12:50:24,118,"Unknown","Complete" +5,"inclusion",NA,NA,"0201976043",2023-03-23,08:50:31,1897-01-02,126.21751302217,126,"male","No","Yes","Yes","East","Complete",NA,NA,NA,"Incomplete",NA,NA,NA,NA,NA,NA,NA +5,"follow1",NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,"Incomplete",NA,NA,"Incomplete",NA,NA,NA,NA +5,"follow1","New Event (?)",1,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,2024-04-11 09:00:33,127,"AIS","Complete" +5,"follow1","New Event (?)",2,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,2024-04-11 09:00:41,127,"ICH","Complete" +6,"inclusion",NA,NA,"1202320122",2024-01-25,08:49:28,1932-02-12,91.952606829709,91,"female","No","Yes","No","East","Complete",NA,NA,NA,"Incomplete",NA,NA,NA,NA,NA,NA,NA diff --git a/app/rsconnect/shinyapps.io/agdamsbo/redcapcast.dcf b/inst/shiny-examples/casting/rsconnect/shinyapps.io/agdamsbo/redcapcast.dcf similarity index 91% rename from app/rsconnect/shinyapps.io/agdamsbo/redcapcast.dcf rename to inst/shiny-examples/casting/rsconnect/shinyapps.io/agdamsbo/redcapcast.dcf index 1607a80..239d88a 100644 --- a/app/rsconnect/shinyapps.io/agdamsbo/redcapcast.dcf +++ b/inst/shiny-examples/casting/rsconnect/shinyapps.io/agdamsbo/redcapcast.dcf @@ -5,6 +5,6 @@ account: agdamsbo server: shinyapps.io hostUrl: https://api.shinyapps.io/v1 appId: 11351429 -bundleId: 9264087 +bundleId: 9285702 url: https://agdamsbo.shinyapps.io/redcapcast/ version: 1 diff --git a/app/server.R b/inst/shiny-examples/casting/server.R similarity index 80% rename from app/server.R rename to inst/shiny-examples/casting/server.R index 93fffbc..6371e06 100644 --- a/app/server.R +++ b/inst/shiny-examples/casting/server.R @@ -1,9 +1,12 @@ server <- function(input, output, session) { require(REDCapCAST) + # bslib::bs_themer() + dat <- shiny::reactive({ shiny::req(input$ds) + output_staging$file <- "loaded" read_input(input$ds$datapath) }) @@ -29,15 +32,15 @@ server <- function(input, output, session) { output$downloadData <- shiny::downloadHandler( filename = "data_ready.csv", content = function(file) { - write.csv(purrr::pluck(dd(), "data"), file, row.names = FALSE) + write.csv(purrr::pluck(dd(), "data"), file, row.names = FALSE,na = "") } ) # Downloadable csv of data dictionary ---- output$downloadMeta <- shiny::downloadHandler( - filename = "dictionary_ready.csv", + filename = "datadictionary_ready.csv", content = function(file) { - write.csv(purrr::pluck(dd(), "meta"), file, row.names = FALSE) + write.csv(purrr::pluck(dd(), "meta"), file, row.names = FALSE,na = "") } ) @@ -45,12 +48,22 @@ server <- function(input, output, session) { output$downloadInstrument <- shiny::downloadHandler( filename = paste0("REDCapCAST_instrument",Sys.Date(),".zip"), content = function(file) { - create_instrument_meta_single(purrr::pluck(dd(), "meta"), file) + export_redcap_instrument(purrr::pluck(dd(), "meta"), file) } ) output_staging <- shiny::reactiveValues() - output_staging$meta <- output_staging$data <- NA + output_staging$meta <- output_staging$data <- output_staging$file <- NA + + output$uploaded <- shiny::reactive({ + if (is.na(output_staging$file)) { + "no" + } else { + "yes" + } + }) + + shiny::outputOptions(output, "uploaded", suspendWhenHidden = FALSE) shiny::observeEvent(input$upload.meta,{ upload_meta() }) diff --git a/inst/shiny-examples/casting/ui.R b/inst/shiny-examples/casting/ui.R new file mode 100644 index 0000000..a5bff8d --- /dev/null +++ b/inst/shiny-examples/casting/ui.R @@ -0,0 +1,104 @@ +ui <- + bslib::page( + theme = bslib::bs_theme(preset = "united"), + title = "REDCap database creator", + bslib::page_navbar( + title = "Easy REDCap database creation", + sidebar = bslib::sidebar( + shiny::h5("1) Database meta data"), + shiny::fileInput( + inputId = "ds", + label = "Upload spreadsheet", + multiple = FALSE, + accept = c( + ".csv", + ".xls", + ".xlsx", + ".dta", + ".ods" + ) + ), + conditionalPanel( + condition = "output.uploaded=='yes'", + shiny::helpText("Below you can download the dataset formatted for upload and the + corresponding data dictionary for a new data base, if you want to upload manually."), + # Button + shiny::downloadButton("downloadData", "Download adjusted data"), + + # Button + shiny::downloadButton("downloadMeta", "Download data dictionary"), + + # Button + shiny::downloadButton("downloadInstrument", "Download as instrument"), + + # Horizontal line ---- + shiny::tags$hr(), + shiny::radioButtons( + inputId = "upload_redcap", + label = "Upload directly to REDCap server?", + selected = "no", + inline = TRUE, + choices = list( + "No" = "no", + "Yes" = "yes" + ) + ), + shiny::conditionalPanel( + condition = "input.upload_redcap=='yes'", + shiny::h4("2) Data base upload"), + shiny::helpText("This tool is usable for now. Detailed instructions are coming."), + shiny::textInput( + inputId = "uri", + label = "URI", + value = "https://redcap.your.institution/api/" + ), + shiny::textInput( + inputId = "api", + label = "API key", + value = "" + ), + shiny::helpText("An API key is an access key to the REDCap database. Please", shiny::a("see here for directions", href = "https://www.iths.org/news/redcap-tip/redcap-api-101/"), " to obtain an API key for your project."), + shiny::actionButton( + inputId = "upload.meta", + label = "Upload datadictionary", icon = shiny::icon("book-bookmark") + ), + shiny::helpText("Please note, that before uploading any real data, put your project + into production mode."), + shiny::actionButton( + inputId = "upload.data", + label = "Upload data", icon = shiny::icon("upload") + ) + ) + ), + shiny::br(), + shiny::br(), + shiny::br(), + shiny::p( + "License: ",shiny::a("GPL-3+", href = "https://agdamsbo.github.io/REDCapCAST/LICENSE.html")), + shiny::p( + shiny::a("Package documentation", href = "https://agdamsbo.github.io/REDCapCAST")) + + ), + bslib::nav_panel( + title = "Intro", + shiny::markdown(readLines("www/SHINYCAST.md")), + shiny::br() + ), + bslib::nav_spacer(), + bslib::nav_panel( + title = "Data preview", + shiny::htmlOutput(outputId = "data.tbl", container = shiny::span) + ), + bslib::nav_panel( + title = "Dictionary overview", + shiny::htmlOutput(outputId = "meta.tbl", container = shiny::span) + ), + bslib::nav_panel( + title = "Upload", + shiny::h3("Meta upload overview"), + shiny::htmlOutput(outputId = "upload.meta.print", container = shiny::span), + shiny::h3("Data upload overview"), + shiny::htmlOutput(outputId = "upload.data.print", container = shiny::span) + ) + ) + ) diff --git a/inst/shiny-examples/casting/www/SHINYCAST.html b/inst/shiny-examples/casting/www/SHINYCAST.html new file mode 100644 index 0000000..c01df45 --- /dev/null +++ b/inst/shiny-examples/casting/www/SHINYCAST.html @@ -0,0 +1,432 @@ + + + + + + + + + + + + + +SHINYCAST + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + +
+

Introduction

+

Welcome to the REDCap database casting tool. This is the introduction +for the graphical app (build in shiny), that allows you to create a new +REDCap database or instrument based on your spreadsheet.

+
+

Disclaimer

+

This tool is aimed at demonstrating use of REDCapCAST. The app can be +run locally or on a hosted server (will save no data anywhere). No +responsibility for data loss or any other problems will be taken. You +are very welcome to contact me for support.

+
+
+

Options

+

These are the functionalities to create and migrate data from a +spreadsheet to a REDCap database:

+
    +
  1. create a REDCap data dictionary or instrument based on a +spreadsheet (.csv/.xls(x)/.dta/.ods) and,

  2. +
  3. upload said database file on a given REDCap server and upload the +dataset via API access or download for all manual upload.

  4. +
+
+
+

Background

+
+
+

Use and feedback

+
+
+ + + + +
+ + + + + + + + + + + + + + + diff --git a/inst/shiny-examples/casting/www/SHINYCAST.md b/inst/shiny-examples/casting/www/SHINYCAST.md new file mode 100644 index 0000000..877147e --- /dev/null +++ b/inst/shiny-examples/casting/www/SHINYCAST.md @@ -0,0 +1,65 @@ +# ![](logo.png) REDCapCAST app + +Welcome to the REDCapCAST app to create/cast REDCap database metadata. This is app allows you to create a new REDCap data base or instrument based on a simple spreadsheet. + +## Disclaimer + +This tool is aimed at demonstrating use of REDCapCAST. The app can be run locally or on a hosted server (will save no data anywhere). No responsibility for data loss or any other problems will be taken. + +Also, this tool will not produce a ready-for-prime-time database, but it will be a comprehensive framework with suggestions for data-classes. You will need to go through your database afterwards and take your time to ensure everything is as you'd expect and work as intended. + +## Overview + +The functions of this app can be described in two parts: + +1. create REDCap metadata files like data dictionary or instrument based on a spreadsheet (.csv/.xls(x)/.dta/.ods) for download and manual upload to your REDCap server or + +2. upload the created database file and data to a given REDCap server via API access. + +Below, each step is described in more details. + +## Getting started + +On the left, you initially just find one single option to upload a spreadsheet. Having done this, you can then preview the uploaded data and generated data dictionary by selecting the relevant tab on the top right. + +### REDCap database files creation + +The spreadsheet column names will be adjusted to comply with REDCap naming criteria, and a renamed (adjusted) spreadsheet can be downloaded. + +Based on the uploaded spreadsheet, the app will make a qualified guess on data classes and if the data is labelled (like .rda or .dta) all this information will be included in the data dictionary file. The default data format is "text". + +If you want to add data to an existing database, an instrument can be created. This metadata file is identical to a data dictionary, but does not include a "record_id" field and is packaged as a .zip file, which is uploaded in the "Designer" interface in REDCap. + +### Transferring directly to REDCap database + +Based on the API-functions in REDCap, you can upload your data dictionary and renamed data directly from this interface (no data is stored on the server, but consider launching this shiny app on your own machine after having installed the [REDCapCAST package](https://agdamsbo.github.io/REDCapCAST/#installation) in R). Launch a local instance of this app with: + +``` +REDCapCAST::shiny_cast() +``` + +## Background + +The main structure of variables of a REDCap database is defined by a so-called data dictionary. This is a simple spreadsheet file defining one or more instruments, data classes, branching logic and more. It does not contain any information on randomization, longitudinal data or repeatable instruments. These functions must be set up in the REDCap interface after having defined the data dictionary. + +## Motivation + +This tool has been created out of frustration with the lack of easy-to-use tools available and with a hope to help colleagues and others to easily create and extend REDCap databases. + +## Use and feedback + +Please, if you use this tool, don't hesitate to contact me with feedback if something doesn't work as expected. But, please also mind the disclaimer above. Contact information can be found on the [package documentation page](https://agdamsbo.github.io/REDCapCAST/). + +## Citing + +This app and package can be cited using the following bibtex citation or by referencing the following doi-identifier: [10.5281/zenodo.8013984](https://doi.org/10.5281/zenodo.8013984) + +``` + @Manual{, + title = {REDCapCAST: REDCap Castellated Data Handling And Metadata Casting}, + author = {Andreas Gammelgaard Damsbo and Paul Egeler}, + year = {2016}, + note = {R package version 24.11.1, https://agdamsbo.github.io/REDCapCAST/}, + url = {https://github.com/agdamsbo/REDCapCAST}, + } +``` diff --git a/app/www/logo.png b/inst/shiny-examples/casting/www/logo.png similarity index 100% rename from app/www/logo.png rename to inst/shiny-examples/casting/www/logo.png diff --git a/man/REDCapCAST-package.Rd b/man/REDCapCAST-package.Rd index 2787378..f0117d7 100644 --- a/man/REDCapCAST-package.Rd +++ b/man/REDCapCAST-package.Rd @@ -4,11 +4,11 @@ \name{REDCapCAST-package} \alias{REDCapCAST} \alias{REDCapCAST-package} -\title{REDCapCAST: REDCap Castellated Data Handling} +\title{REDCapCAST: REDCap Castellated Data Handling And Metadata Casting} \description{ \if{html}{\figure{logo.png}{options: style='float: right' alt='logo' width='120'}} -Originally forked from the R part of 'REDCapRITS' by Paul Egeler. See \url{https://github.com/pegeler/REDCapRITS}. 'REDCap' database casting and handling of castellated data when using repeated instruments and longitudinal projects. Keeps a focused data export approach, by allowing to only export required data from the database. 'REDCap' (Research Electronic Data Capture) is a secure, web-based software platform designed to support data capture for research studies, providing 1) an intuitive interface for validated data capture; 2) audit trails for tracking data manipulation and export procedures; 3) automated export procedures for seamless data downloads to common statistical packages; and 4) procedures for data integration and interoperability with external sources (Harris et al (2009) \doi{10.1016/j.jbi.2008.08.010}; Harris et al (2019) \doi{10.1016/j.jbi.2019.103208}). +Originally forked from the R part of 'REDCapRITS' by Paul Egeler. See \url{https://github.com/pegeler/REDCapRITS}. 'REDCap' database casting and handling of castellated data when using repeated instruments and longitudinal projects. Keeps a focused data export approach, by allowing to only export required data from the database. Also for casting new REDCap databases based on datasets from other sources. 'REDCap' (Research Electronic Data Capture) is a secure, web-based software platform designed to support data capture for research studies, providing 1) an intuitive interface for validated data capture; 2) audit trails for tracking data manipulation and export procedures; 3) automated export procedures for seamless data downloads to common statistical packages; and 4) procedures for data integration and interoperability with external sources (Harris et al (2009) \doi{10.1016/j.jbi.2008.08.010}; Harris et al (2019) \doi{10.1016/j.jbi.2019.103208}). } \seealso{ Useful links: diff --git a/man/server_factory.Rd b/man/server_factory.Rd deleted file mode 100644 index 6c3a7d0..0000000 --- a/man/server_factory.Rd +++ /dev/null @@ -1,14 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/shiny_cast.R -\name{server_factory} -\alias{server_factory} -\title{Shiny server factory} -\usage{ -server_factory() -} -\value{ -shiny server -} -\description{ -Shiny server factory -} diff --git a/man/shiny_cast.Rd b/man/shiny_cast.Rd index 7811ed7..d7c73c4 100644 --- a/man/shiny_cast.Rd +++ b/man/shiny_cast.Rd @@ -4,13 +4,16 @@ \alias{shiny_cast} \title{Launch the included Shiny-app for database casting and upload} \usage{ -shiny_cast() +shiny_cast(...) +} +\arguments{ +\item{...}{Arguments passed to shiny::runApp()} } \value{ shiny app } \description{ -Launch the included Shiny-app for database casting and upload +Wraps shiny::runApp() } \examples{ # shiny_cast() diff --git a/man/ui_factory.Rd b/man/ui_factory.Rd deleted file mode 100644 index 80ca574..0000000 --- a/man/ui_factory.Rd +++ /dev/null @@ -1,14 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/shiny_cast.R -\name{ui_factory} -\alias{ui_factory} -\title{UI factory for shiny app} -\usage{ -ui_factory() -} -\value{ -shiny ui -} -\description{ -UI factory for shiny app -} diff --git a/renv.lock b/renv.lock index 1f1133b..3df9e0a 100644 --- a/renv.lock +++ b/renv.lock @@ -21,7 +21,7 @@ }, "REDCapR": { "Package": "REDCapR", - "Version": "1.1.0", + "Version": "1.3.0", "Source": "Repository", "Repository": "CRAN", "Requirements": [ @@ -37,28 +37,28 @@ "tibble", "tidyr" ], - "Hash": "e76c401b631961c865b89bb5a4ea3b97" + "Hash": "de630e9e6168aae0a178eaa3198dbe54" }, "Rcpp": { "Package": "Rcpp", - "Version": "1.0.12", + "Version": "1.0.13-1", "Source": "Repository", "Repository": "CRAN", "Requirements": [ "methods", "utils" ], - "Hash": "5ea2700d21e038ace58269ecdbeb9ec0" + "Hash": "6b868847b365672d6c1677b1608da9ed" }, "askpass": { "Package": "askpass", - "Version": "1.2.0", + "Version": "1.2.1", "Source": "Repository", "Repository": "CRAN", "Requirements": [ "sys" ], - "Hash": "cad6cf7f1d5f6e906700b9d3e718c796" + "Hash": "c39f4155b3ceb1a9a2799d700fbd4b6a" }, "assertthat": { "Package": "assertthat", @@ -72,13 +72,13 @@ }, "backports": { "Package": "backports", - "Version": "1.4.1", + "Version": "1.5.0", "Source": "Repository", "Repository": "CRAN", "Requirements": [ "R" ], - "Hash": "c39fbec8a30d23e721980b8afb31984c" + "Hash": "e1e1b9d75c37401117b636b7ae50827a" }, "base64enc": { "Package": "base64enc", @@ -92,17 +92,17 @@ }, "bit": { "Package": "bit", - "Version": "4.0.5", + "Version": "4.5.0", "Source": "Repository", "Repository": "CRAN", "Requirements": [ "R" ], - "Hash": "d242abec29412ce988848d0294b208fd" + "Hash": "5dc7b2677d65d0e874fc4aaf0e879987" }, "bit64": { "Package": "bit64", - "Version": "4.0.5", + "Version": "4.5.2", "Source": "Repository", "Repository": "CRAN", "Requirements": [ @@ -112,11 +112,11 @@ "stats", "utils" ], - "Hash": "9fe98599ca456d6552421db0d6772d8f" + "Hash": "e84984bf5f12a18628d9a02322128dfd" }, "bslib": { "Package": "bslib", - "Version": "0.7.0", + "Version": "0.8.0", "Source": "Repository", "Repository": "CRAN", "Requirements": [ @@ -134,7 +134,7 @@ "rlang", "sass" ], - "Hash": "8644cc53f43828f19133548195d7e59e" + "Hash": "b299c6741ca9746fb227debcb0f9fb6c" }, "cachem": { "Package": "cachem", @@ -161,7 +161,7 @@ }, "checkmate": { "Package": "checkmate", - "Version": "2.3.1", + "Version": "2.3.2", "Source": "Repository", "Repository": "CRAN", "Requirements": [ @@ -169,18 +169,18 @@ "backports", "utils" ], - "Hash": "c01cab1cb0f9125211a6fc99d540e315" + "Hash": "0e14e01ce07e7c88fd25de6d4260d26b" }, "cli": { "Package": "cli", - "Version": "3.6.2", + "Version": "3.6.3", "Source": "Repository", "Repository": "CRAN", "Requirements": [ "R", "utils" ], - "Hash": "1216ac65ac55ec0058a6f75d7ca0fd52" + "Hash": "b21916dd77a27642b447374a5d30ecf3" }, "clipr": { "Package": "clipr", @@ -194,10 +194,10 @@ }, "commonmark": { "Package": "commonmark", - "Version": "1.9.1", + "Version": "1.9.2", "Source": "Repository", "Repository": "CRAN", - "Hash": "5d8225445acb167abf7797de48b2ee3c" + "Hash": "14eb0596f987c71535d07c3aff814742" }, "cpp11": { "Package": "cpp11", @@ -211,7 +211,7 @@ }, "crayon": { "Package": "crayon", - "Version": "1.5.2", + "Version": "1.5.3", "Source": "Repository", "Repository": "CRAN", "Requirements": [ @@ -219,28 +219,28 @@ "methods", "utils" ], - "Hash": "e8a1e41acf02548751f45c718d55aa6a" + "Hash": "859d96e65ef198fd43e82b9628d593ef" }, "curl": { "Package": "curl", - "Version": "5.2.1", + "Version": "6.0.1", "Source": "Repository", "Repository": "CRAN", "Requirements": [ "R" ], - "Hash": "411ca2c03b1ce5f548345d2fc2685f7a" + "Hash": "e8ba62486230951fcd2b881c5be23f96" }, "digest": { "Package": "digest", - "Version": "0.6.35", + "Version": "0.6.37", "Source": "Repository", "Repository": "CRAN", "Requirements": [ "R", "utils" ], - "Hash": "698ece7ba5a4fa4559e3d537e7ec3d31" + "Hash": "33698c4b3127fc9f506654607fb73676" }, "dplyr": { "Package": "dplyr", @@ -324,14 +324,14 @@ }, "fs": { "Package": "fs", - "Version": "1.6.4", + "Version": "1.6.5", "Source": "Repository", "Repository": "CRAN", "Requirements": [ "R", "methods" ], - "Hash": "15aeb8c27f5ea5161f9f6a641fafd93a" + "Hash": "7f48af39fa27711ea5fbd183b399920d" }, "generics": { "Package": "generics", @@ -346,14 +346,14 @@ }, "glue": { "Package": "glue", - "Version": "1.7.0", + "Version": "1.8.0", "Source": "Repository", "Repository": "CRAN", "Requirements": [ "R", "methods" ], - "Hash": "e0b3a53876554bd45879e596cdb10a52" + "Hash": "5899f1eaa825580172bb56c08266f37c" }, "haven": { "Package": "haven", @@ -448,13 +448,13 @@ }, "jsonlite": { "Package": "jsonlite", - "Version": "1.8.8", + "Version": "1.8.9", "Source": "Repository", "Repository": "CRAN", "Requirements": [ "methods" ], - "Hash": "e1b9c55281c5adc4dd113652d9e26768" + "Hash": "4e993b65c2c3ffbffce7bb3e2c6f832b" }, "keyring": { "Package": "keyring", @@ -533,7 +533,7 @@ }, "minty": { "Package": "minty", - "Version": "0.0.1", + "Version": "0.0.4", "Source": "Repository", "Repository": "CRAN", "Requirements": [ @@ -541,21 +541,21 @@ "cpp11", "tzdb" ], - "Hash": "d19909f1b3fac42e7f3ac635321dbb15" + "Hash": "ab9d0930bfa21e98aec9d07f0c43cc89" }, "openssl": { "Package": "openssl", - "Version": "2.2.0", + "Version": "2.2.2", "Source": "Repository", "Repository": "CRAN", "Requirements": [ "askpass" ], - "Hash": "2bcca3848e4734eb3b16103bc9aa4b8e" + "Hash": "d413e0fef796c9401a4419485f709ca1" }, "openxlsx2": { "Package": "openxlsx2", - "Version": "1.9", + "Version": "1.10", "Source": "Repository", "Repository": "CRAN", "Requirements": [ @@ -568,7 +568,7 @@ "utils", "zip" ], - "Hash": "f3e00e7d16abb500e63faee67b7dc1a7" + "Hash": "ee4b99fc9ca05bbfd29d567785c093d2" }, "pillar": { "Package": "pillar", @@ -664,12 +664,13 @@ }, "readODS": { "Package": "readODS", - "Version": "2.3.0", + "Version": "2.3.1", "Source": "Repository", "Repository": "CRAN", "Requirements": [ "R", "cellranger", + "cpp11", "minty", "stringi", "tibble", @@ -677,7 +678,7 @@ "vctrs", "zip" ], - "Hash": "54bd5930f49d8f914f80eb0eedcd45e2" + "Hash": "d81971565325ed8cbe59993ed5c0e611" }, "readr": { "Package": "readr", @@ -711,19 +712,24 @@ }, "renv": { "Package": "renv", - "Version": "1.0.9", - "Source": "Repository" + "Version": "1.0.11", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "47623f66b4e80b3b0587bc5d7b309888" }, "rlang": { "Package": "rlang", - "Version": "1.1.3", + "Version": "1.1.4", "Source": "Repository", "Repository": "CRAN", "Requirements": [ "R", "utils" ], - "Hash": "42548638fae05fd9a9b5f3f437fbbbe2" + "Hash": "3eec01f8b1dee337674b2e34ab1f9bc1" }, "sass": { "Package": "sass", @@ -774,10 +780,10 @@ }, "sodium": { "Package": "sodium", - "Version": "1.3.1", + "Version": "1.3.2", "Source": "Repository", "Repository": "CRAN", - "Hash": "dd86d6fd2a01d4eb3777dfdee7076d56" + "Hash": "869b09ca565ecaa9efc62534ebfa3efd" }, "sourcetools": { "Package": "sourcetools", @@ -821,10 +827,10 @@ }, "sys": { "Package": "sys", - "Version": "3.4.2", + "Version": "3.4.3", "Source": "Repository", "Repository": "CRAN", - "Hash": "3a1be13d68d47a8cd0bfd74739ca1555" + "Hash": "de342ebfebdbf40477d0758d05426646" }, "tibble": { "Package": "tibble", @@ -947,7 +953,7 @@ }, "withr": { "Package": "withr", - "Version": "3.0.0", + "Version": "3.0.2", "Source": "Repository", "Repository": "CRAN", "Requirements": [ @@ -955,7 +961,7 @@ "grDevices", "graphics" ], - "Hash": "d31b6c62c10dcf11ec530ca6b0dd5d35" + "Hash": "cc2d62c76458d425210d1eb1478b30b4" }, "xtable": { "Package": "xtable", @@ -971,10 +977,10 @@ }, "yaml": { "Package": "yaml", - "Version": "2.3.8", + "Version": "2.3.10", "Source": "Repository", "Repository": "CRAN", - "Hash": "29240487a071f535f5e5d5a323b7afbd" + "Hash": "51dab85c6c98e50a18d7551e9d49f76c" }, "zip": { "Package": "zip", diff --git a/renv/activate.R b/renv/activate.R index c360bf2..0eb5108 100644 --- a/renv/activate.R +++ b/renv/activate.R @@ -2,7 +2,7 @@ local({ # the requested version of renv - version <- "1.0.9" + version <- "1.0.11" attr(version, "sha") <- NULL # the project directory @@ -368,8 +368,7 @@ local({ quiet = TRUE ) - if ("headers" %in% names(formals(utils::download.file))) - { + if ("headers" %in% names(formals(utils::download.file))) { headers <- renv_bootstrap_download_custom_headers(url) if (length(headers) && is.character(headers)) args$headers <- headers @@ -457,9 +456,8 @@ local({ # add custom headers if available -- note that # utils::available.packages() will pass this to download.file() - if ("headers" %in% names(formals(utils::download.file))) - { - headers <- renv_bootstrap_download_custom_headers(url) + if ("headers" %in% names(formals(utils::download.file))) { + headers <- renv_bootstrap_download_custom_headers(repos) if (length(headers) && is.character(headers)) args$headers <- headers }