mirror of
https://github.com/agdamsbo/REDCapCAST.git
synced 2024-11-23 21:50:21 +01:00
Compare commits
No commits in common. "767d03f45ffded437b558a24485dd3fa086ea809" and "1aefacba430cba511420181dadd5218e07d620d4" have entirely different histories.
767d03f45f
...
1aefacba43
2
.gitignore
vendored
2
.gitignore
vendored
@ -8,5 +8,3 @@ logo.R
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
docs
|
docs
|
||||||
drafting
|
drafting
|
||||||
\.DS_Store
|
|
||||||
.DS_Store
|
|
||||||
|
@ -36,13 +36,11 @@ Suggests:
|
|||||||
here,
|
here,
|
||||||
styler,
|
styler,
|
||||||
devtools,
|
devtools,
|
||||||
roxygen2,
|
roxygen2
|
||||||
openxlsx2,
|
|
||||||
rsconnect
|
|
||||||
License: GPL (>= 3)
|
License: GPL (>= 3)
|
||||||
Encoding: UTF-8
|
Encoding: UTF-8
|
||||||
LazyData: true
|
LazyData: true
|
||||||
RoxygenNote: 7.3.1
|
RoxygenNote: 7.3.0
|
||||||
URL: https://github.com/agdamsbo/REDCapCAST, https://agdamsbo.github.io/REDCapCAST/
|
URL: https://github.com/agdamsbo/REDCapCAST, https://agdamsbo.github.io/REDCapCAST/
|
||||||
BugReports: https://github.com/agdamsbo/REDCapCAST/issues
|
BugReports: https://github.com/agdamsbo/REDCapCAST/issues
|
||||||
Imports:
|
Imports:
|
||||||
@ -66,6 +64,5 @@ Collate:
|
|||||||
'redcap_wider.R'
|
'redcap_wider.R'
|
||||||
'redcapcast_data.R'
|
'redcapcast_data.R'
|
||||||
'redcapcast_meta.R'
|
'redcapcast_meta.R'
|
||||||
'shiny_cast.R'
|
|
||||||
Language: en-US
|
Language: en-US
|
||||||
VignetteBuilder: knitr
|
VignetteBuilder: knitr
|
||||||
|
@ -6,22 +6,17 @@ export(d2w)
|
|||||||
export(ds2dd)
|
export(ds2dd)
|
||||||
export(ds2dd_detailed)
|
export(ds2dd_detailed)
|
||||||
export(easy_redcap)
|
export(easy_redcap)
|
||||||
export(file_extension)
|
|
||||||
export(focused_metadata)
|
export(focused_metadata)
|
||||||
export(get_api_key)
|
export(get_api_key)
|
||||||
export(guess_time_only_filter)
|
export(guess_time_only_filter)
|
||||||
export(is_repeated_longitudinal)
|
export(is_repeated_longitudinal)
|
||||||
export(match_fields_to_form)
|
export(match_fields_to_form)
|
||||||
export(read_input)
|
|
||||||
export(read_redcap_instrument)
|
export(read_redcap_instrument)
|
||||||
export(read_redcap_tables)
|
export(read_redcap_tables)
|
||||||
export(redcap_wider)
|
export(redcap_wider)
|
||||||
export(sanitize_split)
|
export(sanitize_split)
|
||||||
export(server_factory)
|
|
||||||
export(shiny_cast)
|
|
||||||
export(split_non_repeating_forms)
|
export(split_non_repeating_forms)
|
||||||
export(strsplitx)
|
export(strsplitx)
|
||||||
export(ui_factory)
|
|
||||||
importFrom(REDCapR,redcap_event_instruments)
|
importFrom(REDCapR,redcap_event_instruments)
|
||||||
importFrom(REDCapR,redcap_metadata_read)
|
importFrom(REDCapR,redcap_metadata_read)
|
||||||
importFrom(REDCapR,redcap_read)
|
importFrom(REDCapR,redcap_read)
|
||||||
|
@ -1,58 +0,0 @@
|
|||||||
#' 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
|
|
||||||
#'
|
|
||||||
#' @return shiny app
|
|
||||||
#' @export
|
|
||||||
#'
|
|
||||||
#' @examples
|
|
||||||
#' # shiny_cast()
|
|
||||||
#'
|
|
||||||
shiny_cast <- function() {
|
|
||||||
# shiny::runApp(appDir = here::here("app/"), launch.browser = TRUE)
|
|
||||||
|
|
||||||
shiny::shinyApp(
|
|
||||||
ui_factory(),
|
|
||||||
server_factory()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#' Deploy the Shiny app with rsconnect
|
|
||||||
#'
|
|
||||||
#' @return deploy
|
|
||||||
#' @export
|
|
||||||
#'
|
|
||||||
#' @examples
|
|
||||||
#' # deploy_shiny
|
|
||||||
#'
|
|
||||||
deploy_shiny <- function(path=here::here("app/"), name.app="shiny_cast"){
|
|
||||||
# Ensure to install latest package version
|
|
||||||
renv::install("agdamsbo/REDCapCAST")
|
|
||||||
|
|
||||||
# Connecting
|
|
||||||
rsconnect::setAccountInfo(
|
|
||||||
name = "cognitiveindex",
|
|
||||||
token = keyring::key_get(service = "rsconnect_cognitiveindex_token"),
|
|
||||||
secret = keyring::key_get(service = "rsconnect_cognitiveindex_secret")
|
|
||||||
)
|
|
||||||
|
|
||||||
# Deploying
|
|
||||||
rsconnect::deployApp(appDir = path,lint = TRUE,appName = name.app,)
|
|
||||||
}
|
|
50
R/utils.r
50
R/utils.r
@ -490,53 +490,3 @@ is_repeated_longitudinal <- function(data, generics = c(
|
|||||||
}
|
}
|
||||||
any(generics %in% names)
|
any(generics %in% names)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#' Helper to import files correctly
|
|
||||||
#'
|
|
||||||
#' @param filenames file names
|
|
||||||
#'
|
|
||||||
#' @return character vector
|
|
||||||
#' @export
|
|
||||||
#'
|
|
||||||
#' @examples
|
|
||||||
#' file_extension(list.files(here::here(""))[[2]])[[1]]
|
|
||||||
file_extension <- function(filenames) {
|
|
||||||
sub(pattern = "^(.*\\.|[^.]+)(?=[^.]*)", replacement = "", filenames, perl = TRUE)
|
|
||||||
}
|
|
||||||
|
|
||||||
#' Flexible file import based on extension
|
|
||||||
#'
|
|
||||||
#' @param file file name
|
|
||||||
#' @param consider.na character vector of strings to consider as NAs
|
|
||||||
#'
|
|
||||||
#' @return tibble
|
|
||||||
#' @export
|
|
||||||
#'
|
|
||||||
#' @examples
|
|
||||||
#' read_input("https://raw.githubusercontent.com/agdamsbo/cognitive.index.lookup/main/data/sample.csv")
|
|
||||||
read_input <- function(file, consider.na= c("NA", '""',"")){
|
|
||||||
|
|
||||||
ext <- file_extension(file)
|
|
||||||
|
|
||||||
tryCatch(
|
|
||||||
{
|
|
||||||
if (ext == "csv") {
|
|
||||||
df <- readr::read_csv(file = file,na = consider.na)
|
|
||||||
} else if (ext %in% c("xls", "xlsx")) {
|
|
||||||
df <- openxlsx2::read_xlsx(file = file, na.strings = consider.na)
|
|
||||||
} else if (ext == "dta"){
|
|
||||||
df <- haven::read_dta(file = file)
|
|
||||||
} else {
|
|
||||||
stop("Input file format has to be either '.csv', '.xls' or '.xlsx'")
|
|
||||||
}
|
|
||||||
},
|
|
||||||
error = function(e) {
|
|
||||||
# return a safeError if a parsing error occurs
|
|
||||||
stop(shiny::safeError(e))
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
df
|
|
||||||
}
|
|
||||||
|
@ -12,11 +12,11 @@ experimental](https://img.shields.io/badge/lifecycle-experimental-orange.svg)](h
|
|||||||
|
|
||||||
# REDCapCAST package <img src="man/figures/logo.png" align="right" />
|
# REDCapCAST package <img src="man/figures/logo.png" align="right" />
|
||||||
|
|
||||||
REDCap database casting and handling of castellated data when using repeated instruments and longitudinal projects.
|
REDCap Castellated data handling when using repeated instruments.
|
||||||
|
|
||||||
This package is a fork of [pegeler/REDCapRITS](https://github.com/pegeler/REDCapRITS). The REDCapRITS represents great and extensive work to handle castellated REDCap data in different programming languages. This fork is purely minded on R usage and includes a few implementations of the main `REDCap_split` function.
|
This package is a fork of [pegeler/REDCapRITS](https://github.com/pegeler/REDCapRITS). The REDCapRITS represents great and extensive work to handle castellated REDCap data in different programming languages. This fork is purely minded on R usage and includes a few implementations of the main `REDCap_split` function.
|
||||||
|
|
||||||
THis package is very much to be seen as an attempt at a REDCap-R foundry for handling both the transition from dataset/variable list to database and the other way, from REDCap database to a tidy dataset. The goal was also to allow for a "minimal data" approach by allowing to filter records, instruments and variables in the export to only download data needed. I think this approach is desireable for handling sensitive, clinical data. No similar functionality is available from similar tools (like `REDCapR` or `REDCapTidieR`). Please refer to [REDCap-Tools](https://redcap-tools.github.io/) for other great tools.
|
The main goal for this project was to allow for a "minimal data" approach by allowing to filter records, instruments and variables in the export to only download data needed. I think this approach is desireable for handling sensitive, clinical data. No similar functionality is available from similar tools (like `REDCapR` or `REDCapTidieR`). Please refer to [REDCap-Tools](https://redcap-tools.github.io/) for other great tools.
|
||||||
|
|
||||||
## Use and immprovements
|
## Use and immprovements
|
||||||
|
|
||||||
@ -30,7 +30,8 @@ This package is primarily relevant for working with longitudinal projects and/or
|
|||||||
|
|
||||||
* `easy_redcap()`: combines secure API key storage with the `keyring`-package, focused data retrieval and optional widening. This is the recommended approach for easy data access and analysis.
|
* `easy_redcap()`: combines secure API key storage with the `keyring`-package, focused data retrieval and optional widening. This is the recommended approach for easy data access and analysis.
|
||||||
|
|
||||||
...
|
|
||||||
|
Compared to the original `REDCapRITS`, all matching functions are improved to accept column naming of REDCap data from manual download or API export.
|
||||||
|
|
||||||
## Future
|
## Future
|
||||||
|
|
||||||
|
@ -18,4 +18,4 @@ StripTrailingWhitespace: Yes
|
|||||||
BuildType: Package
|
BuildType: Package
|
||||||
PackageUseDevtools: Yes
|
PackageUseDevtools: Yes
|
||||||
PackageInstallArgs: --no-multiarch --with-keep.source
|
PackageInstallArgs: --no-multiarch --with-keep.source
|
||||||
PackageRoxygenize: rd,collate,namespace,vignette
|
PackageRoxygenize: rd,collate,namespace
|
||||||
|
81
app/server.R
81
app/server.R
@ -1,81 +0,0 @@
|
|||||||
server <- function(input, output, session) {
|
|
||||||
require(REDCapCAST)
|
|
||||||
|
|
||||||
dat <- shiny::reactive({
|
|
||||||
shiny::req(input$ds)
|
|
||||||
|
|
||||||
read_input(input$ds$datapath)
|
|
||||||
})
|
|
||||||
|
|
||||||
dd <- shiny::reactive({
|
|
||||||
ds2dd_detailed(data = dat())
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
output$data.tbl <- shiny::renderTable({
|
|
||||||
dd() |>
|
|
||||||
purrr::pluck("data") |>
|
|
||||||
head(20) |>
|
|
||||||
dplyr::tibble()
|
|
||||||
})
|
|
||||||
|
|
||||||
output$meta.tbl <- shiny::renderTable({
|
|
||||||
dd() |>
|
|
||||||
purrr::pluck("meta") |>
|
|
||||||
dplyr::tibble()
|
|
||||||
})
|
|
||||||
|
|
||||||
# Downloadable csv of dataset ----
|
|
||||||
output$downloadData <- shiny::downloadHandler(
|
|
||||||
filename = "data_ready.csv",
|
|
||||||
content = function(file) {
|
|
||||||
write.csv(purrr::pluck(dd(), "data"), file, row.names = FALSE)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
# Downloadable csv of data dictionary ----
|
|
||||||
output$downloadMeta <- shiny::downloadHandler(
|
|
||||||
filename = "dictionary_ready.csv",
|
|
||||||
content = function(file) {
|
|
||||||
write.csv(purrr::pluck(dd(), "meta"), file, row.names = FALSE)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
output_staging <- shiny::reactiveValues()
|
|
||||||
output_staging$meta <- output_staging$data <- NA
|
|
||||||
|
|
||||||
shiny::observeEvent(input$upload.meta,{ upload_meta() })
|
|
||||||
|
|
||||||
shiny::observeEvent(input$upload.data,{ upload_data() })
|
|
||||||
|
|
||||||
upload_meta <- function(){
|
|
||||||
|
|
||||||
shiny::req(input$uri)
|
|
||||||
|
|
||||||
shiny::req(input$api)
|
|
||||||
|
|
||||||
output_staging$meta <- REDCapR::redcap_metadata_write(
|
|
||||||
ds = purrr::pluck(dd(), "meta"),
|
|
||||||
redcap_uri = input$uri,
|
|
||||||
token = input$api
|
|
||||||
)|> purrr::pluck("success")
|
|
||||||
}
|
|
||||||
|
|
||||||
upload_data <- function(){
|
|
||||||
|
|
||||||
shiny::req(input$uri)
|
|
||||||
|
|
||||||
shiny::req(input$api)
|
|
||||||
|
|
||||||
output_staging$data <- REDCapR::redcap_write(
|
|
||||||
ds = purrr::pluck(dd(), "data"),
|
|
||||||
redcap_uri = input$uri,
|
|
||||||
token = input$api
|
|
||||||
) |> purrr::pluck("success")
|
|
||||||
}
|
|
||||||
|
|
||||||
output$upload.meta.print <- renderText(output_staging$meta)
|
|
||||||
|
|
||||||
output$upload.data.print <- renderText(output_staging$data)
|
|
||||||
|
|
||||||
}
|
|
89
app/ui.R
89
app/ui.R
@ -1,89 +0,0 @@
|
|||||||
ui <- shiny::fluidPage(
|
|
||||||
|
|
||||||
## -----------------------------------------------------------------------------
|
|
||||||
## Application title
|
|
||||||
## -----------------------------------------------------------------------------
|
|
||||||
shiny::titlePanel("Simple REDCap data base creation and data upload from data set file via API",
|
|
||||||
windowTitle = "REDCap databse creator"
|
|
||||||
),
|
|
||||||
shiny::h5("Please note, that this tool serves as a demonstration of some of the functionality
|
|
||||||
of the REDCapCAST package. No responsibility for data loss or any other
|
|
||||||
problems will be taken."),
|
|
||||||
|
|
||||||
## -----------------------------------------------------------------------------
|
|
||||||
## Side panel
|
|
||||||
## -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
shiny::sidebarPanel(
|
|
||||||
shiny::h4("REDCap database and dataset"),
|
|
||||||
shiny::fileInput("ds", "Choose data file",
|
|
||||||
multiple = FALSE,
|
|
||||||
accept = c(
|
|
||||||
".csv",
|
|
||||||
".xls",
|
|
||||||
".xlsx",
|
|
||||||
".dta"
|
|
||||||
)
|
|
||||||
),
|
|
||||||
shiny::h6("Below you can download the dataset formatted for upload and the
|
|
||||||
corresponding data dictionary for a new data base."),
|
|
||||||
# Button
|
|
||||||
shiny::downloadButton("downloadData", "Download data"),
|
|
||||||
|
|
||||||
# Button
|
|
||||||
shiny::downloadButton("downloadMeta", "Download dictionary"),
|
|
||||||
|
|
||||||
|
|
||||||
# Horizontal line ----
|
|
||||||
shiny::tags$hr(),
|
|
||||||
shiny::h4("REDCap upload"),
|
|
||||||
shiny::textInput(
|
|
||||||
inputId = "uri",
|
|
||||||
label = "URI",
|
|
||||||
value = "https://redcap.au.dk/api/"
|
|
||||||
),
|
|
||||||
shiny::textInput(
|
|
||||||
inputId = "api",
|
|
||||||
label = "API key",
|
|
||||||
value = "21CF2C17EA1CA4F3688DF991C8FE3EBF"
|
|
||||||
),
|
|
||||||
shiny::actionButton(
|
|
||||||
inputId = "upload.meta",
|
|
||||||
label = "Upload dictionary", 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)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
@ -1,5 +0,0 @@
|
|||||||
mtcars |> dplyr::mutate(record_id=seq_len(n()),
|
|
||||||
name=rownames(mtcars)
|
|
||||||
) |>
|
|
||||||
dplyr::select(record_id,dplyr::everything()) |>
|
|
||||||
write.csv(here::here("data/mtcars_redcap.csv"),row.names = FALSE)
|
|
@ -1,33 +0,0 @@
|
|||||||
"record_id","mpg","cyl","disp","hp","drat","wt","qsec","vs","am","gear","carb","name"
|
|
||||||
1,21,6,160,110,3.9,2.62,16.46,0,1,4,4,"Mazda RX4"
|
|
||||||
2,21,6,160,110,3.9,2.875,17.02,0,1,4,4,"Mazda RX4 Wag"
|
|
||||||
3,22.8,4,108,93,3.85,2.32,18.61,1,1,4,1,"Datsun 710"
|
|
||||||
4,21.4,6,258,110,3.08,3.215,19.44,1,0,3,1,"Hornet 4 Drive"
|
|
||||||
5,18.7,8,360,175,3.15,3.44,17.02,0,0,3,2,"Hornet Sportabout"
|
|
||||||
6,18.1,6,225,105,2.76,3.46,20.22,1,0,3,1,"Valiant"
|
|
||||||
7,14.3,8,360,245,3.21,3.57,15.84,0,0,3,4,"Duster 360"
|
|
||||||
8,24.4,4,146.7,62,3.69,3.19,20,1,0,4,2,"Merc 240D"
|
|
||||||
9,22.8,4,140.8,95,3.92,3.15,22.9,1,0,4,2,"Merc 230"
|
|
||||||
10,19.2,6,167.6,123,3.92,3.44,18.3,1,0,4,4,"Merc 280"
|
|
||||||
11,17.8,6,167.6,123,3.92,3.44,18.9,1,0,4,4,"Merc 280C"
|
|
||||||
12,16.4,8,275.8,180,3.07,4.07,17.4,0,0,3,3,"Merc 450SE"
|
|
||||||
13,17.3,8,275.8,180,3.07,3.73,17.6,0,0,3,3,"Merc 450SL"
|
|
||||||
14,15.2,8,275.8,180,3.07,3.78,18,0,0,3,3,"Merc 450SLC"
|
|
||||||
15,10.4,8,472,205,2.93,5.25,17.98,0,0,3,4,"Cadillac Fleetwood"
|
|
||||||
16,10.4,8,460,215,3,5.424,17.82,0,0,3,4,"Lincoln Continental"
|
|
||||||
17,14.7,8,440,230,3.23,5.345,17.42,0,0,3,4,"Chrysler Imperial"
|
|
||||||
18,32.4,4,78.7,66,4.08,2.2,19.47,1,1,4,1,"Fiat 128"
|
|
||||||
19,30.4,4,75.7,52,4.93,1.615,18.52,1,1,4,2,"Honda Civic"
|
|
||||||
20,33.9,4,71.1,65,4.22,1.835,19.9,1,1,4,1,"Toyota Corolla"
|
|
||||||
21,21.5,4,120.1,97,3.7,2.465,20.01,1,0,3,1,"Toyota Corona"
|
|
||||||
22,15.5,8,318,150,2.76,3.52,16.87,0,0,3,2,"Dodge Challenger"
|
|
||||||
23,15.2,8,304,150,3.15,3.435,17.3,0,0,3,2,"AMC Javelin"
|
|
||||||
24,13.3,8,350,245,3.73,3.84,15.41,0,0,3,4,"Camaro Z28"
|
|
||||||
25,19.2,8,400,175,3.08,3.845,17.05,0,0,3,2,"Pontiac Firebird"
|
|
||||||
26,27.3,4,79,66,4.08,1.935,18.9,1,1,4,1,"Fiat X1-9"
|
|
||||||
27,26,4,120.3,91,4.43,2.14,16.7,0,1,5,2,"Porsche 914-2"
|
|
||||||
28,30.4,4,95.1,113,3.77,1.513,16.9,1,1,5,2,"Lotus Europa"
|
|
||||||
29,15.8,8,351,264,4.22,3.17,14.5,0,1,5,4,"Ford Pantera L"
|
|
||||||
30,19.7,6,145,175,3.62,2.77,15.5,0,1,5,6,"Ferrari Dino"
|
|
||||||
31,15,8,301,335,3.54,3.57,14.6,0,1,5,8,"Maserati Bora"
|
|
||||||
32,21.4,4,121,109,4.11,2.78,18.6,1,1,4,2,"Volvo 142E"
|
|
|
@ -1,20 +0,0 @@
|
|||||||
% Generated by roxygen2: do not edit by hand
|
|
||||||
% Please edit documentation in R/utils.r
|
|
||||||
\name{file_extension}
|
|
||||||
\alias{file_extension}
|
|
||||||
\title{Helper to import files correctly}
|
|
||||||
\usage{
|
|
||||||
file_extension(filenames)
|
|
||||||
}
|
|
||||||
\arguments{
|
|
||||||
\item{filenames}{file names}
|
|
||||||
}
|
|
||||||
\value{
|
|
||||||
character vector
|
|
||||||
}
|
|
||||||
\description{
|
|
||||||
Helper to import files correctly
|
|
||||||
}
|
|
||||||
\examples{
|
|
||||||
file_extension(list.files(here::here(""))[[2]])[[1]]
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
% Generated by roxygen2: do not edit by hand
|
|
||||||
% Please edit documentation in R/utils.r
|
|
||||||
\name{read_input}
|
|
||||||
\alias{read_input}
|
|
||||||
\title{Flexible file import based on extension}
|
|
||||||
\usage{
|
|
||||||
read_input(file, consider.na = c("NA", "\\"\\"", ""))
|
|
||||||
}
|
|
||||||
\arguments{
|
|
||||||
\item{file}{file name}
|
|
||||||
|
|
||||||
\item{consider.na}{character vector of strings to consider as NAs}
|
|
||||||
}
|
|
||||||
\value{
|
|
||||||
tibble
|
|
||||||
}
|
|
||||||
\description{
|
|
||||||
Flexible file import based on extension
|
|
||||||
}
|
|
||||||
\examples{
|
|
||||||
read_input("https://raw.githubusercontent.com/agdamsbo/cognitive.index.lookup/main/data/sample.csv")
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
% Generated by roxygen2: do not edit by hand
|
|
||||||
% Please edit documentation in R/shiny_cast.R
|
|
||||||
\name{shiny_cast}
|
|
||||||
\alias{shiny_cast}
|
|
||||||
\title{Launch the included Shiny-app for database casting and upload}
|
|
||||||
\usage{
|
|
||||||
shiny_cast()
|
|
||||||
}
|
|
||||||
\value{
|
|
||||||
shiny app
|
|
||||||
}
|
|
||||||
\description{
|
|
||||||
Launch the included Shiny-app for database casting and upload
|
|
||||||
}
|
|
||||||
\examples{
|
|
||||||
# shiny_cast()
|
|
||||||
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
@ -428,13 +428,13 @@
|
|||||||
},
|
},
|
||||||
"renv": {
|
"renv": {
|
||||||
"Package": "renv",
|
"Package": "renv",
|
||||||
"Version": "1.0.4",
|
"Version": "1.0.3",
|
||||||
"Source": "Repository",
|
"Source": "Repository",
|
||||||
"Repository": "CRAN",
|
"Repository": "CRAN",
|
||||||
"Requirements": [
|
"Requirements": [
|
||||||
"utils"
|
"utils"
|
||||||
],
|
],
|
||||||
"Hash": "11abaf7c540ff33f94514d50f929bfd1"
|
"Hash": "41b847654f567341725473431dd0d5ab"
|
||||||
},
|
},
|
||||||
"rlang": {
|
"rlang": {
|
||||||
"Package": "rlang",
|
"Package": "rlang",
|
||||||
@ -512,7 +512,7 @@
|
|||||||
},
|
},
|
||||||
"tidyr": {
|
"tidyr": {
|
||||||
"Package": "tidyr",
|
"Package": "tidyr",
|
||||||
"Version": "1.3.1",
|
"Version": "1.3.0",
|
||||||
"Source": "Repository",
|
"Source": "Repository",
|
||||||
"Repository": "CRAN",
|
"Repository": "CRAN",
|
||||||
"Requirements": [
|
"Requirements": [
|
||||||
@ -531,7 +531,7 @@
|
|||||||
"utils",
|
"utils",
|
||||||
"vctrs"
|
"vctrs"
|
||||||
],
|
],
|
||||||
"Hash": "915fb7ce036c22a6a33b5a8adb712eb1"
|
"Hash": "e47debdc7ce599b070c8e78e8ac0cfcf"
|
||||||
},
|
},
|
||||||
"tidyselect": {
|
"tidyselect": {
|
||||||
"Package": "tidyselect",
|
"Package": "tidyselect",
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
local({
|
local({
|
||||||
|
|
||||||
# the requested version of renv
|
# the requested version of renv
|
||||||
version <- "1.0.4"
|
version <- "1.0.3"
|
||||||
attr(version, "sha") <- NULL
|
attr(version, "sha") <- NULL
|
||||||
|
|
||||||
# the project directory
|
# the project directory
|
||||||
@ -31,14 +31,6 @@ local({
|
|||||||
if (!is.null(override))
|
if (!is.null(override))
|
||||||
return(override)
|
return(override)
|
||||||
|
|
||||||
# if we're being run in a context where R_LIBS is already set,
|
|
||||||
# don't load -- presumably we're being run as a sub-process and
|
|
||||||
# the parent process has already set up library paths for us
|
|
||||||
rcmd <- Sys.getenv("R_CMD", unset = NA)
|
|
||||||
rlibs <- Sys.getenv("R_LIBS", unset = NA)
|
|
||||||
if (!is.na(rlibs) && !is.na(rcmd))
|
|
||||||
return(FALSE)
|
|
||||||
|
|
||||||
# next, check environment variables
|
# next, check environment variables
|
||||||
# TODO: prefer using the configuration one in the future
|
# TODO: prefer using the configuration one in the future
|
||||||
envvars <- c(
|
envvars <- c(
|
||||||
@ -58,22 +50,9 @@ local({
|
|||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
# bail if we're not enabled
|
if (!enabled)
|
||||||
if (!enabled) {
|
|
||||||
|
|
||||||
# if we're not enabled, we might still need to manually load
|
|
||||||
# the user profile here
|
|
||||||
profile <- Sys.getenv("R_PROFILE_USER", unset = "~/.Rprofile")
|
|
||||||
if (file.exists(profile)) {
|
|
||||||
cfg <- Sys.getenv("RENV_CONFIG_USER_PROFILE", unset = "TRUE")
|
|
||||||
if (tolower(cfg) %in% c("true", "t", "1"))
|
|
||||||
sys.source(profile, envir = globalenv())
|
|
||||||
}
|
|
||||||
|
|
||||||
return(FALSE)
|
return(FALSE)
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
# avoid recursion
|
# avoid recursion
|
||||||
if (identical(getOption("renv.autoloader.running"), TRUE)) {
|
if (identical(getOption("renv.autoloader.running"), TRUE)) {
|
||||||
warning("ignoring recursive attempt to run renv autoloader")
|
warning("ignoring recursive attempt to run renv autoloader")
|
||||||
@ -1062,7 +1041,7 @@ local({
|
|||||||
# if jsonlite is loaded, use that instead
|
# if jsonlite is loaded, use that instead
|
||||||
if ("jsonlite" %in% loadedNamespaces()) {
|
if ("jsonlite" %in% loadedNamespaces()) {
|
||||||
|
|
||||||
json <- tryCatch(renv_json_read_jsonlite(file, text), error = identity)
|
json <- catch(renv_json_read_jsonlite(file, text))
|
||||||
if (!inherits(json, "error"))
|
if (!inherits(json, "error"))
|
||||||
return(json)
|
return(json)
|
||||||
|
|
||||||
@ -1071,7 +1050,7 @@ local({
|
|||||||
}
|
}
|
||||||
|
|
||||||
# otherwise, fall back to the default JSON reader
|
# otherwise, fall back to the default JSON reader
|
||||||
json <- tryCatch(renv_json_read_default(file, text), error = identity)
|
json <- catch(renv_json_read_default(file, text))
|
||||||
if (!inherits(json, "error"))
|
if (!inherits(json, "error"))
|
||||||
return(json)
|
return(json)
|
||||||
|
|
||||||
@ -1084,14 +1063,14 @@ local({
|
|||||||
}
|
}
|
||||||
|
|
||||||
renv_json_read_jsonlite <- function(file = NULL, text = NULL) {
|
renv_json_read_jsonlite <- function(file = NULL, text = NULL) {
|
||||||
text <- paste(text %||% readLines(file, warn = FALSE), collapse = "\n")
|
text <- paste(text %||% read(file), collapse = "\n")
|
||||||
jsonlite::fromJSON(txt = text, simplifyVector = FALSE)
|
jsonlite::fromJSON(txt = text, simplifyVector = FALSE)
|
||||||
}
|
}
|
||||||
|
|
||||||
renv_json_read_default <- function(file = NULL, text = NULL) {
|
renv_json_read_default <- function(file = NULL, text = NULL) {
|
||||||
|
|
||||||
# find strings in the JSON
|
# find strings in the JSON
|
||||||
text <- paste(text %||% readLines(file, warn = FALSE), collapse = "\n")
|
text <- paste(text %||% read(file), collapse = "\n")
|
||||||
pattern <- '["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'
|
pattern <- '["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'
|
||||||
locs <- gregexpr(pattern, text, perl = TRUE)[[1]]
|
locs <- gregexpr(pattern, text, perl = TRUE)[[1]]
|
||||||
|
|
||||||
@ -1139,14 +1118,14 @@ local({
|
|||||||
map <- as.list(map)
|
map <- as.list(map)
|
||||||
|
|
||||||
# remap strings in object
|
# remap strings in object
|
||||||
remapped <- renv_json_read_remap(json, map)
|
remapped <- renv_json_remap(json, map)
|
||||||
|
|
||||||
# evaluate
|
# evaluate
|
||||||
eval(remapped, envir = baseenv())
|
eval(remapped, envir = baseenv())
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
renv_json_read_remap <- function(json, map) {
|
renv_json_remap <- function(json, map) {
|
||||||
|
|
||||||
# fix names
|
# fix names
|
||||||
if (!is.null(names(json))) {
|
if (!is.null(names(json))) {
|
||||||
@ -1173,7 +1152,7 @@ local({
|
|||||||
# recurse
|
# recurse
|
||||||
if (is.recursive(json)) {
|
if (is.recursive(json)) {
|
||||||
for (i in seq_along(json)) {
|
for (i in seq_along(json)) {
|
||||||
json[i] <- list(renv_json_read_remap(json[[i]], map))
|
json[i] <- list(renv_json_remap(json[[i]], map))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,81 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Database casting"
|
|
||||||
output: rmarkdown::html_vignette
|
|
||||||
vignette: >
|
|
||||||
%\VignetteIndexEntry{Database casting}
|
|
||||||
%\VignetteEngine{knitr::rmarkdown}
|
|
||||||
%\VignetteEncoding{UTF-8}
|
|
||||||
---
|
|
||||||
|
|
||||||
```{r, include = FALSE}
|
|
||||||
knitr::opts_chunk$set(
|
|
||||||
collapse = TRUE,
|
|
||||||
comment = "#>"
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
```{r setup}
|
|
||||||
library(REDCapCAST)
|
|
||||||
```
|
|
||||||
|
|
||||||
# Easy data set to data base workflow
|
|
||||||
|
|
||||||
THe first iteration of a dataset to data dictionary function is the `ds2dd()`, which creates a very basic data dictionary with all variables stored as text. This is sufficient for just storing old datasets/spreadsheets securely in REDCap.
|
|
||||||
|
|
||||||
```{r}
|
|
||||||
mtcars |>
|
|
||||||
dplyr::mutate(record_id = seq_len(dplyr::n())) |>
|
|
||||||
ds2dd()
|
|
||||||
```
|
|
||||||
|
|
||||||
The more advanced `ds2dd_detailed()` is a natural development. It will try to apply the most common data classes for data validation and will assume that the first column is the id number. It outputs a list with the dataset with modified variable names to comply with REDCap naming conventions and a data dictionary.
|
|
||||||
|
|
||||||
The dataset should be correctly formatted for the data dictionary to preserve as much information as possible.
|
|
||||||
|
|
||||||
```{r}
|
|
||||||
dd_ls <- mtcars |>
|
|
||||||
dplyr::mutate(record_id = seq_len(dplyr::n())) |>
|
|
||||||
dplyr::select(record_id, dplyr::everything()) |>
|
|
||||||
ds2dd_detailed()
|
|
||||||
dd_ls |> str()
|
|
||||||
```
|
|
||||||
|
|
||||||
Additional specifications to the DataDictionary can be made manually, or it can be uploaded and modified manually in the graphical user interface on the web page.
|
|
||||||
|
|
||||||
## Step 3 - Meta data upload
|
|
||||||
|
|
||||||
Now the DataDictionary can be exported as a spreadsheet and uploaded or it can be uploaded using the `REDCapR` package (only projects with "Development" status).
|
|
||||||
|
|
||||||
Use one of the two approaches below:
|
|
||||||
|
|
||||||
### Manual upload
|
|
||||||
|
|
||||||
```{r eval=FALSE}
|
|
||||||
write.csv(dd_ls$meta, "datadictionary.csv")
|
|
||||||
```
|
|
||||||
|
|
||||||
### Upload with `REDCapR`
|
|
||||||
|
|
||||||
```{r eval=FALSE}
|
|
||||||
REDCapR::redcap_metadata_write(
|
|
||||||
dd_ls$meta,
|
|
||||||
redcap_uri = keyring::key_get("DB_URI"),
|
|
||||||
token = keyring::key_get("DB_TOKEN")
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
In the ["REDCap R Handbook"](https://agdamsbo.github.io/redcap-r-handbook/) more is written on interfacing with REDCap in R using the `library(keyring)`to store credentials in [chapter 1.1](https://agdamsbo.github.io/redcap-r-handbook/access.html#sec-getting-access).
|
|
||||||
|
|
||||||
## Step 4 - Data upload
|
|
||||||
|
|
||||||
The same two options are available for data upload as meta data upload: manual or through `REDCapR`.
|
|
||||||
|
|
||||||
Only the latter is shown here.
|
|
||||||
|
|
||||||
```{r eval=FALSE}
|
|
||||||
REDCapR::redcap_write(
|
|
||||||
dd_ls$data,
|
|
||||||
redcap_uri = keyring::key_get("DB_URI"),
|
|
||||||
token = keyring::key_get("DB_TOKEN")
|
|
||||||
)
|
|
||||||
```
|
|
@ -1,30 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Introduction"
|
|
||||||
output: rmarkdown::html_vignette
|
|
||||||
vignette: >
|
|
||||||
%\VignetteIndexEntry{Introduction}
|
|
||||||
%\VignetteEngine{knitr::rmarkdown}
|
|
||||||
%\VignetteEncoding{UTF-8}
|
|
||||||
---
|
|
||||||
|
|
||||||
```{r, include = FALSE}
|
|
||||||
knitr::opts_chunk$set(
|
|
||||||
collapse = TRUE,
|
|
||||||
comment = "#>"
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
```{r setup}
|
|
||||||
library(REDCapCAST)
|
|
||||||
```
|
|
||||||
|
|
||||||
To make the easiest possible transistion from spreadsheet/dataset to REDCap, I have created a small Shiny app, which adds a graphical interface to the casting of a data dictionary and data upload. Install the package and run the app as follows:
|
|
||||||
|
|
||||||
```{r}
|
|
||||||
require(REDCapCAST)
|
|
||||||
shiny_cast()
|
|
||||||
```
|
|
||||||
|
|
||||||
The app will launch in a new window and the interface should be fairly self-explanatory.
|
|
||||||
The app only provides the most basic functionality, but might be extended in the future.
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user