From 4a56f4ec45aa259968d88570ecea505b3e7c6de1 Mon Sep 17 00:00:00 2001 From: Andreas Gammelgaard Damsbo Date: Fri, 7 Jun 2024 10:35:16 +0200 Subject: [PATCH] major update with new functions and renv is out! see NEWS section --- .Rprofile | 2 +- .github/workflows/R-CMD-check.yaml | 2 +- .github/workflows/pkgdown.yaml | 2 +- .github/workflows/test-coverage.yaml | 2 +- DESCRIPTION | 2 +- NEWS.md | 2 +- R/doc2dd.R | 25 ++++++++- R/ds2dd_detailed.R | 4 +- .../shinyapps.io/agdamsbo/redcapcast.dcf | 2 +- app/ui.R | 48 +++++++++-------- inst/WORDLIST | 7 ++- man/replace_curly_quote.Rd | 18 +++++++ renv.lock | 52 ++++-------------- tests/spelling.Rout.save | 25 +++++++++ vignettes/Database-creation.Rmd | 54 ++++++++++++++----- vignettes/Shiny-app.Rmd | 4 -- 16 files changed, 158 insertions(+), 93 deletions(-) create mode 100644 man/replace_curly_quote.Rd create mode 100644 tests/spelling.Rout.save diff --git a/.Rprofile b/.Rprofile index e7ad303..f46e78a 100644 --- a/.Rprofile +++ b/.Rprofile @@ -6,7 +6,7 @@ options( ) -source("renv/activate.R") +# source("renv/activate.R") if (interactive()) { suppressMessages(require(usethis)) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 0733ece..c71f5ae 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -39,7 +39,7 @@ jobs: http-user-agent: ${{ matrix.config.http-user-agent }} use-public-rspm: true - - uses: r-lib/actions/setup-renv@v2 + # - uses: r-lib/actions/setup-renv@v2 - uses: r-lib/actions/setup-r-dependencies@v2 with: diff --git a/.github/workflows/pkgdown.yaml b/.github/workflows/pkgdown.yaml index 7bb49ec..d30c854 100644 --- a/.github/workflows/pkgdown.yaml +++ b/.github/workflows/pkgdown.yaml @@ -30,7 +30,7 @@ jobs: with: use-public-rspm: true - - uses: r-lib/actions/setup-renv@v2 + # - uses: r-lib/actions/setup-renv@v2 - uses: r-lib/actions/setup-r-dependencies@v2 with: diff --git a/.github/workflows/test-coverage.yaml b/.github/workflows/test-coverage.yaml index 6027924..8cad675 100644 --- a/.github/workflows/test-coverage.yaml +++ b/.github/workflows/test-coverage.yaml @@ -21,7 +21,7 @@ jobs: with: use-public-rspm: true - - uses: r-lib/actions/setup-renv@v2 + # - uses: r-lib/actions/setup-renv@v2 - uses: r-lib/actions/setup-r-dependencies@v2 with: diff --git a/DESCRIPTION b/DESCRIPTION index 09f5b73..71ca0b3 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: REDCapCAST Title: REDCap Castellated Data Handling -Version: 24.5.1 +Version: 24.6.1 Authors@R: c( person("Andreas Gammelgaard", "Damsbo", email = "agdamsbo@clin.au.dk", role = c("aut", "cre"),comment = c(ORCID = "0000-0002-7559-1154")), diff --git a/NEWS.md b/NEWS.md index 6574886..11a3787 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,4 +1,4 @@ -# REDCapCAST 24.5.1 +# REDCapCAST 24.6.1 ### Functions diff --git a/R/doc2dd.R b/R/doc2dd.R index 1eaf190..13e64ed 100644 --- a/R/doc2dd.R +++ b/R/doc2dd.R @@ -108,6 +108,9 @@ doc2dd <- function(data, ) } + + + ## Defining the calculations if (is_missing(col.calculation)) { out <- out |> @@ -115,12 +118,13 @@ doc2dd <- function(data, calculations = missing.default ) } else { + # With inspiration from textclean package, curly apostrophe is replaced out <- out |> dplyr::mutate( calculations = dplyr::pick(col.calculation) |> unlist() |> tolower() |> - (\(.x) gsub("’", "'", .x))() + replace_curly_quote() ) } @@ -288,3 +292,22 @@ is_missing <- function(data,nas=c("", "NA")) { is.na(data) | data %in% nas } } + + +#' Replace curly apostrophes and quotes from word +#' +#' @description +#' Copied from textclean, which has not been updated since 2018 and is not +#' on CRAN. Github:https://github.com/trinker/textclean +#' +#' @param x character vector +#' +#' @return character vector +replace_curly_quote <- function(x){ + replaces <- c('\x91', '\x92', '\x93', '\x94') + Encoding(replaces) <- "latin1" + for (i in 1:4) { + x <- gsub(replaces[i], c("'", "'", "\"", "\"")[i], x, fixed = TRUE) + } + x +} diff --git a/R/ds2dd_detailed.R b/R/ds2dd_detailed.R index 4d3dcda..731e3b5 100644 --- a/R/ds2dd_detailed.R +++ b/R/ds2dd_detailed.R @@ -343,10 +343,10 @@ ds2dd_detailed <- function(data, lapply(function(x) { if (is.factor(x)) { ## Re-factors to avoid confusion with missing levels - ## Assumes alle relevant levels are represented in the data + ## Assumes all relevant levels are represented in the data re_fac <- factor(x) paste( - paste(unique(as.numeric(re_fac)), + paste(seq_along(levels(re_fac)), levels(re_fac), sep = ", " ), diff --git a/app/rsconnect/shinyapps.io/agdamsbo/redcapcast.dcf b/app/rsconnect/shinyapps.io/agdamsbo/redcapcast.dcf index ccf5c42..9635472 100644 --- a/app/rsconnect/shinyapps.io/agdamsbo/redcapcast.dcf +++ b/app/rsconnect/shinyapps.io/agdamsbo/redcapcast.dcf @@ -5,6 +5,6 @@ account: agdamsbo server: shinyapps.io hostUrl: https://api.shinyapps.io/v1 appId: 11351429 -bundleId: +bundleId: 8567755 url: https://agdamsbo.shinyapps.io/redcapcast/ version: 1 diff --git a/app/ui.R b/app/ui.R index beb1820..0ad38e2 100644 --- a/app/ui.R +++ b/app/ui.R @@ -1,6 +1,6 @@ ui <- shiny::shinyUI( shiny::fluidPage( - theme = shinythemes::shinytheme("united"), + theme = shinythemes::shinytheme("flatly"), ## ----------------------------------------------------------------------------- ## Application title @@ -11,15 +11,20 @@ ui <- shiny::shinyUI( # 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"), + 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("This tool includes to convenient functions:", - shiny::br(), - "1) creating a REDCap data dictionary based on a spreadsheet (.csv/.xls(x)/.dta) and", - shiny::br(), - "2) creating said database on a given REDCap server and uploading the dataset via API access."), + shiny::h4( + "This tool includes to convenient functions:", + shiny::br(), + "1) creating a REDCap data dictionary based on a spreadsheet (.csv/.xls(x)/.dta) and", + shiny::br(), + "2) creating said database on a given REDCap server and uploading the dataset via API access." + ), ## ----------------------------------------------------------------------------- @@ -60,7 +65,7 @@ ui <- shiny::shinyUI( 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::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") @@ -110,17 +115,17 @@ ui <- shiny::shinyUI( shiny::br(), shiny::hr(), shiny::tags$footer(shiny::strong("Disclaimer: "), - "This tool is aimed at demonstrating use of REDCapCAST. 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 = " + "This tool is aimed at demonstrating use of REDCapCAST. 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%; @@ -129,6 +134,7 @@ ui <- shiny::shinyUI( padding: 0px; background-color: White; z-index: 100; - ") + " + ) ) ) diff --git a/inst/WORDLIST b/inst/WORDLIST index 0bb698a..97ee7eb 100644 --- a/inst/WORDLIST +++ b/inst/WORDLIST @@ -4,6 +4,7 @@ Codecov DOI DataDictionary GStat +Github GithubActions JSON Lifecycle @@ -14,7 +15,6 @@ README REDCap REDCapR REDCapRITS -THe UI WD al @@ -34,8 +34,10 @@ dplyr ds dta et +github gues hms +https immprovements io jbi @@ -55,6 +57,7 @@ perl pos pre readr +realising sel sep seperator @@ -64,9 +67,11 @@ stRoke stata strsplit subheader +textclean thorugh tibble tidyverse +trinker ui uri wil diff --git a/man/replace_curly_quote.Rd b/man/replace_curly_quote.Rd new file mode 100644 index 0000000..8b9ed56 --- /dev/null +++ b/man/replace_curly_quote.Rd @@ -0,0 +1,18 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/doc2dd.R +\name{replace_curly_quote} +\alias{replace_curly_quote} +\title{Replace curly apostrophes and quotes from word} +\usage{ +replace_curly_quote(x) +} +\arguments{ +\item{x}{character vector} +} +\value{ +character vector +} +\description{ +Copied from textclean, which has not been updated since 2018 and is not +on CRAN. Github:https://github.com/trinker/textclean +} diff --git a/renv.lock b/renv.lock index a0e4b90..e46fe91 100644 --- a/renv.lock +++ b/renv.lock @@ -1,6 +1,6 @@ { "R": { - "Version": "4.3.3", + "Version": "4.4.0", "Repositories": [ { "Name": "CRAN", @@ -138,14 +138,14 @@ }, "cachem": { "Package": "cachem", - "Version": "1.0.8", + "Version": "1.1.0", "Source": "Repository", "Repository": "CRAN", "Requirements": [ "fastmap", "rlang" ], - "Hash": "c35768291560ce302c0a6589f92e837d" + "Hash": "cd9a672193789068eb5a2aad65a0dedf" }, "cellranger": { "Package": "cellranger", @@ -199,16 +199,6 @@ "Repository": "CRAN", "Hash": "5d8225445acb167abf7797de48b2ee3c" }, - "cpp11": { - "Package": "cpp11", - "Version": "0.4.7", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R" - ], - "Hash": "5a295d7d963cc5035284dcdbaf334f4e" - }, "crayon": { "Package": "crayon", "Version": "1.5.2", @@ -279,10 +269,10 @@ }, "fastmap": { "Package": "fastmap", - "Version": "1.1.1", + "Version": "1.2.0", "Source": "Repository", "Repository": "CRAN", - "Hash": "f7736a18de97dea803bde0a2daaafb27" + "Hash": "aa5e1cd11c2d15497494c5292d7ffcc8" }, "filelock": { "Package": "filelock", @@ -533,13 +523,13 @@ }, "openssl": { "Package": "openssl", - "Version": "2.1.2", + "Version": "2.2.0", "Source": "Repository", "Repository": "CRAN", "Requirements": [ "askpass" ], - "Hash": "ea2475b073243d9d338aa8f086ce973e" + "Hash": "2bcca3848e4734eb3b16103bc9aa4b8e" }, "openxlsx2": { "Package": "openxlsx2", @@ -585,30 +575,6 @@ ], "Hash": "01f28d4278f15c76cddbea05899c5d6f" }, - "prettyunits": { - "Package": "prettyunits", - "Version": "1.2.0", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R" - ], - "Hash": "6b01fc98b1e86c4f705ce9dcfd2f57c7" - }, - "progress": { - "Package": "progress", - "Version": "1.2.3", - "Source": "Repository", - "Repository": "CRAN", - "Requirements": [ - "R", - "R6", - "crayon", - "hms", - "prettyunits" - ], - "Hash": "f4625e061cb2865f111b47ff163a5ca6" - }, "promises": { "Package": "promises", "Version": "1.3.0", @@ -785,7 +751,7 @@ }, "stringi": { "Package": "stringi", - "Version": "1.8.3", + "Version": "1.8.4", "Source": "Repository", "Repository": "CRAN", "Requirements": [ @@ -794,7 +760,7 @@ "tools", "utils" ], - "Hash": "058aebddea264f4c99401515182e656a" + "Hash": "39e1144fd75428983dc3f63aa53dfa91" }, "stringr": { "Package": "stringr", diff --git a/tests/spelling.Rout.save b/tests/spelling.Rout.save new file mode 100644 index 0000000..17bf7b4 --- /dev/null +++ b/tests/spelling.Rout.save @@ -0,0 +1,25 @@ + +R version 3.4.1 (2017-06-30) -- "Single Candle" +Copyright (C) 2017 The R Foundation for Statistical Computing +Platform: x86_64-apple-darwin15.6.0 (64-bit) + +R is free software and comes with ABSOLUTELY NO WARRANTY. +You are welcome to redistribute it under certain conditions. +Type 'license()' or 'licence()' for distribution details. + +R is a collaborative project with many contributors. +Type 'contributors()' for more information and +'citation()' on how to cite R or R packages in publications. + +Type 'demo()' for some demos, 'help()' for on-line help, or +'help.start()' for an HTML browser interface to help. +Type 'q()' to quit R. + +> if(requireNamespace('spelling', quietly = TRUE)) ++ spelling::spell_check_test(vignettes = TRUE, error = FALSE, ++ skip_on_cran = TRUE) +All Done! +> +> proc.time() + user system elapsed + 0.372 0.039 0.408 diff --git a/vignettes/Database-creation.Rmd b/vignettes/Database-creation.Rmd index e71e05e..e8e8450 100644 --- a/vignettes/Database-creation.Rmd +++ b/vignettes/Database-creation.Rmd @@ -18,33 +18,59 @@ knitr::opts_chunk$set( library(REDCapCAST) ``` -# Easy data set to data base workflow +# Two different ways to create a data base -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. +`REDCapCAST` provides two approaches to creating a data dictionary aimed at helping out in two different cases: -```{r eval=FALSE} -mtcars |> +1. Easily create a REDCap data base from an existing data set. + +2. Create a table in Word describing a variables in a data base and use this to create a data base. + +In the following I will try to come with a few suggestions on how to use these approaches. + +## 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 eval=TRUE} +d1 <- mtcars |> dplyr::mutate(record_id = seq_len(dplyr::n())) |> - ds2dd() |> - str() + ds2dd() + +d1 |> + gt::gt() ``` 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 eval=FALSE} -dd_ls <- mtcars |> - dplyr::mutate(record_id = seq_len(dplyr::n())) |> +```{r eval=TRUE} +d2 <- REDCapCAST::redcapcast_data |> + dplyr::mutate(record_id = seq_len(dplyr::n()), + region=factor(region)) |> dplyr::select(record_id, dplyr::everything()) |> - ds2dd_detailed() -dd_ls |> - str() + (\(.x){ + .x[!grepl("_complete$",names(.x))] + })() |> + (\(.x){ + .x[!grepl("^redcap",names(.x))] + })() |> + ds2dd_detailed() |> + purrr::pluck("meta") + +d2 |> + gt::gt() ``` -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. +Additional specifications to the DataDictionary can be made manually, or it can be uploaded and modified manually in the graphical user interface on the REDCap server. -## Step 3 - Meta data upload +## Data base from table + + + + +## Meta data and 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). diff --git a/vignettes/Shiny-app.Rmd b/vignettes/Shiny-app.Rmd index 1c194ab..acb4dd7 100644 --- a/vignettes/Shiny-app.Rmd +++ b/vignettes/Shiny-app.Rmd @@ -14,10 +14,6 @@ knitr::opts_chunk$set( ) ``` -```{r setup} -library(REDCapCAST) -``` - To make the easiest possible transition 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 eval=FALSE}