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 @@ + + + + +
+ + + + + + + + +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.
+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.
+These are the functionalities to create and migrate data from a +spreadsheet to a REDCap database:
+create a REDCap data dictionary or instrument based on a +spreadsheet (.csv/.xls(x)/.dta/.ods) and,
upload said database file on a given REDCap server and upload the +dataset via API access or download for all manual upload.