diff --git a/.Rbuildignore b/.Rbuildignore index 1429c347..65604bd2 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -21,3 +21,4 @@ ^CRAN-SUBMISSION$ +^R/_disable_autoload\.R$ diff --git a/DESCRIPTION b/DESCRIPTION index a278bb58..04fb297d 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: safetyGraphics Title: Interactive Graphics for Monitoring Clinical Trial Safety -Version: 2.1.1 +Version: 2.2.0 Authors@R: c( person("Jeremy", "Wildfire", email = "jwildfire@gmail.com", role = c("cre","aut")), person("Becca", "Krouse", role="aut"), @@ -15,13 +15,11 @@ BugReports: https://github.com/SafetyGraphics/safetyGraphics/issues Depends: R (>= 4.0) License: MIT + file LICENSE Encoding: UTF-8 -LazyData: true -RoxygenNote: 7.2.3 +RoxygenNote: 7.3.2 Suggests: ggplot2 (>= 3.3.0), knitr (>= 1.34), rmarkdown (>= 2.10), - safetyProfile, shinydashboard (>= 0.7.1), shinytest (>= 1.5.0), testthat (>= 3.0.4), @@ -30,6 +28,7 @@ Suggests: shinybusy (>= 0.2.2), shinyWidgets (>= 0.6.1) Imports: + cli (>= 3.0.0), dplyr (>= 1.0.0), DT (>= 0.19), datamods (>= 1.1.5), @@ -40,7 +39,7 @@ Imports: rclipboard (>= 0.1.3), rlang (>= 0.4.11), safetyData (>= 1.0.0), - safetyCharts (>= 0.3), + safetyCharts (>= 0.5.0), shiny (>= 1.6.0), shinyjs (>= 2.0.0), sortable (>= 0.4.4), diff --git a/NAMESPACE b/NAMESPACE index 725ee710..16b423b4 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -59,12 +59,12 @@ import(shiny) import(yaml) importFrom(DT,renderDT) importFrom(magrittr,"%>%") +importFrom(purrr,imap) importFrom(purrr,keep) importFrom(purrr,map) importFrom(purrr,map2) -importFrom(purrr,map_lgl) -importFrom(purrr,set_names) importFrom(rlang,.data) +importFrom(rlang,set_names) importFrom(shiny,dataTableOutput) importFrom(shiny,renderDataTable) importFrom(shinyjs,addClass) diff --git a/NEWS.md b/NEWS.md index 606f5024..a3200926 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,34 @@ +# safetyGraphics v2.2.0 (Upcoming) + +This is the first release since v2.1.1 and is the release that returns safetyGraphics to CRAN. + +## Back on CRAN + +- safetyGraphics was archived from CRAN on 2026-03-25 with the notice "requires archived package 'Tendril'". It never used `Tendril` itself; it was archived because it imports safetyCharts, which did. +- safetyCharts v0.5.0 removes `Tendril`, so no code change was needed here for the archival itself. The changes below are what the resubmission required. +- The version had to move off 2.1.1. CRAN will not accept a version equal to one already in the archive, and 2.1.1 is the version that was archived. Everything listed under "New since the v2.1.1 CRAN release" has been sitting unreleased under that same number. + +## New since the v2.1.1 CRAN release + +- Optional patient profile tab, backed by the `safetyProfile` package. Clicking a participant id in the header opens the profile; the tab is hidden when `safetyProfile` is not loaded. +- New chart status framework. `getChartStatus()` reports whether a chart has every data mapping it needs, and the charts navigation shows that status in the UI. +- App-level settings support, and `launchBrowser` handling, in `safetyGraphicsApp()`. +- Chart modules refactored to simplify the reactive flow, with chart event listeners added to the widgets. +- `generateMappingList()` now works with `tbl_df`-classed data frames. +- Utility source files renamed with a `util-` prefix. + +## Packaging changes for the CRAN resubmission + +- `safetyProfile` has been removed from `Suggests`. It is not on CRAN and never has been, so declaring it made the package uncheckable by CRAN. The patient profile tab is unaffected for anyone who has `safetyProfile` installed; it is now reached through `getExportedValue()`, which loads and resolves exactly as `safetyProfile::` did. Install it with `remotes::install_github("SafetyGraphics/safetyProfile")`. +- `profileTabUI()` no longer fails when the `safetyProfile` namespace is loaded but not attached. It previously called `profile_ui()` unqualified, which is only resolvable off the search path, so `loadNamespace("safetyProfile")` without `library()` produced "could not find function". +- `cli` added to `Imports`. `cli::cli_alert_info()` was already being called in `safetyGraphicsServer()` without being declared anywhere. +- `LazyData: true` removed. The package has no `data/` directory, so the field was inert. +- The `config` argument is now documented for `homeTab()`, `safetyGraphicsServer()` and `safetyGraphicsUI()`. +- The `getChartStatus()` example has been fixed. It called `map()`, `distinct()`, `filter()` and others without qualifying them, so it failed whenever it was run outside an interactive session with the tidyverse attached. +- `NAMESPACE` regenerated. The checked-in copy had drifted from the roxygen comments: it imported `set_names` from `purrr` rather than `rlang`, and `map_lgl` rather than `imap`. +- Ten external links in the Technical FAQ vignette updated. Nine now point at the addresses they were redirecting to; the tenth, a d-wise article that no longer exists, points at its Internet Archive snapshot so the citation still resolves to what was cited. +- Every exported function now documents its return value. CRAN asks for this on new submissions, and an archived package is reviewed as one; 32 exported functions had no `\value` section, including everything in the module layer. + # safetyGraphics v2.1.1 Resolves a bug that was causing unexpected data filters on initial load. diff --git a/R/getChartStatus.R b/R/getChartStatus.R index 6037729e..041c5f2a 100644 --- a/R/getChartStatus.R +++ b/R/getChartStatus.R @@ -15,53 +15,47 @@ #' #' @examples #' sample_chart <- list( -#' domains=c("aes","dm"), -#' dataSpec=list( -#' aes=c("id_col","custom_col"), -#' dm=c("id_col","test_col") +#' domains = c("aes", "dm"), +#' dataSpec = list( +#' aes = c("id_col", "custom_col"), +#' dm = c("id_col", "test_col") #' ) #' ) -#' +#' #' sample_mapping <- data.frame( -#' domain=c("aes","aes","dm","dm"), -#' text_key=c("id_col","custom_col","id_col","test_col"), -#' current=c("myID","AEcol","myID","dmCol") +#' domain = c("aes", "aes", "dm", "dm"), +#' text_key = c("id_col", "custom_col", "id_col", "test_col"), +#' current = c("myID", "AEcol", "myID", "dmCol") #' ) -#' -#' check <- safetyGraphics:::getChartStatus(chart=sample_chart, mapping=sample_mapping) -#' # check$status=TRUE #' -#' # Add data spec to each chart. -#' charts <- makeChartConfig() %>% -#' map(function(chart) { -#' chart$mapping <- chart$domain %>% -#' map_dfr(function(domain) { -#' do.call( -#' `::`, -#' list( -#' 'safetyCharts', -#' paste0('meta_', domain) -#' ) -#' ) -#' }) %>% -#' distinct(domain, col_key, current = standard_adam) %>% -#' filter(!is.na(current)) %>% -#' select(domain, text_key = col_key, current) -#' -#' chart$dataSpec <- chart$domain %>% -#' map(function(domain) { -#' chart$mapping %>% -#' filter(.data$domain == !!domain) %>% -#' pull(text_key) %>% -#' unique() -#' }) %>% -#' set_names(chart$domain) -#' -#' chart -#' }) +#' check <- safetyGraphics:::getChartStatus( +#' chart = sample_chart, +#' mapping = sample_mapping +#' ) +#' # check$status = TRUE +#' +#' # Add a data spec to each chart, then check them all. +#' charts <- purrr::map(safetyGraphics::makeChartConfig(), function(chart) { +#' chart$mapping <- purrr::map_dfr(chart$domain, function(dom) { +#' getExportedValue("safetyCharts", paste0("meta_", dom)) +#' }) %>% +#' dplyr::distinct(domain, col_key, current = standard_adam) %>% +#' dplyr::filter(!is.na(current)) %>% +#' dplyr::select(domain, text_key = col_key, current) +#' +#' chart$dataSpec <- purrr::map(chart$domain, function(dom) { +#' chart$mapping %>% +#' dplyr::filter(domain == dom) %>% +#' dplyr::pull(text_key) %>% +#' unique() +#' }) %>% +#' rlang::set_names(chart$domain) +#' +#' chart +#' }) +#' +#' checks <- purrr::map(charts, ~ safetyGraphics:::getChartStatus(.x, .x$mapping)) #' -#' checks <- map(charts, ~getChartStatus(.x, .x$mapping)) -#' #' @importFrom purrr imap map #' @importFrom rlang set_names #' diff --git a/R/makeChartParams.R b/R/makeChartParams.R index ce76c55d..a2181e8c 100644 --- a/R/makeChartParams.R +++ b/R/makeChartParams.R @@ -5,6 +5,10 @@ #' @param chart list containing chart specifications #' @param mapping data frame with current mapping #' +#' +#' @return A named list of parameters for the chart, ready to pass to the chart's +#' rendering function. For `htmlwidget` charts the list is the widget's +#' parameter object, with `data` and JSON-encoded `settings` attached. #' @export makeChartParams <- function(data, chart, mapping){ diff --git a/R/makeChartSummary.R b/R/makeChartSummary.R index 97882fb8..b9209be9 100644 --- a/R/makeChartSummary.R +++ b/R/makeChartSummary.R @@ -6,6 +6,9 @@ #' @param showLinks boolean indicating whether to include links #' @param class character to include as class #' +#' +#' @return A `shiny::div()` summarising the chart: its label, type, data domains, +#' status and, when `showLinks` is `TRUE`, its documentation links. #' @export makeChartSummary<- function(chart, status=NULL, showLinks=TRUE, class="chart-header"){ diff --git a/R/mod_chartsNav.R b/R/mod_chartsNav.R index d2c3fd76..037f285b 100644 --- a/R/mod_chartsNav.R +++ b/R/mod_chartsNav.R @@ -3,6 +3,9 @@ #' @param id module id #' @param chart chart metadata #' +#' @return A `shiny::tabPanel()` for the chart, ready to be added to the +#' application's navbar. +#' #' @export #' @@ -30,6 +33,9 @@ chartsNavUI <- function(id, chart) { #' @param data named list of current data sets (Reactive). #' @param mapping tibble capturing the current data mappings (Reactive). #' +#' +#' @return No return value. Called for its side effect of starting the chart tab +#' module for this navbar entry. #' @export #' diff --git a/R/mod_chartsTab.R b/R/mod_chartsTab.R index b27c1b63..4ed40dc9 100644 --- a/R/mod_chartsTab.R +++ b/R/mod_chartsTab.R @@ -3,6 +3,9 @@ #' @param id module id #' @param chart list containing chart specifications like those returned by \link{makeChartConfig}. #' +#' +#' @return A list of two Shiny UI elements: the chart header and the chart +#' container. #' @importFrom stringr str_to_title #' @importFrom purrr map2 #' @@ -26,6 +29,9 @@ chartsTabUI <- function(id, chart){ #' @param mapping tibble capturing the current data mappings (Reactive). #' @param status chart status (Reactive) #' +#' +#' @return No return value. Called for its side effect of rendering the chart and +#' wiring up its export handlers. #' @export chartsTab <- function(input, output, session, chart, data, mapping, status){ diff --git a/R/mod_filterTab.R b/R/mod_filterTab.R index 0eb8040e..0afc4340 100644 --- a/R/mod_filterTab.R +++ b/R/mod_filterTab.R @@ -2,6 +2,8 @@ #' #' @param id module id #' +#' +#' @return The filter module's Shiny UI. #' @import datamods #' @importFrom shiny dataTableOutput #' diff --git a/R/mod_homeTab.R b/R/mod_homeTab.R index d942bd4e..c146b95c 100644 --- a/R/mod_homeTab.R +++ b/R/mod_homeTab.R @@ -2,6 +2,8 @@ #' #' @param id module id #' +#' +#' @return A `shiny::fluidRow()` containing the home tab's layout. #' @export homeTabUI <- function(id) { @@ -17,7 +19,13 @@ homeTabUI <- function(id) { #' @param input mod input #' @param output mod output #' @param session mod session +#' @param config named list of app configuration options as returned by +#' \code{app_startup()}. `homeTab()` uses the `homeTabPath` and `hexPath` +#' elements. #' +#' +#' @return No return value. Called for its side effect of rendering the home tab's +#' text and hex sticker. #' @export homeTab <- function(input, output, session, config) { diff --git a/R/mod_loadCharts.R b/R/mod_loadCharts.R index 858095a0..8a99045e 100644 --- a/R/mod_loadCharts.R +++ b/R/mod_loadCharts.R @@ -3,6 +3,8 @@ #' @param id module id #' @param charts list containing chart specifications like those returned by \link{makeChartConfig}. #' +#' +#' @return The chart loader's Shiny UI. #' @importFrom purrr keep #' @importFrom sortable bucket_list add_rank_list #' @@ -27,6 +29,8 @@ loadChartsUI <- function(id, charts=makeChartConfig()){ #' @param session Shiny session object #' @param charts list containing chart specifications like those returned by \link{makeChartConfig}. #' +#' +#' @return A `shiny::reactive()` returning the list of charts the user has selected. #' @export loadCharts <- function(input, output, session, charts=makeChartConfig()) { diff --git a/R/mod_loadData.R b/R/mod_loadData.R index 9673952e..f284965c 100644 --- a/R/mod_loadData.R +++ b/R/mod_loadData.R @@ -3,6 +3,8 @@ #' @param id module id #' @param domain character vector with domains to be loaded #' +#' +#' @return The data loader's Shiny UI for one domain. #' @export loadDataUI <- function(id, domain=NULL){ @@ -25,6 +27,8 @@ loadDataUI <- function(id, domain=NULL){ #' @param output Shiny output object #' @param session Shiny session object #' +#' +#' @return A `shiny::reactive()` returning the data frame imported for this domain. #' @export loadData <- function(input, output, session, domain) { diff --git a/R/mod_mappingColumn.R b/R/mod_mappingColumn.R index b9b7ea25..ad7d3c66 100644 --- a/R/mod_mappingColumn.R +++ b/R/mod_mappingColumn.R @@ -5,6 +5,8 @@ #' @param data current data file for the domain #' @param mapping current data mapping for the column (and related fields) +#' +#' @return The Shiny UI for a single column mapping. #' @export mappingColumnUI <- function(id, meta, data, mapping=NULL){ diff --git a/R/mod_mappingDomain.R b/R/mod_mappingDomain.R index 38871694..705a0b9e 100644 --- a/R/mod_mappingDomain.R +++ b/R/mod_mappingDomain.R @@ -5,6 +5,8 @@ #' @param data data file for the domain #' @param mapping current data mapping #' +#' +#' @return The Shiny UI for one data domain's mappings. #' @export mappingDomainUI <- function(id, meta, data, mapping=NULL){ diff --git a/R/mod_mappingTab.R b/R/mod_mappingTab.R index d41bdf52..c15a16a0 100644 --- a/R/mod_mappingTab.R +++ b/R/mod_mappingTab.R @@ -6,6 +6,8 @@ #' @param mappings optional data frame containing stacked mappings for all domains #' @param standards optional list of data standards like the ones generated by \code{detectStandard()} #' +#' +#' @return The Shiny UI for the mapping tab. #' @importFrom stringr str_to_upper #' #' @export diff --git a/R/mod_profileTab.R b/R/mod_profileTab.R index 9c305358..341adef3 100644 --- a/R/mod_profileTab.R +++ b/R/mod_profileTab.R @@ -1,48 +1,76 @@ -#' @title UI for the profile module in safetyProfile::profile_ui +#' @title UI for the optional patient profile module +#' +#' @description +#' Renders the UI for the patient profile module supplied by the `safetyProfile` +#' package. `safetyProfile` is an optional add-on that is not distributed on +#' CRAN; when its namespace is not loaded, this function returns `NULL` and the +#' profile tab is simply absent from the application. +#' +#' Install it with `remotes::install_github("SafetyGraphics/safetyProfile")`, +#' then load it with `library(safetyProfile)` before starting the app. #' #' @param id module id -#' +#' +#' @return The profile module UI when `safetyProfile` is available, otherwise +#' `NULL`. +#' #' @export -profileTabUI <- function(id){ +profileTabUI <- function(id){ ns <- NS(id) - - if(isNamespaceLoaded("safetyProfile")){ - profile_ui<-profile_ui(ns("profile")) - }else{ - profile_ui<-NULL + + if (!has_safety_profile()) { + return(NULL) } - return(profile_ui) + + profile_ui <- getExportedValue("safetyProfile", "profile_ui") + + return(profile_ui(ns("profile"))) } -#' @title Server for the patient profile in safetyProfile::profile_server +#' @title Server for the optional patient profile module +#' +#' @description +#' Starts the patient profile module supplied by the `safetyProfile` package. +#' `safetyProfile` is an optional add-on that is not distributed on CRAN. +#' +#' Install it with `remotes::install_github("SafetyGraphics/safetyProfile")`. +#' +#' @details +#' This function requires `safetyProfile` and raises an error if it is not +#' installed, exactly as calling `safetyProfile::profile_server()` directly +#' would. `safetyGraphicsServer()` only reaches it when the namespace is +#' loaded, so the application never hits that error. It is reached through +#' `getExportedValue()` rather than `::` so that a package CRAN cannot see is +#' not written into safetyGraphics as a hard reference. #' #' @param input Shiny input object #' @param output Shiny output object #' @param session Shiny session object #' @param params reactive containing mapping and data -#' @param current_id reactive containing currently selected participant -#' -#' @return current_id +#' +#' @return A reactive containing the currently selected participant id. #' #' @import datamods #' @importFrom shinyjs show hide #' @importFrom shiny renderDataTable -#' +#' #' @export profileTab <- function(input, output, session, params) { - id <- safetyProfile::profile_server( + profile_server <- getExportedValue("safetyProfile", "profile_server") + + id <- profile_server( "profile", params ) observe({ shinyjs::html( - "pt-header", + "pt-header", id(), - asis=TRUE + asis=TRUE ) shinyjs::toggleClass( @@ -54,3 +82,25 @@ profileTab <- function(input, output, session, params) { return(id) } + + +#' Is the optional safetyProfile package available? +#' +#' @description +#' `safetyProfile` supplies the patient profile module. It is not distributed on +#' CRAN, so safetyGraphics treats it as strictly optional and gates the profile +#' tab's UI on this check. +#' +#' Matches the test used for the other optional packages in this application +#' (`listviewer`, `shinybusy`, `shinyWidgets`): the feature appears once the +#' user has attached the package, not merely installed it. +#' +#' @return `TRUE` when the `safetyProfile` namespace is loaded, otherwise +#' `FALSE`. +#' +#' @keywords internal +#' @noRd + +has_safety_profile <- function() { + isNamespaceLoaded("safetyProfile") +} diff --git a/R/mod_safetyGraphicsServer.R b/R/mod_safetyGraphicsServer.R index dae54fa5..b5b6563a 100644 --- a/R/mod_safetyGraphicsServer.R +++ b/R/mod_safetyGraphicsServer.R @@ -10,7 +10,11 @@ #' @param mapping current mapping #' @param charts list of charts to include in the app #' @param filterDomain domain used for the data/filter tab. Demographics ("`dm`") is used by default. Using a domain that is not one record per participant is not recommended. +#' @param config named list of app configuration options as returned by \code{app_startup()}, passed through to the modules that need it. #' +#' +#' @return No return value. Called for its side effect of starting the application's +#' home, mapping, filter, chart and settings modules. #' @import shiny #' @import dplyr #' @importFrom purrr map diff --git a/R/mod_safetyGraphicsUI.R b/R/mod_safetyGraphicsUI.R index c6ff6051..39a7c256 100644 --- a/R/mod_safetyGraphicsUI.R +++ b/R/mod_safetyGraphicsUI.R @@ -6,7 +6,10 @@ #' @param domainData named list of data.frames to be loaded in to the app. #' @param mapping data.frame specifying the initial values for each data mapping. If no mapping is provided, the app will attempt to generate one via \code{detectStandard()} #' @param standards a list of information regarding data standards. Each list item should use the format returned by safetyGraphics::detectStandard. +#' @param config named list of app configuration options as returned by \code{app_startup()}, passed through to the modules that need it. #' +#' +#' @return The Shiny UI for the whole application. #' @importFrom shinyjs useShinyjs #' #' @export diff --git a/R/mod_settingsCharts.R b/R/mod_settingsCharts.R index 98fafc9d..98685e2f 100644 --- a/R/mod_settingsCharts.R +++ b/R/mod_settingsCharts.R @@ -2,6 +2,8 @@ #' #' @param id module id #' +#' +#' @return The Shiny UI for the chart settings panel. #' @export settingsChartsUI <- function(id){ @@ -37,6 +39,9 @@ settingsChartsUI <- function(id){ #' @param session Shiny session object #' @param charts list data frame summarizing the charts #' +#' +#' @return No return value. Called for its side effect of rendering the chart +#' configuration as YAML. #' @import dplyr #' #' @export diff --git a/R/mod_settingsCode.R b/R/mod_settingsCode.R index 6280c131..1a0707c3 100644 --- a/R/mod_settingsCode.R +++ b/R/mod_settingsCode.R @@ -2,6 +2,8 @@ #' #' @param id module ID #' +#' +#' @return The Shiny UI for the code settings panel. #' @export settingsCodeUI <- function(id){ @@ -101,6 +103,9 @@ settingsCodeUI <- function(id){ #' @param charts charts #' @param domainData data list #' +#' +#' @return No return value. Called for its side effect of rendering the restart code +#' and registering the app download handler. #' @importFrom utils zip #' #' @export diff --git a/R/mod_settingsData.R b/R/mod_settingsData.R index 8d15aae3..c39ac184 100644 --- a/R/mod_settingsData.R +++ b/R/mod_settingsData.R @@ -4,6 +4,8 @@ #' #' @importFrom DT renderDT +#' +#' @return The Shiny UI for the data settings panel. #' @export settingsDataUI <- function(id){ @@ -26,6 +28,9 @@ settingsDataUI <- function(id){ #' @param session Shiny session object #' @param domains named list of the data.frames for each domain #' +#' +#' @return No return value. Called for its side effect of rendering a preview table +#' for each data domain. #' @export settingsData <- function(input, output, session, domains){ diff --git a/R/mod_settingsMapping.R b/R/mod_settingsMapping.R index 9b1b353e..62817b19 100644 --- a/R/mod_settingsMapping.R +++ b/R/mod_settingsMapping.R @@ -2,6 +2,8 @@ #' #' @param id module id #' +#' +#' @return The Shiny UI for the mapping settings panel. #' @import rclipboard #' #' @export @@ -26,6 +28,9 @@ settingsMappingUI <- function(id){ #' @param metadata Data mapping metadata used for initial loading of app #' @param mapping reactive data frame representing the current metadata mapping. columns = "domain", "text_id" and "current" #' +#' +#' @return No return value. Called for its side effect of rendering the mapping and +#' metadata tables. #' @import rclipboard #' @import yaml #' diff --git a/R/mod_settingsTab.R b/R/mod_settingsTab.R index 380cd13b..04416618 100644 --- a/R/mod_settingsTab.R +++ b/R/mod_settingsTab.R @@ -2,6 +2,8 @@ #' #' @param id module ID #' +#' +#' @return A `shiny::tabsetPanel()` containing the four settings panels. #' @export settingsTabUI <- function(id){ @@ -26,6 +28,9 @@ settingsTabUI <- function(id){ #' @param mapping mapping #' @param charts charts #' +#' +#' @return No return value. Called for its side effect of starting the mapping, +#' data, chart and code settings modules. #' @export settingsTab <- function(input, output, session, domains, metadata, mapping, charts){ diff --git a/R/safetyGraphicsApp.R b/R/safetyGraphicsApp.R index 15525ff4..7eb68ec5 100644 --- a/R/safetyGraphicsApp.R +++ b/R/safetyGraphicsApp.R @@ -13,6 +13,9 @@ #' @param launchBrowser boolean indicating whether to launch the app in a browser. default is false #' @param runNow Should the shiny app object created be run directly? Helpful when writing functions to dispatch to shinyapps, rsconnect, or shinyproxy. #' +#' +#' @return A Shiny app object, invisibly to the caller when `runNow` is `TRUE` +#' because the app is run before it is returned. #' @import shiny #' @import safetyData #' diff --git a/R/safetyGraphicsInit.R b/R/safetyGraphicsInit.R index bd5dc165..48d145f3 100644 --- a/R/safetyGraphicsInit.R +++ b/R/safetyGraphicsInit.R @@ -4,6 +4,9 @@ #' @param delayTime time (in ms) between drawing app UI and starting server. Default set to 1000 (1 second), but could need to be higher on slow machine. #' @param maxFileSize maximum file size in MB allowed for file upload #' +#' +#' @return No return value. Called for its side effect of running the initialisation +#' app, which in turn launches the main application. #' @import shiny #' @importFrom shinyjs hidden hide show delay disabled disable enable #' diff --git a/R/util-generateMappingList.R b/R/util-generateMappingList.R index 53d53374..994b1605 100644 --- a/R/util-generateMappingList.R +++ b/R/util-generateMappingList.R @@ -4,6 +4,10 @@ #' @param domain mapping domain to return (returns all domains as a named list by default) #' @param pull call pull() the value for each parameter - needed for testing only. default: FALSE #' +#' +#' @return A named list of data mappings. When `domain` names a single domain the +#' list holds that domain's mappings; otherwise it is a list of such lists, +#' one per domain. #' @importFrom stringr str_split #' @export diff --git a/cran-comments.md b/cran-comments.md index cfe28e38..6a060178 100644 --- a/cran-comments.md +++ b/cran-comments.md @@ -1,11 +1,33 @@ +## Resubmission of an archived package + +safetyGraphics was archived on CRAN on 2026-03-25 because it required the archived package 'Tendril'. It has never used 'Tendril' directly; it was archived because it imports 'safetyCharts', which did. + + + +'safetyCharts' , which removes 'Tendril', was accepted to CRAN on +. Every dependency of safetyGraphics is now available from CRAN. + +The version is 2.2.0. The last version on CRAN was 2.1.1, so the version had to be increased. This release also carries the work that accumulated between the 2.1.1 release and the archival; see NEWS.md. + +`R CMD check --as-cran` reports "New submission" and "Package was archived on CRAN" in the incoming feasibility check. Both are expected for the resubmission of an archived package, and the reason for the archival is resolved as described above. + +The package has an optional patient profile tab backed by 'safetyProfile', which is not on CRAN and never has been. It is therefore declared in no DESCRIPTION field: declaring it in Suggests would make the package uncheckable, and there is no repository to name in Additional_repositories. It is reached only through getExportedValue() behind a namespace-loaded guard, so safetyGraphics installs, checks and runs without it; the tab is simply absent. The manual points users at the GitHub repository for anyone who wants it. Please let me know if you would prefer this handled differently. + +One URL in the Technical FAQ vignette is reported as possibly invalid: + +* https://www.hhs.gov/ohrp/sites/default/files/report-privacy-and-health-10-31-19.pdf returns 403 Forbidden. The server rejects automated requests, including from a client sending a full browser user-agent; the link is not dead. The other URLs previously flagged as moved have been updated to their current addresses. + ## Test environments -* local OS X install, R 4.1.0 -* Fedora Linux, R-devel, clang, gfortran (rhub.io) -* Ubuntu Linux 16.04 LTS, R-release, GCC (rhub.io) -* Windows Server 2008 R2 SP1, R-devel, 32/64 bit (rhub.io) + +* local macOS 14.6.1 (aarch64), R 4.3.3 ## R CMD check results -There were no ERRORs, WARNINGs or NOTEs. + + ## Downstream dependencies -None + +None on CRAN. diff --git a/man/SafetyGraphics.Rd b/man/SafetyGraphics.Rd index 8241bb1e..3a1a5e84 100644 --- a/man/SafetyGraphics.Rd +++ b/man/SafetyGraphics.Rd @@ -2,6 +2,8 @@ % Please edit documentation in R/safetyGraphics.R \docType{package} \name{SafetyGraphics} +\alias{safetyGraphics} +\alias{safetyGraphics-package} \alias{SafetyGraphics} \title{\code{safetyGraphics} package} \description{ @@ -11,5 +13,26 @@ Create Interactive Graphics Related to Clinical Trial Safety Learn more on \href{https://CRAN.R-project.org/package=safetyGraphics}{CRAN} or \href{https://github.com/SafetyGraphics/safetyGraphics}{GitHub} +} +\seealso{ +Useful links: +\itemize{ + \item \url{https://github.com/SafetyGraphics/safetyGraphics} + \item Report bugs at \url{https://github.com/SafetyGraphics/safetyGraphics/issues} +} + +} +\author{ +\strong{Maintainer}: Jeremy Wildfire \email{jwildfire@gmail.com} + +Authors: +\itemize{ + \item Becca Krouse + \item Preston Burns + \item Xiao Ni + \item James Buchanan + \item Susan Duke +} + } \keyword{internal} diff --git a/man/chartsNav.Rd b/man/chartsNav.Rd index b46e9f65..7799a81d 100644 --- a/man/chartsNav.Rd +++ b/man/chartsNav.Rd @@ -19,6 +19,10 @@ chartsNav(input, output, session, chart, data, mapping) \item{mapping}{tibble capturing the current data mappings (Reactive).} } +\value{ +No return value. Called for its side effect of starting the chart tab +module for this navbar entry. +} \description{ Server for a navbar tab } diff --git a/man/chartsNavUI.Rd b/man/chartsNavUI.Rd index bc3cea1f..58fc9e03 100644 --- a/man/chartsNavUI.Rd +++ b/man/chartsNavUI.Rd @@ -11,6 +11,10 @@ chartsNavUI(id, chart) \item{chart}{chart metadata} } +\value{ +A \code{shiny::tabPanel()} for the chart, ready to be added to the +application's navbar. +} \description{ Adds a navbar tab that initializes the Chart Module UI } diff --git a/man/chartsTab.Rd b/man/chartsTab.Rd index 46d4985b..bd90fb22 100644 --- a/man/chartsTab.Rd +++ b/man/chartsTab.Rd @@ -21,6 +21,10 @@ chartsTab(input, output, session, chart, data, mapping, status) \item{status}{chart status (Reactive)} } +\value{ +No return value. Called for its side effect of rendering the chart and +wiring up its export handlers. +} \description{ Server for chart module, designed to be re-used for each chart generated. } diff --git a/man/chartsTabUI.Rd b/man/chartsTabUI.Rd index f27a9aea..5bd69920 100644 --- a/man/chartsTabUI.Rd +++ b/man/chartsTabUI.Rd @@ -11,6 +11,10 @@ chartsTabUI(id, chart) \item{chart}{list containing chart specifications like those returned by \link{makeChartConfig}.} } +\value{ +A list of two Shiny UI elements: the chart header and the chart +container. +} \description{ UI for chart module, designed to be re-used for each chart generated. } diff --git a/man/detectStandard.Rd b/man/detectStandard.Rd index b627b42c..e2d8a5f8 100644 --- a/man/detectStandard.Rd +++ b/man/detectStandard.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/detectStandard.R +% Please edit documentation in R/util-detectStandard.R \name{detectStandard} \alias{detectStandard} \title{Detect the data standard used for a data set} diff --git a/man/evaluateStandard.Rd b/man/evaluateStandard.Rd index f1ef1c25..3336bc30 100644 --- a/man/evaluateStandard.Rd +++ b/man/evaluateStandard.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/evaluateStandard.R +% Please edit documentation in R/util-evaluateStandard.R \name{evaluateStandard} \alias{evaluateStandard} \title{Evaluate a data set against a data standard} diff --git a/man/filterTabUI.Rd b/man/filterTabUI.Rd index 9c0841d5..82e83f08 100644 --- a/man/filterTabUI.Rd +++ b/man/filterTabUI.Rd @@ -9,6 +9,9 @@ filterTabUI(id) \arguments{ \item{id}{module id} } +\value{ +The filter module's Shiny UI. +} \description{ UI for the filter module in datamods::filter_data_ui } diff --git a/man/generateMappingList.Rd b/man/generateMappingList.Rd index cf9573bf..d84c8e7c 100644 --- a/man/generateMappingList.Rd +++ b/man/generateMappingList.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/generateMappingList.R +% Please edit documentation in R/util-generateMappingList.R \name{generateMappingList} \alias{generateMappingList} \title{Convert mapping data.frame to a list} @@ -13,6 +13,11 @@ generateMappingList(settingsDF, domain = NULL, pull = FALSE) \item{pull}{call pull() the value for each parameter - needed for testing only. default: FALSE} } +\value{ +A named list of data mappings. When \code{domain} names a single domain the +list holds that domain's mappings; otherwise it is a list of such lists, +one per domain. +} \description{ Convert mapping data.frame to a list } diff --git a/man/getChartStatus.Rd b/man/getChartStatus.Rd index be1a5a86..7d8db0fa 100644 --- a/man/getChartStatus.Rd +++ b/man/getChartStatus.Rd @@ -7,41 +7,66 @@ getChartStatus(chart, mapping) } \arguments{ -\item{chart}{chart object} +\item{chart}{\code{list} chart object} -\item{mapping}{the current mapping data.frame} +\item{mapping}{\code{data.frame} current mapping} } \value{ -a list with \code{status}, \code{domains} and \code{columns} properties -} -\description{ -Checks a chart's status when column-level chart specifications are provided in chart$dataSpec. Note that safetyGraphicsApp() does not allow a \code{mapping} value that is not found in \code{domainData}, so this function only needs to check that an expected parameter exists in \code{mapping} (not that the specified column is found in the loaded data). -} -\details{ -Returns a list, with: +\code{list} Named list with properties: \itemize{ -\item \code{status} (TRUE, FALSE) -\item \code{domains} a list specifying wheter all columns are specified in each domain -\item \code{columns} a list that matches the structure of chart$dataSpec and indicates which variables are available. +\item status \code{logical} +\item domains \code{list} list specifying whether all columns are specified in each domain +\item columns \code{list} list that matches the structure of chart$dataSpec and indicates which variables are available. } } +\description{ +Checks a chart's status when column-level chart specifications are provided in chart$dataSpec. +Note that safetyGraphicsApp() does not allow a \code{mapping} value that is not found in \code{domainData}, +so this function only needs to check that an expected parameter exists in \code{mapping} (not that the +specified column is found in the loaded data). +} \examples{ sample_chart <- list( - domains=c("aes","dm"), - dataSpec=list( - aes=c("id_col","custom_col"), - dm=c("id_col","test_col") + domains = c("aes", "dm"), + dataSpec = list( + aes = c("id_col", "custom_col"), + dm = c("id_col", "test_col") ) ) sample_mapping <- data.frame( - domain=c("aes","aes","dm","dm"), - text_key=c("id_col","custom_col","id_col","test_col"), - current=c("myID","AEcol","myID","dmCol") + domain = c("aes", "aes", "dm", "dm"), + text_key = c("id_col", "custom_col", "id_col", "test_col"), + current = c("myID", "AEcol", "myID", "dmCol") ) -check <- safetyGraphics:::getChartStatus(chart=sample_chart, mapping=sample_mapping) -# check$status=TRUE +check <- safetyGraphics:::getChartStatus( + chart = sample_chart, + mapping = sample_mapping +) +# check$status = TRUE + +# Add a data spec to each chart, then check them all. +charts <- purrr::map(safetyGraphics::makeChartConfig(), function(chart) { + chart$mapping <- purrr::map_dfr(chart$domain, function(dom) { + getExportedValue("safetyCharts", paste0("meta_", dom)) + }) \%>\% + dplyr::distinct(domain, col_key, current = standard_adam) \%>\% + dplyr::filter(!is.na(current)) \%>\% + dplyr::select(domain, text_key = col_key, current) + + chart$dataSpec <- purrr::map(chart$domain, function(dom) { + chart$mapping \%>\% + dplyr::filter(domain == dom) \%>\% + dplyr::pull(text_key) \%>\% + unique() + }) \%>\% + rlang::set_names(chart$domain) + + chart +}) + +checks <- purrr::map(charts, ~ safetyGraphics:::getChartStatus(.x, .x$mapping)) } \keyword{internal} diff --git a/man/hasColumn.Rd b/man/hasColumn.Rd index dc56ef95..b69173f6 100644 --- a/man/hasColumn.Rd +++ b/man/hasColumn.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/hasColumn.R +% Please edit documentation in R/util-hasColumn.R \name{hasColumn} \alias{hasColumn} \title{Check whether a column is found in a data set} diff --git a/man/hasField.Rd b/man/hasField.Rd index 4e0e9412..b15c25e6 100644 --- a/man/hasField.Rd +++ b/man/hasField.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/hasField.R +% Please edit documentation in R/util-hasField.R \name{hasField} \alias{hasField} \title{Check whether a specified field value is found in a data set} diff --git a/man/homeTab.Rd b/man/homeTab.Rd index 08da5ed2..c107263f 100644 --- a/man/homeTab.Rd +++ b/man/homeTab.Rd @@ -12,6 +12,14 @@ homeTab(input, output, session, config) \item{output}{mod output} \item{session}{mod session} + +\item{config}{named list of app configuration options as returned by +\code{app_startup()}. \code{homeTab()} uses the \code{homeTabPath} and \code{hexPath} +elements.} +} +\value{ +No return value. Called for its side effect of rendering the home tab's +text and hex sticker. } \description{ Server for the filter module in datamods::filter_data_ui diff --git a/man/homeTabUI.Rd b/man/homeTabUI.Rd index eb271cfd..7a094c48 100644 --- a/man/homeTabUI.Rd +++ b/man/homeTabUI.Rd @@ -9,6 +9,9 @@ homeTabUI(id) \arguments{ \item{id}{module id} } +\value{ +A \code{shiny::fluidRow()} containing the home tab's layout. +} \description{ UI for the home module } diff --git a/man/loadCharts.Rd b/man/loadCharts.Rd index f05f3d70..5af6f285 100644 --- a/man/loadCharts.Rd +++ b/man/loadCharts.Rd @@ -15,6 +15,9 @@ loadCharts(input, output, session, charts = makeChartConfig()) \item{charts}{list containing chart specifications like those returned by \link{makeChartConfig}.} } +\value{ +A \code{shiny::reactive()} returning the list of charts the user has selected. +} \description{ Server for the chart loading module used in safetyGraphicsInit() } diff --git a/man/loadChartsUI.Rd b/man/loadChartsUI.Rd index 174e180e..c8c110ab 100644 --- a/man/loadChartsUI.Rd +++ b/man/loadChartsUI.Rd @@ -11,6 +11,9 @@ loadChartsUI(id, charts = makeChartConfig()) \item{charts}{list containing chart specifications like those returned by \link{makeChartConfig}.} } +\value{ +The chart loader's Shiny UI. +} \description{ UI for the chart loading module used in safetyGraphicsInit() } diff --git a/man/loadData.Rd b/man/loadData.Rd index d47020a7..3caad445 100644 --- a/man/loadData.Rd +++ b/man/loadData.Rd @@ -15,6 +15,9 @@ loadData(input, output, session, domain) \item{domain}{data domain to be loaded} } +\value{ +A \code{shiny::reactive()} returning the data frame imported for this domain. +} \description{ Server for the data loading module used in safetyGraphicsInit() } diff --git a/man/loadDataUI.Rd b/man/loadDataUI.Rd index 2ed708d8..d407c12a 100644 --- a/man/loadDataUI.Rd +++ b/man/loadDataUI.Rd @@ -11,6 +11,9 @@ loadDataUI(id, domain = NULL) \item{domain}{character vector with domains to be loaded} } +\value{ +The data loader's Shiny UI for one domain. +} \description{ UI for the data loading module used in safetyGraphicsInit() } diff --git a/man/makeChartParams.Rd b/man/makeChartParams.Rd index 09b47c83..3f06b38c 100644 --- a/man/makeChartParams.Rd +++ b/man/makeChartParams.Rd @@ -13,6 +13,11 @@ makeChartParams(data, chart, mapping) \item{mapping}{data frame with current mapping} } +\value{ +A named list of parameters for the chart, ready to pass to the chart's +rendering function. For \code{htmlwidget} charts the list is the widget's +parameter object, with \code{data} and JSON-encoded \code{settings} attached. +} \description{ Updates raw data and mapping for use with a specific chart } diff --git a/man/makeChartSummary.Rd b/man/makeChartSummary.Rd index 1bdda058..fb24b685 100644 --- a/man/makeChartSummary.Rd +++ b/man/makeChartSummary.Rd @@ -20,6 +20,10 @@ makeChartSummary( \item{class}{character to include as class} } +\value{ +A \code{shiny::div()} summarising the chart: its label, type, data domains, +status and, when \code{showLinks} is \code{TRUE}, its documentation links. +} \description{ makes a nicely formatted html summary for a chart object } diff --git a/man/mappingColumnUI.Rd b/man/mappingColumnUI.Rd index d195c9c0..cfd489be 100644 --- a/man/mappingColumnUI.Rd +++ b/man/mappingColumnUI.Rd @@ -15,6 +15,9 @@ mappingColumnUI(id, meta, data, mapping = NULL) \item{mapping}{current data mapping for the column (and related fields)} } +\value{ +The Shiny UI for a single column mapping. +} \description{ UI that facilitates the mapping of a column data (and any associated fields) } diff --git a/man/mappingDomainUI.Rd b/man/mappingDomainUI.Rd index 960372ce..e5c4b648 100644 --- a/man/mappingDomainUI.Rd +++ b/man/mappingDomainUI.Rd @@ -15,6 +15,9 @@ mappingDomainUI(id, meta, data, mapping = NULL) \item{mapping}{current data mapping} } +\value{ +The Shiny UI for one data domain's mappings. +} \description{ UI that facilitates the mapping of a full data domain } diff --git a/man/mappingTabUI.Rd b/man/mappingTabUI.Rd index 08fec487..b1980eec 100644 --- a/man/mappingTabUI.Rd +++ b/man/mappingTabUI.Rd @@ -17,6 +17,9 @@ mappingTabUI(id, meta, domainData, mappings = NULL, standards = NULL) \item{standards}{optional list of data standards like the ones generated by \code{detectStandard()}} } +\value{ +The Shiny UI for the mapping tab. +} \description{ UI for mapping tab covering of all data domains } diff --git a/man/profileTab.Rd b/man/profileTab.Rd index d74e9f09..cf178aa4 100644 --- a/man/profileTab.Rd +++ b/man/profileTab.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/mod_profileTab.R \name{profileTab} \alias{profileTab} -\title{Server for the patient profile in safetyProfile::profile_server} +\title{Server for the optional patient profile module} \usage{ profileTab(input, output, session, params) } @@ -14,12 +14,21 @@ profileTab(input, output, session, params) \item{session}{Shiny session object} \item{params}{reactive containing mapping and data} - -\item{current_id}{reactive containing currently selected participant} } \value{ -current_id +A reactive containing the currently selected participant id. } \description{ -Server for the patient profile in safetyProfile::profile_server +Starts the patient profile module supplied by the \code{safetyProfile} package. +\code{safetyProfile} is an optional add-on that is not distributed on CRAN. + +Install it with \code{remotes::install_github("SafetyGraphics/safetyProfile")}. +} +\details{ +This function requires \code{safetyProfile} and raises an error if it is not +installed, exactly as calling \code{safetyProfile::profile_server()} directly +would. \code{safetyGraphicsServer()} only reaches it when the namespace is +loaded, so the application never hits that error. It is reached through +\code{getExportedValue()} rather than \code{::} so that a package CRAN cannot see is +not written into safetyGraphics as a hard reference. } diff --git a/man/profileTabUI.Rd b/man/profileTabUI.Rd index 0db64c61..8c1bc62a 100644 --- a/man/profileTabUI.Rd +++ b/man/profileTabUI.Rd @@ -2,13 +2,23 @@ % Please edit documentation in R/mod_profileTab.R \name{profileTabUI} \alias{profileTabUI} -\title{UI for the profile module in safetyProfile::profile_ui} +\title{UI for the optional patient profile module} \usage{ profileTabUI(id) } \arguments{ \item{id}{module id} } +\value{ +The profile module UI when \code{safetyProfile} is available, otherwise +\code{NULL}. +} \description{ -UI for the profile module in safetyProfile::profile_ui +Renders the UI for the patient profile module supplied by the \code{safetyProfile} +package. \code{safetyProfile} is an optional add-on that is not distributed on +CRAN; when its namespace is not loaded, this function returns \code{NULL} and the +profile tab is simply absent from the application. + +Install it with \code{remotes::install_github("SafetyGraphics/safetyProfile")}, +then load it with \code{library(safetyProfile)} before starting the app. } diff --git a/man/safetyGraphicsApp.Rd b/man/safetyGraphicsApp.Rd index 4936c37a..941ccfa6 100644 --- a/man/safetyGraphicsApp.Rd +++ b/man/safetyGraphicsApp.Rd @@ -46,6 +46,10 @@ safetyGraphicsApp( \item{runNow}{Should the shiny app object created be run directly? Helpful when writing functions to dispatch to shinyapps, rsconnect, or shinyproxy.} } +\value{ +A Shiny app object, invisibly to the caller when \code{runNow} is \code{TRUE} +because the app is run before it is returned. +} \description{ Run the core safetyGraphics App } diff --git a/man/safetyGraphicsInit.Rd b/man/safetyGraphicsInit.Rd index b4ac65b1..9c64fa79 100644 --- a/man/safetyGraphicsInit.Rd +++ b/man/safetyGraphicsInit.Rd @@ -17,6 +17,10 @@ safetyGraphicsInit( \item{maxFileSize}{maximum file size in MB allowed for file upload} } +\value{ +No return value. Called for its side effect of running the initialisation +app, which in turn launches the main application. +} \description{ App to select charts, load data and then initialize the core safetyGraphics app } diff --git a/man/safetyGraphicsServer.Rd b/man/safetyGraphicsServer.Rd index 8aadddf9..3569dc0d 100644 --- a/man/safetyGraphicsServer.Rd +++ b/man/safetyGraphicsServer.Rd @@ -32,6 +32,12 @@ safetyGraphicsServer( \item{charts}{list of charts to include in the app} \item{filterDomain}{domain used for the data/filter tab. Demographics ("\code{dm}") is used by default. Using a domain that is not one record per participant is not recommended.} + +\item{config}{named list of app configuration options as returned by \code{app_startup()}, passed through to the modules that need it.} +} +\value{ +No return value. Called for its side effect of starting the application's +home, mapping, filter, chart and settings modules. } \description{ This function returns a server function suitable for use in shiny::runApp() diff --git a/man/safetyGraphicsUI.Rd b/man/safetyGraphicsUI.Rd index 19bdbd6a..7dfa5676 100644 --- a/man/safetyGraphicsUI.Rd +++ b/man/safetyGraphicsUI.Rd @@ -18,6 +18,11 @@ safetyGraphicsUI(id, meta, mapping, domainData, charts, standards, config) \item{charts}{list of charts in the format produced by safetyGraphics::makeChartConfig()} \item{standards}{a list of information regarding data standards. Each list item should use the format returned by safetyGraphics::detectStandard.} + +\item{config}{named list of app configuration options as returned by \code{app_startup()}, passed through to the modules that need it.} +} +\value{ +The Shiny UI for the whole application. } \description{ UI for the core safetyGraphics app including Home, Mapping, Filter, Charts and Settings modules. diff --git a/man/setMappingListValue.Rd b/man/setMappingListValue.Rd index 8b4942ea..25c3d92c 100644 --- a/man/setMappingListValue.Rd +++ b/man/setMappingListValue.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/setMappingListValue.R +% Please edit documentation in R/util-setMappingListValue.R \name{setMappingListValue} \alias{setMappingListValue} \title{Set the value for a given named parameter} diff --git a/man/settingsCharts.Rd b/man/settingsCharts.Rd index f9ed6d3f..5e7fcec7 100644 --- a/man/settingsCharts.Rd +++ b/man/settingsCharts.Rd @@ -15,6 +15,10 @@ settingsCharts(input, output, session, charts) \item{charts}{list data frame summarizing the charts} } +\value{ +No return value. Called for its side effect of rendering the chart +configuration as YAML. +} \description{ Server for settings tab showing details for the charts loaded in the app } diff --git a/man/settingsChartsUI.Rd b/man/settingsChartsUI.Rd index ff68aac0..23e75a98 100644 --- a/man/settingsChartsUI.Rd +++ b/man/settingsChartsUI.Rd @@ -9,6 +9,9 @@ settingsChartsUI(id) \arguments{ \item{id}{module id} } +\value{ +The Shiny UI for the chart settings panel. +} \description{ UI for settings tab showing details for the charts loaded in the app } diff --git a/man/settingsCode.Rd b/man/settingsCode.Rd index 960f3465..db423b99 100644 --- a/man/settingsCode.Rd +++ b/man/settingsCode.Rd @@ -19,6 +19,10 @@ settingsCode(input, output, session, mapping, charts, domainData) \item{domainData}{data list} } +\value{ +No return value. Called for its side effect of rendering the restart code +and registering the app download handler. +} \description{ Server for settings tab providing code to re-start the app with current data/settings } diff --git a/man/settingsCodeUI.Rd b/man/settingsCodeUI.Rd index 7b54f1bc..1f3d62cd 100644 --- a/man/settingsCodeUI.Rd +++ b/man/settingsCodeUI.Rd @@ -9,6 +9,9 @@ settingsCodeUI(id) \arguments{ \item{id}{module ID} } +\value{ +The Shiny UI for the code settings panel. +} \description{ UI for settings tab providing code to re-start the app with current data/settings } diff --git a/man/settingsData.Rd b/man/settingsData.Rd index 745b953a..33fa3f67 100644 --- a/man/settingsData.Rd +++ b/man/settingsData.Rd @@ -15,6 +15,10 @@ settingsData(input, output, session, domains) \item{domains}{named list of the data.frames for each domain} } +\value{ +No return value. Called for its side effect of rendering a preview table +for each data domain. +} \description{ Server for settings tab showing current data } diff --git a/man/settingsDataUI.Rd b/man/settingsDataUI.Rd index 5a814d50..bf6a1274 100644 --- a/man/settingsDataUI.Rd +++ b/man/settingsDataUI.Rd @@ -9,6 +9,9 @@ settingsDataUI(id) \arguments{ \item{id}{module id} } +\value{ +The Shiny UI for the data settings panel. +} \description{ UI for settings tab showing current data } diff --git a/man/settingsMapping.Rd b/man/settingsMapping.Rd index bff64b23..838749b8 100644 --- a/man/settingsMapping.Rd +++ b/man/settingsMapping.Rd @@ -17,6 +17,10 @@ settingsMapping(input, output, session, metadata, mapping) \item{mapping}{reactive data frame representing the current metadata mapping. columns = "domain", "text_id" and "current"} } +\value{ +No return value. Called for its side effect of rendering the mapping and +metadata tables. +} \description{ Server for settings tab showing current mapping } diff --git a/man/settingsMappingUI.Rd b/man/settingsMappingUI.Rd index 3ee17585..38a64efa 100644 --- a/man/settingsMappingUI.Rd +++ b/man/settingsMappingUI.Rd @@ -9,6 +9,9 @@ settingsMappingUI(id) \arguments{ \item{id}{module id} } +\value{ +The Shiny UI for the mapping settings panel. +} \description{ UI for settings tab showing current mapping } diff --git a/man/settingsTab.Rd b/man/settingsTab.Rd index 79875ab4..9fb5a2b4 100644 --- a/man/settingsTab.Rd +++ b/man/settingsTab.Rd @@ -21,6 +21,10 @@ settingsTab(input, output, session, domains, metadata, mapping, charts) \item{charts}{charts} } +\value{ +No return value. Called for its side effect of starting the mapping, +data, chart and code settings modules. +} \description{ Server for the setting page } diff --git a/man/settingsTabUI.Rd b/man/settingsTabUI.Rd index 33df0d70..a8a7eb93 100644 --- a/man/settingsTabUI.Rd +++ b/man/settingsTabUI.Rd @@ -9,6 +9,9 @@ settingsTabUI(id) \arguments{ \item{id}{module ID} } +\value{ +A \code{shiny::tabsetPanel()} containing the four settings panels. +} \description{ UI for the settings tab } diff --git a/man/textKeysToList.Rd b/man/textKeysToList.Rd index 9a11e91a..933d6a71 100644 --- a/man/textKeysToList.Rd +++ b/man/textKeysToList.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/textKeysToList.R +% Please edit documentation in R/util-textKeysToList.R \name{textKeysToList} \alias{textKeysToList} \title{Helper function to convert keys from text to nested lists} diff --git a/vignettes/TechnicalFAQ.Rmd b/vignettes/TechnicalFAQ.Rmd index bbb32a02..4ad6d984 100644 --- a/vignettes/TechnicalFAQ.Rmd +++ b/vignettes/TechnicalFAQ.Rmd @@ -28,7 +28,7 @@ Whenever new questions come in, we'll update the version of [this FAQ in our wik # Validation, Quality Control and Testing ## Q: Is the safetyGraphics package validated? -**A**: As of the version 2 release, the safetyGraphics package is intended for exploratory use only and is not validated or qualified per [21 CFR Part 11](https://www.fda.gov/regulatory-information/search-fda-guidance-documents/part-11-electronic-records-electronic-signatures-scope-and-application). No warranty or guarantees are included as part of the package. Further, any formal validation should be fit for purpose and follow your organization's procedures. That said, extensive quality checks are built in to the package (see the question below for details) and into many of charts that are included by default. The R Consortium has [guidance on usage of R in Regulated Trials](https://www.r-project.org/doc/R-FDA.pdf) and we also follow the work of [R Validation hub](https://www.pharmar.org/) closely, and may release validation guidance based on the approach described in their [white paper](https://www.pharmar.org/white-paper/) at a future date. +**A**: As of the version 2 release, the safetyGraphics package is intended for exploratory use only and is not validated or qualified per [21 CFR Part 11](https://www.fda.gov/regulatory-information/search-fda-guidance-documents/part-11-electronic-records-electronic-signatures-scope-and-application). No warranty or guarantees are included as part of the package. Further, any formal validation should be fit for purpose and follow your organization's procedures. That said, extensive quality checks are built in to the package (see the question below for details) and into many of charts that are included by default. The R Consortium has [guidance on usage of R in Regulated Trials](https://www.r-project.org/doc/R-FDA.pdf) and we also follow the work of [R Validation hub](https://pharmar.org/) closely, and may release validation guidance based on the approach described in their [white paper](https://pharmar.org/white-paper/) at a future date. ## Q: Can I validate charts created by safetyGraphics? **A**: Study-specific instances of most safetyGraphics charts can be exported either as an R script or as a standalone html report. It may be possible to treat those outputs as standard TLFs (Tables, Listings and Figures) and conduct QC/Validation on them using standard statistical SOPs. Consult with your companies procedures to confirm. @@ -57,13 +57,13 @@ That said, the issues surrounding a 'production' deployment are mostly technical Many of these issues aren't specific to safetyGraphics and may be easier to address for an organization that has experience using R and Shiny in production. [As discussed above](#q-is-the-safetygraphics-package-validated), there is a significant push towards using R for many aspects of clinical trials. We plan to keep safetyGraphics up to date with emerging best practices and will provide supporting documentation whenever possible. -Finally, it is worth noting "Productionize” and “Validate” are slightly different. Joe Cheng (the primary author of Shiny) has a [nice talk](https://www.rstudio.com/resources/rstudioconf-2019/shiny-in-production-principles-practices-and-tools/) on this topic from a software engineering perspective. +Finally, it is worth noting "Productionize” and “Validate” are slightly different. Joe Cheng (the primary author of Shiny) has a [nice talk](https://posit.co/resources/videos/shiny-in-production-principles-practices-and-tools) on this topic from a software engineering perspective. # Use Cases, Data Sources and Deployment ## Q: I can see that the software has the MIT license, so does this essentially mean that any company would be free to use the software without restriction? -**A**: Yes. The package is open source and free to use. Here's a link to the [license](https://github.com/SafetyGraphics/safetyGraphics/blob/dev/LICENSE.md) and a quick [summary](https://tldrlegal.com/license/mit-license) of how it works. +**A**: Yes. The package is open source and free to use. Here's a link to the [license](https://github.com/SafetyGraphics/safetyGraphics/blob/dev/LICENSE.md) and a quick [summary](https://www.tldrlegal.com/license/mit-license) of how it works. ## Q: What are some common use cases for safetyGraphics? @@ -73,7 +73,7 @@ Finally, it is worth noting "Productionize” and “Validate” are slightly di - Analysts and Clinicians evaluating safety signals in completed studies As an open source tool with a flexible data pipeline, many other use cases have been discussed: -- Data review by Data Safety Monitoring Boards ([link](https://www.ncbi.nlm.nih.gov/pmc/articles/PMC7833551/pdf/main.pdf)) +- Data review by Data Safety Monitoring Boards ([link](https://pmc.ncbi.nlm.nih.gov/articles/PMC7833551/pdf/main.pdf)) - Visualizing Analysis results data ([link](https://github.com/phuse-org/aesummaries)) - Risk based monitoring @@ -95,22 +95,22 @@ More detail is provided in the [Loading data section of the Introductory vignett Since the safetyGraphics app typically uses data subject to GxP regulations, data security is extremely important and should always be discussed with your organizations IT and regulatory departments before loading any study data in to the application. No warranty or guarantees are included as part of the package. -Just like the discussion regarding [Validation and Quality Control](#validation-quality-control-and-testing), data security requirements are dictated by the intended use of the app and should be fit for purpose. There are many different ways to run shiny applications, and the security implications of each approach varies. For example, having statisticians run the app locally using R Studio is quite different than deploying the app on a service like shinyapps.io. This complexity is all the more reason to discuss with IT. There are many resources - related to data security for clinical trials in general ([1](https://www.hhs.gov/ohrp/sites/default/files/report-privacy-and-health-10-31-19.pdf), [2](https://www.ncbi.nlm.nih.gov/pmc/articles/PMC4943495/), [3](https://www.d-wise.com/blog/detailing-the-de-identification-and-data-transparency-boundaries-of-ema-policy-70-and-gdpr)) and discussing data security in shiny ([4](https://www.youtube.com/watch?v=5odJxZj9LE4), +Just like the discussion regarding [Validation and Quality Control](#validation-quality-control-and-testing), data security requirements are dictated by the intended use of the app and should be fit for purpose. There are many different ways to run shiny applications, and the security implications of each approach varies. For example, having statisticians run the app locally using R Studio is quite different than deploying the app on a service like shinyapps.io. This complexity is all the more reason to discuss with IT. There are many resources - related to data security for clinical trials in general ([1](https://www.hhs.gov/ohrp/sites/default/files/report-privacy-and-health-10-31-19.pdf), [2](https://pmc.ncbi.nlm.nih.gov/articles/PMC4943495/), [3](https://web.archive.org/web/20250512155605/https://www.d-wise.com/blog/detailing-the-de-identification-and-data-transparency-boundaries-of-ema-policy-70-and-gdpr)) and discussing data security in shiny ([4](https://www.youtube.com/watch?v=5odJxZj9LE4), [5](https://mastering-shiny.org/scaling-security.html#data)) - that could help to facilitate these discussion. ## Q: How can the safetyGraphics app be shared? -**A**: The safetyGraphics app can be shared using [standard shiny methodology](https://shiny.rstudio.com/tutorial/written-tutorial/lesson7/). More details for specific use cases are given in the next few questions. Charts created by safetyGraphics can also be exported and re-used. Charts created with [htmlwidgets](https://www.htmlwidgets.org/) are especially flexible and can be used in many contexts - including in web applications outside of R. +**A**: The safetyGraphics app can be shared using [standard shiny methodology](https://shiny.posit.co/r/getstarted/shiny-basics/lesson7/). More details for specific use cases are given in the next few questions. Charts created by safetyGraphics can also be exported and re-used. Charts created with [htmlwidgets](https://www.htmlwidgets.org/) are especially flexible and can be used in many contexts - including in web applications outside of R. ## Q: Do you recommend deploying the app to a dedicated server for internal usage? -**A**: It depends a bit on your use-case and how the app is hosted. For example, analysts using the data in an exploratory fashion can probably just run it from RStudio, but if multiple medical monitors using the app for medical monitoring in active studies probably need a more robust (and possibly validated) set up using [Shiny Server](https://github.com/rstudio/shiny-server/blob/master/README.md), [RStudio Connect](https://posit.co/products/enterprise/connect/) or something similar. +**A**: It depends a bit on your use-case and how the app is hosted. For example, analysts using the data in an exploratory fashion can probably just run it from RStudio, but if multiple medical monitors using the app for medical monitoring in active studies probably need a more robust (and possibly validated) set up using [Shiny Server](https://github.com/rstudio/shiny-server/blob/master/README.md), [RStudio Connect](https://posit.co/products/enterprise/connect) or something similar. ## Q: Can I deploy `safetyGraphics` to shinyapps.io to explore trial data from my organization? **A**: We advise against loading non-authorized, private, or non-deidentified patient data outside of your organization's firewall. Consult with your IT and QA first. There is huge risk associated with confidentiality, IP, and patient privacy. Also refer to [ShinyApps.io Chapter 8 Security and Compliance](https://docs.posit.co/shinyapps.io/security-and-compliance.html). -## **Q**: Can I deploy `safetyGraphics` to an internal [RStudio Connect](https://posit.co/products/enterprise/connect/) server? +## **Q**: Can I deploy `safetyGraphics` to an internal [RStudio Connect](https://posit.co/products/enterprise/connect) server? **A**: Yes. The [script below](https://github.com/SafetyGraphics/safetyGraphics/blob/dev/inst/deploy/app.R) should be easy to deploy via the RStudio interface or by running `rsconnect::deployApp()` and can easily be customized to support custom data and charts.