diff --git a/DESCRIPTION b/DESCRIPTION index aabef0f..3e67c5a 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -48,6 +48,7 @@ Suggests: knitr, RefManageR, rmarkdown, + scRNAseq, sessioninfo, testthat (>= 3.0.0), yaml diff --git a/R/iSEEindex.R b/R/iSEEindex.R index 6a3d95d..edaba54 100644 --- a/R/iSEEindex.R +++ b/R/iSEEindex.R @@ -6,15 +6,19 @@ #' states prepared by the app maintainer. #' #' @section Data Sets: -#' The function passed to the argument `FUN.datasets` must return a `list` that contains metadata about the available data sets. +#' The function passed to the argument `FUN.datasets` must return a `list` that +#' contains metadata about the available data sets. #' #' For each data set, required metadata are: #' #' \describe{ #' \item{id}{A unique identifier for the data set.} -#' \item{title}{A short human-readable title for the data set, displayed in the 'Info' panel when the data set is selected.} -#' \item{uri}{A Uniform Resource Identifier (URI) that indicates the location of the data file that contains the data set.} -#' \item{description}{A more detailed description of the data set, displayed in the 'Info' panel when the data set is selected.} +#' \item{title}{A short human-readable title for the data set, displayed in the +#' 'Info' panel when the data set is selected.} +#' \item{uri}{A Uniform Resource Identifier (URI) that indicates the location of +#' the data file that contains the data set.} +#' \item{description}{A more detailed description of the data set, displayed in +#' the 'Info' panel when the data set is selected.} #' } #' #' Example: @@ -22,40 +26,54 @@ #' ``` #' list( #' list( -#' id = "dataset01", -#' title = "Dataset 01", -#' uri = "https://example.com/1.rds", -#' description = "My first data set." +#' id = "dataset01", +#' title = "Dataset 01", +#' uri = "https://example.com/1.rds", +#' description = "My first data set." #' ), #' list( -#' id = "dataset02", -#' title = "Dataset 02", -#' uri = "https://example.com/1.rds", -#' description = "My second data set." +#' id = "dataset02", +#' title = "Dataset 02", +#' uri = "https://example.com/1.rds", +#' description = "My second data set." #' ) #' ) #' ``` #' -#' The individual sub-lists may also contain optional named metadata specific to individual [`iSEEindexResource-class`] classes (refer to the help page of those classes for details). +#' The individual sub-lists may also contain optional named metadata specific to +#' individual [`iSEEindexResource-class`] classes (refer to the help page of +#' those classes for details). #' -#' **Important**: The `id` value is used to identify the data set file in the \pkg{BiocFileCache}. -#' Thus, we recommend using a dedicated `BiocFileCache()` for the app, using the `BiocFileCache(cache)` argument to specify an on-disk location (directory path) for the dedicated cache. +#' **Important**: The `id` value is used to identify the data set file in the +#' \pkg{BiocFileCache}. +#' Thus, we recommend using a dedicated `BiocFileCache()` for the app, using the +#' `BiocFileCache(cache)` argument to specify an on-disk location (directory +#' path) for the dedicated cache. #' #' @section Initial Configurations: -#' The function passed to the argument `FUN.initial` must return a `list` that contains metadata about the available initial configurations, or `NULL` in the absence of any custom initial configuration (default settings will be applied to all data sets.). +#' The function passed to the argument `FUN.initial` must return a `list` that +#' contains metadata about the available initial configurations, or `NULL` in +#' the absence of any custom initial configuration (default settings will be +#' applied to all data sets.). #' #' For each initial configuration, required metadata are: #' #' \describe{ #' \item{id}{A unique identifier for the initial configuration.} -#' \item{title}{A short human-readable title for the initial configuration, representing the initial configuration in the 'Initial settings' dropdown menu.} -#' \item{uri}{A Uniform Resource Identifier (URI) that indicates the location of the R script that contains the initial configuration.} -#' \item{description}{A more detailed description of the initial configuration, displayed in the 'Configure and launch' panel when the initial configuration is selected.} +#' \item{title}{A short human-readable title for the initial configuration, +#' representing the initial configuration in the 'Initial settings' dropdown menu.} +#' \item{uri}{A Uniform Resource Identifier (URI) that indicates the location of +#' the R script that contains the initial configuration.} +#' \item{description}{A more detailed description of the initial configuration, +#' displayed in the 'Configure and launch' panel when the initial configuration +#' is selected.} #' } #' #' For each initial configuration, optional metadata are: #' \describe{ -#' \item{datasets}{A series of data set identifiers for which the configuration should be made available. If missing, the configuration will be available for all data sets.} +#' \item{datasets}{A series of data set identifiers for which the configuration +#' should be made available. If missing, the configuration will be available for +#' all data sets.} #' } #' #' Example: @@ -78,7 +96,9 @@ #' ) #' ``` #' -#' The individual sub-lists may also contain additional optional named metadata specific to individual [`iSEEindexResource-class`] classes (refer to the help page of those classes for details). +#' The individual sub-lists may also contain additional optional named metadata +#' specific to individual [`iSEEindexResource-class`] classes (refer to the help +#' page of those classes for details). #' #' @param bfc An [BiocFileCache()] object. #' @param FUN.datasets A function that returns a `list` of metadata for @@ -97,7 +117,8 @@ #' @param body.header UI element to display \emph{above} the main landing page body. #' @param body.footer UI element to display \emph{below} the main landing page body. #' -#' @return An [iSEE::iSEE()] app with a custom landing page using a [BiocFileCache()] to cache a selection of data sets. +#' @return An [iSEE::iSEE()] app with a custom landing page using a +#' [BiocFileCache()] to cache a selection of data sets. #' #' @author Kevin Rue-Albrecht #' @@ -107,17 +128,9 @@ #' @importFrom utils packageVersion #' #' @examples -#' ## -#' # BiocFileCache ---- -#' ## -#' -#' library(BiocFileCache) +#' library("BiocFileCache") #' bfc <- BiocFileCache(cache = tempdir()) #' -#' ## -#' # iSEEindex ---- -#' ## -#' #' dataset_fun <- function() { #' x <- yaml::read_yaml(system.file(package = "iSEEindex", "example.yaml")) #' x$datasets @@ -133,46 +146,18 @@ #' if (interactive()) { #' shiny::runApp(app, port = 1234) #' } -iSEEindex <- function(bfc, FUN.datasets, FUN.initial = NULL, default.add = TRUE, default.position = c("first", "last"), app.title = NULL, body.header = NULL, body.footer = NULL) { - stopifnot(is(bfc, "BiocFileCache")) - if (is.null(FUN.initial)) { - FUN.initial <- function() NULL - } - - if (is.null(app.title)) { - app.title <- sprintf("iSEEindex - v%s | powered by iSEE - v%s", - packageVersion("iSEEindex"), - packageVersion("iSEE")) - } - - iSEE( - landingPage=.landing_page(bfc, FUN.datasets, FUN.initial, default.add, default.position, body.header, body.footer), - appTitle = app.title - ) -} - -#' @examples -#' ## -#' # BiocFileCache ---- -#' ## #' -#' library(BiocFileCache) -#' bfc <- BiocFileCache(cache = tempdir()) -#' -#' ## -#' # iSEEindex ---- -#' ## +#' ## Alternatively, with the example based on using runr calls #' -#' dataset_fun <- function() { +#' dataset_fun_tonsils <- function() { #' x <- yaml::read_yaml( -#' system.file("tonsils_example", "tonsil_package.yml", package = "iSEEindex") +#' system.file("tonsils_example", "tonsil_package.yml", package = "iSEEindex") #' ) #' x$datasets #' } -#' -#' initial_fun <- function() { +#' initial_fun_tonsils <- function() { #' x <- yaml::read_yaml( -#' system.file("tonsils_example", "tonsil_package.yml", package = "iSEEindex") +#' system.file("tonsils_example", "tonsil_package.yml", package = "iSEEindex") #' ) #' x$initial #' } @@ -180,16 +165,13 @@ iSEEindex <- function(bfc, FUN.datasets, FUN.initial = NULL, default.add = TRUE, #' library("shiny") #' header_tonsils <- fluidRow( #' shinydashboard::box( -#' width = 12, -#' collapsible = TRUE, -#' collapsed = TRUE, +#' width = 12, collapsible = TRUE, collapsed = TRUE, #' title = "How to explore the Tonsil Atlas datasets", #' includeMarkdown( #' system.file("tonsils_example", "header_tonsils.md", package = "iSEEindex") #' ) #' ) #' ) -#' #' footer_tonsils <- fluidRow( #' shinydashboard::box( #' width = 12, @@ -199,48 +181,52 @@ iSEEindex <- function(bfc, FUN.datasets, FUN.initial = NULL, default.add = TRUE, #' ) #' ) #' -#' app <- iSEEindex_runr(bfc, dataset_fun, initial_fun, -#' default.add = TRUE, -#' default.position = "last", -#' app.title = "iSEE ❤️ Tonsil Data Atlas", -#' body.header = header_tonsils, -#' body.footer = footer_tonsils) +#' app_tonsils <- iSEEindex(bfc, +#' dataset_fun_tonsils, +#' initial_fun_tonsils, +#' default.add = TRUE, +#' default.position = "last", +#' app.title = "iSEE ❤️ Tonsil Data Atlas", +#' body.header = header_tonsils, +#' body.footer = footer_tonsils) #' #' if (interactive()) { -#' shiny::runApp(app, port = 1234) +#' shiny::runApp(app_tonsils, port = 5678) #' } -iSEEindex_runr <- function(bfc, - FUN.datasets, - FUN.initial = NULL, - default.add = TRUE, default.position = c("first", "last"), - app.title = NULL, - body.header = NULL, - body.footer = NULL) { - stopifnot(is(bfc, "BiocFileCache")) - if (is.null(FUN.initial)) { - FUN.initial <- function() NULL - } +iSEEindex <- function(bfc, + FUN.datasets, + FUN.initial = NULL, + default.add = TRUE, + default.position = c("first", "last"), + app.title = NULL, + body.header = NULL, + body.footer = NULL) { + stopifnot(is(bfc, "BiocFileCache")) + if (is.null(FUN.initial)) { + FUN.initial <- function() NULL + } - if (is.null(app.title)) { - app.title <- sprintf("iSEEindex - v%s", - packageVersion("iSEEindex")) - } else { - app.title <- app.title - } + if (is.null(app.title)) { + app.title <- sprintf("iSEEindex - v%s | powered by iSEE - v%s", + packageVersion("iSEEindex"), + packageVersion("iSEE")) + } - iSEE( - landingPage=.landing_page_runr(bfc, - FUN.datasets, - FUN.initial, - default.add, default.position, - body.header, - body.footer), - appTitle = app.title - ) + iSEE( + landingPage = .landing_page( + bfc, + FUN.datasets, + FUN.initial, + default.add, + default.position, + body.header, + body.footer + ), + appTitle = app.title + ) } - #' Prepare and Launch the Main App. #' #' Invokes a function that replaces the landing page by the \pkg{iSEE} @@ -258,7 +244,7 @@ iSEEindex_runr <- function(bfc, #' #' @param FUN A function to initialize the \pkg{iSEE} observer #' architecture. Refer to [iSEE::createLandingPage()] for more details. -#' @param bfc An [BiocFileCache()] object. +#' @param bfc A [BiocFileCache()] object. #' @param session The Shiny session object from the server function. #' @param pObjects An environment containing global parameters generated in the #' landing page. @@ -333,75 +319,3 @@ iSEEindex_runr <- function(bfc, # nocov end } - - - - - -.launch_isee_runr <- function(FUN, bfc, session, pObjects) { - # nocov start - dataset_id <- pObjects[[.dataset_selected_id]] - which_dataset <- which(pObjects$datasets_table[[.datasets_id]] == dataset_id) - # TODO: refactor as function that takes data set identifier and returns uri - dataset_metadata <- as.list(pObjects$datasets_table[which_dataset, , drop=FALSE]) - # TODO: refactor as function that takes data set identifier and returns title - dataset_title <- pObjects$datasets_table[which_dataset, .datasets_title, drop=TRUE] - withProgress(message = sprintf("Loading '%s'", dataset_title), - value = 0, max = 2, { - incProgress(1, detail = "(Down)loading object") - - - # se2 <- try(.load_sce_runr(bfc, dataset_id, dataset_metadata)) - se2 <- try(.load_sce_runr(bfc, dataset_id, dataset_metadata)) - - - - - - incProgress(1, detail = "Launching iSEE app") - if (is(se2, "try-error")) { - showNotification("Invalid SummarizedExperiment supplied.", type="error") - } else { - if (is.null(pObjects$initial_table)) { - initial <- NULL - tour <- NULL - } else { - initial_id <- pObjects[[.ui_initial]] - which_initial <- which( - pObjects$initial_table[[.initial_config_id]] == initial_id & - pObjects$initial_table[[.initial_datasets_id]] == dataset_id - ) - initial_metadata <- as.list(pObjects$initial_table[which_initial, , drop = FALSE]) - initial_message <- capture.output( - init <- try(.parse_initial(bfc, dataset_id, initial_id, initial_metadata)), - type = "message") - initial <- init$initial - tour <- init$tour - } - if (is(init, "try-error")) { - showModal(modalDialog( - title = "Invalid initial state", - p("An error occured while evaluating the script:"), - markdown(paste0(c("```", initial_message, "```"), collapse = "\n")), - p("Contact the app maintainer for further help."), - footer = NULL, - size = "l", - easyClose = TRUE - )) - return(NULL) - } - FUN(SE=se2, INITIAL=initial, TOUR=tour) - shinyjs::enable(iSEE:::.generalOrganizePanels) # organize panels - shinyjs::enable(iSEE:::.generalLinkGraph) # link graph - shinyjs::enable(iSEE:::.generalExportOutput) # export content - shinyjs::enable(iSEE:::.generalCodeTracker) # tracked code - shinyjs::enable(iSEE:::.generalPanelSettings) # panel settings - shinyjs::enable(iSEE:::.generalVignetteOpen) # open vignette - shinyjs::enable(iSEE:::.generalSessionInfo) # session info - shinyjs::enable(iSEE:::.generalCitationInfo) # citation info - } - }, session = session) - - invisible(NULL) - # nocov end -} diff --git a/R/iSEEindexResource-class.R b/R/iSEEindexResource-class.R index 9ac6559..4371437 100644 --- a/R/iSEEindexResource-class.R +++ b/R/iSEEindexResource-class.R @@ -2,7 +2,8 @@ #' The iSEEindexResource class #' -#' The iSEEindexResource class is a virtual class from which classes of supported resource must be derived. +#' The iSEEindexResource class is a virtual class from which classes of +#' supported resource must be derived. #' #' @section Slot overview: #' \itemize{ @@ -10,11 +11,14 @@ #' } #' #' @section Supported methods: -#' In the following code snippets, \code{x} is an instance of a [`iSEEindexResource-class`] class. -#' Refer to the documentation for each method for more details on the remaining arguments. +#' In the following code snippets, \code{x} is an instance of a +#' [`iSEEindexResource-class`] class. +#' Refer to the documentation for each method for more details on the remaining +#' arguments. #' #' \itemize{ -#' \item \code{\link{precache}(x, bfc, id, ...)} throws an error, encouraging users to develop a method for derived classes that are not supported yet. +#' \item \code{\link{precache}(x, bfc, id, ...)} throws an error, encouraging +#' users to develop a method for derived classes that are not supported yet. #' } #' #' @author Kevin Rue-Albrecht @@ -58,7 +62,8 @@ setMethod("show", "iSEEindexResource", #' @param ... additional arguments passed to and from other methods. #' #' @section Preparing and caching resources: -#' `precache(x, bfc, id, ...)` retrieves and caches a resource from an URI, caches it, and returns the path to the cached file. +#' `precache(x, bfc, id, ...)` retrieves and caches a resource from an URI, +#' caches it, and returns the path to the cached file. #' #' @author Kevin Rue-Albrecht #' @@ -79,7 +84,8 @@ NULL #' @export #' @rdname iSEEindexResource-generics #' -#' @return `precache()` returns the file path to the cached copy of a resource fetched from a given URI. +#' @return `precache()` returns the file path to the cached copy of a resource +#' fetched from a given URI. setGeneric("precache", function(x, bfc, id, ...) { stopifnot(is(x, "iSEEindexResource"), length(x) == 1L) standardGeneric("precache") @@ -119,14 +125,19 @@ setMethod("precache", "iSEEindexResource", #' } #' #' @section Slot overview: -#' This class inherits all slots from its parent class \linkS4class{iSEEindexResource}. +#' This class inherits all slots from its parent class +#' \linkS4class{iSEEindexResource}. #' #' @section Supported methods: -#' In the following code snippets, \code{x} is an instance of a \linkS4class{iSEEindexHttpsResource} class. -#' Refer to the documentation for each method for more details on the remaining arguments. +#' In the following code snippets, \code{x} is an instance of a +#' \linkS4class{iSEEindexHttpsResource} class. +#' Refer to the documentation for each method for more details on the remaining +#' arguments. #' #' \itemize{ -#' \item \code{\link{precache}(x, bfc, id, ...)} caches the resource located at the given URI using \pkg{BiocFileCache} and returns the file path to the cached file. +#' \item \code{\link{precache}(x, bfc, id, ...)} caches the resource located at +#' the given URI using \pkg{BiocFileCache} and returns the file path to the +#' cached file. #' } #' #' @author Kevin Rue-Albrecht @@ -148,7 +159,8 @@ setClass("iSEEindexHttpsResource", contains="iSEEindexResource") #' #' @param x List of metadata. See Details. #' -#' @return The constructor function `iSEEindexHttpsResource()` returns an object of object of class `iSEEindexHttpsResource`. +#' @return The constructor function `iSEEindexHttpsResource()` returns an object +#' of class `iSEEindexHttpsResource`. iSEEindexHttpsResource <- function(x) { new("iSEEindexHttpsResource", uri = x[[.datasets_uri]]) } @@ -183,11 +195,15 @@ setMethod("precache", "iSEEindexHttpsResource", #' This class inherits all slots from its parent class \linkS4class{iSEEindexResource}. #' #' @section Supported methods: -#' In the following code snippets, \code{x} is an instance of a \linkS4class{iSEEindexLocalhostResource} class. -#' Refer to the documentation for each method for more details on the remaining arguments. +#' In the following code snippets, \code{x} is an instance of a +#' \linkS4class{iSEEindexLocalhostResource} class. +#' Refer to the documentation for each method for more details on the remaining +#' arguments. #' #' \itemize{ -#' \item \code{\link{precache}(x, ...)} trims the `localhost://` prefix, and caches a copy of the resource located at the resulting file path using \pkg{BiocFileCache}, before returning the file path to the cached file. +#' \item \code{\link{precache}(x, ...)} trims the `localhost://` prefix, and +#' caches a copy of the resource located at the resulting file path using +#' \pkg{BiocFileCache}, before returning the file path to the cached file. #' } #' #' @section Absolute and relative paths: @@ -195,7 +211,8 @@ setMethod("precache", "iSEEindexHttpsResource", #' Absolute and relative paths are both supported. #' #' Absolute paths require an additional `/` (forward slash) -#' following the double forward slash `//` separating the scheme component of the URI. +#' following the double forward slash `//` separating the scheme component of +#' the URI. #' #' For instance: #' @@ -225,7 +242,8 @@ setClass("iSEEindexLocalhostResource", contains="iSEEindexResource") #' #' @param x List of metadata. See Details. #' -#' @return The constructor function `iSEEindexLocalhostResource()` returns an object of object of class `iSEEindexLocalhostResource`. +#' @return The constructor function `iSEEindexLocalhostResource()` returns an +#' object of object of class `iSEEindexLocalhostResource`. iSEEindexLocalhostResource <- function(x) { new("iSEEindexLocalhostResource", uri = x[[.datasets_uri]]) } @@ -255,7 +273,8 @@ setMethod("precache", "iSEEindexLocalhostResource", #' Required metadata: #' #' \describe{ -#' \item{uri}{Character scalar. R call which, once evaluated, produces a character scalar that is the URI of the resource.} +#' \item{uri}{Character scalar. R call which, once evaluated, produces a +#' character scalar that is the URI of the resource.} #' } #' #' @section URI format: @@ -269,11 +288,14 @@ setMethod("precache", "iSEEindexLocalhostResource", #' ``` #' #' @section Slot overview: -#' This class inherits all slots from its parent class \linkS4class{iSEEindexResource}. +#' This class inherits all slots from its parent class +#' \linkS4class{iSEEindexResource}. #' #' @section Supported methods: -#' In the following code snippets, \code{x} is an instance of a \linkS4class{iSEEindexRcallResource} class. -#' Refer to the documentation for each method for more details on the remaining arguments. +#' In the following code snippets, \code{x} is an instance of a +#' \linkS4class{iSEEindexRcallResource} class. +#' Refer to the documentation for each method for more details on the remaining +#' arguments. #' #' \itemize{ #' \item \code{\link{precache}(x, ...)} trims the `rcall://` prefix, @@ -303,7 +325,8 @@ setClass("iSEEindexRcallResource", contains="iSEEindexResource") #' #' @param x List of metadata. See Details. #' -#' @return The constructor function `iSEEindexRcallResource()` returns an object of object of class `iSEEindexRcallResource`. +#' @return The constructor function `iSEEindexRcallResource()` returns an object +#' of object of class `iSEEindexRcallResource`. iSEEindexRcallResource <- function(x) { new("iSEEindexRcallResource", uri = x[[.datasets_uri]]) } @@ -325,7 +348,6 @@ setMethod("precache", "iSEEindexRcallResource", }) - # iSEEindexRunrResource ---- #' The iSEEindexRunrResource class @@ -343,11 +365,15 @@ setMethod("precache", "iSEEindexRcallResource", #' } #' #' @section Supported methods: -#' In the following code snippets, \code{x} is an instance of a \linkS4class{iSEEindexRunrResource} class. -#' Refer to the documentation for each method for more details on the remaining arguments. +#' In the following code snippets, \code{x} is an instance of a +#' \linkS4class{iSEEindexRunrResource} class. +#' Refer to the documentation for each method for more details on the remaining +#' arguments. #' #' \itemize{ -#' \item \code{\link{precache}(x, bfc, id, ...)} caches the resource located at the given URI using \pkg{BiocFileCache} and returns the file path to the cached file. +#' \item \code{\link{precache}(x, bfc, id, ...)} caches the resource located at +#' the given URI using \pkg{BiocFileCache} and returns the file path to the +#' cached file. #' } #' #' @name iSEEindexRunrResource-class @@ -369,45 +395,29 @@ setClass("iSEEindexRunrResource", contains="iSEEindexResource") #' #' @param x List of metadata. See Details. #' -#' @return The constructor function `iSEEindexRunrResource()` returns an object of object of class `iSEEindexRunrResource`. +#' @return The constructor function `iSEEindexRunrResource()` returns an object +#' of object of class `iSEEindexRunrResource`. iSEEindexRunrResource <- function(x) { new("iSEEindexRunrResource", uri = x[[.datasets_uri]]) } #' @export setMethod("precache", "iSEEindexRunrResource", - function(x, bfc, id, ...) - { - # Trim 'runr://' from the original URI and evaluate the R call, - # check that the value is an existing filepath and pass to BiocFileCache, - # which will manage the caching. - # Use action="copy" to leave the original file untouched. - call_string <- sub("runr://", "", x@uri) - env <- new.env() - - # fpath not needed per se, we should have a "valid r call and that is it" - - object_path <- eval(parse(text = call_string)) - - - # fpath <- eval(parse(text = call_string), envir = env) - # stopifnot(file.exists(fpath)) - # object_path <- bfcadd(x = bfc, rname = id, fpath = fpath, action = "copy", ...) - - - - - return(object_path) - }) - - - - - - + function(x, bfc, id, ...) +{ + # Trim 'runr://' from the original URI and evaluate the R call, + # We expect already that an SE object will be returned + call_string <- sub("runr://", "", x@uri) + # fpath not needed per se, we should have a "valid r call and that is it" + # this time it is called object, as it is already returning that + object <- eval(parse(text = call_string)) + ## "we have to believe" that this is already somehow cached e.g. via + ## Bioc data packages using the cache, and many times it is so + return(object) +}) @@ -429,7 +439,8 @@ setMethod("precache", "iSEEindexRunrResource", #' } #' #' @section Slot overview: -#' This class inherits all slots from its parent class \linkS4class{iSEEindexResource}. +#' This class inherits all slots from its parent class +#' \linkS4class{iSEEindexResource}. #' #' Furthermore, this class defines the additional slot(s) below: #' \describe{ @@ -437,12 +448,14 @@ setMethod("precache", "iSEEindexRunrResource", #' } #' #' @section Supported methods: -#' In the following code snippets, \code{x} is an instance of a \linkS4class{iSEEindexS3Resource} class. -#' Refer to the documentation for each method for more details on the remaining arguments. +#' In the following code snippets, \code{x} is an instance of a +#' \linkS4class{iSEEindexS3Resource} class. +#' Refer to the documentation for each method for more details on the remaining +#' arguments. #' #' \itemize{ -#' \item \code{\link{precache}(x, ..., temp_dir = tempdir())} trims the `s3://` prefix, -#' parses information encoded in the remainder of the URI, +#' \item \code{\link{precache}(x, ..., temp_dir = tempdir())} trims the `s3://` +#' prefix, parses information encoded in the remainder of the URI, #' downloads the resource from AWS S3 using that information, #' and caches a copy of the resource located at the resulting file path using #' \pkg{BiocFileCache}, before returning the file path to the cached file. @@ -473,7 +486,8 @@ setMethod("precache", "iSEEindexRunrResource", #' For detailed information, please consult the #' [paws R package documentation](https://github.com/paws-r/paws/blob/main/docs/credentials.md). #' -#' Currently, you must have the [AWS Command Line Interface](https://aws.amazon.com/cli/) installed to use AWS SSO with \pkg{paws.storage}. +#' Currently, you must have the [AWS Command Line Interface](https://aws.amazon.com/cli/) +#' installed to use AWS SSO with \pkg{paws.storage}. #' #' A default AWS region can be set in the file `~/.aws/config`. #' For instance: @@ -483,7 +497,8 @@ setMethod("precache", "iSEEindexRunrResource", #' region=eu-west-2 #' ``` #' -#' Optionally, a field named `region` can be added in the list of resource metadata to set the AWS S3 region for each individual resource, e.g. +#' Optionally, a field named `region` can be added in the list of resource +#' metadata to set the AWS S3 region for each individual resource, e.g. #' #' ``` #' - id: ID1 @@ -494,12 +509,16 @@ setMethod("precache", "iSEEindexRunrResource", #' region: eu-west-2 #' ``` #' -#' Regions set in individual resource metadata override the default AWS region set in `~/.aws/config` (if any). -#' The region metadata does not need to be set for resources that should use the default region, and resource classes that do not require region information. +#' Regions set in individual resource metadata override the default AWS region +#' set in `~/.aws/config` (if any). +#' The region metadata does not need to be set for resources that should use the +#' default region, and resource classes that do not require region information. #' -#' If a default region is NOT set in `~/.aws/config`, then the region MUST be set in the metadata. +#' If a default region is NOT set in `~/.aws/config`, then the region MUST be +#' set in the metadata. #' -#' Credentials for all services can be set in the AWS shared credentials file `~/.aws/credentials`. +#' Credentials for all services can be set in the AWS shared credentials file +#' `~/.aws/credentials`. #' For instance: #' #' ``` @@ -537,7 +556,8 @@ setClass("iSEEindexS3Resource", contains="iSEEindexResource", slots = c("region" #' #' @param x List of metadata. See Details. #' -#' @return The constructor function `iSEEindexS3Resource()` returns an object of object of class `iSEEindexS3Resource`. +#' @return The constructor function `iSEEindexS3Resource()` returns an object of +#' object of class `iSEEindexS3Resource`. iSEEindexS3Resource <- function(x) { region <- x[[.dataset_region]] if (is.null(region) || identical(nchar(region), 0L)) { diff --git a/R/landing_page.R b/R/landing_page.R index 236261e..de2b95b 100644 --- a/R/landing_page.R +++ b/R/landing_page.R @@ -1,10 +1,13 @@ #' Landing page function #' #' @param bfc A [BiocFileCache()] object. -#' @param FUN.datasets A function that returns a `data.frame` of metadata for available data sets. -#' @param FUN.initial A function that returns a `data.frame` of metadata for initial configuration states. +#' @param FUN.datasets A function that returns a `data.frame` of metadata for +#' available data sets. +#' @param FUN.initial A function that returns a `data.frame` of metadata for +#' initial configuration states. #' @param default.add Logical scalar indicating whether a default -#' initial configuration should be added as a choice in the Shiny `selectizeInput()`. +#' initial configuration should be added as a choice in the Shiny +#' `selectizeInput()`. #' See [iSEEindex()]. #' @param default.position Character scalar indicating whether the default #' initial configuration should be added as the `"first"` or `"last"` option @@ -24,7 +27,13 @@ #' @importFrom shinyjs disable #' #' @rdname INTERNAL_landing_page -.landing_page <- function(bfc, FUN.datasets, FUN.initial, default.add = TRUE, default.position = c("first", "last"), body.header = NULL, body.footer = NULL) { +.landing_page <- function(bfc, + FUN.datasets, + FUN.initial, + default.add = TRUE, + default.position = c("first", "last"), + body.header = NULL, + body.footer = NULL) { default.position <- match.arg(default.position) # datasets datasets_available_list <- FUN.datasets() @@ -33,7 +42,8 @@ # initial configurations initial_available_list <- FUN.initial() .check_initial_list(initial_available_list) - initial_available_table <- .initial_to_dataframe(initial_available_list, datasets_available_table[[.datasets_id]]) + initial_available_table <- .initial_to_dataframe(initial_available_list, + datasets_available_table[[.datasets_id]]) # landing page function (return value) function (FUN, input, output, session) { # nocov start @@ -45,7 +55,8 @@ shinydashboard::box(title = "Available Data Sets", collapsible = FALSE, width = NULL, selectizeInput(.ui_dataset_columns, label = "Show columns:", - choices = setdiff(colnames(datasets_available_table), c(.datasets_id, .datasets_uri, .datasets_description)), + choices = setdiff(colnames(datasets_available_table), + c(.datasets_id, .datasets_uri, .datasets_description)), selected = c(.datasets_title), multiple = TRUE, options = list(plugins=list('remove_button', 'drag_drop'))), @@ -109,102 +120,14 @@ } - - - -.landing_page_runr <- function(bfc, FUN.datasets, FUN.initial, default.add = TRUE, default.position = c("first", "last"), body.header = NULL, body.footer = NULL) { - default.position <- match.arg(default.position) - # datasets - datasets_available_list <- FUN.datasets() - .check_datasets_list(datasets_available_list) - datasets_available_table <- .datasets_to_dataframe(datasets_available_list) - # initial configurations - initial_available_list <- FUN.initial() - .check_initial_list(initial_available_list) - initial_available_table <- .initial_to_dataframe(initial_available_list, datasets_available_table[[.datasets_id]]) - # landing page function (return value) - function (FUN, input, output, session) { - # nocov start - output$allPanels <- renderUI({ - tagList( - body.header, - fluidRow( - column(width = 7L, - shinydashboard::box(title = "Available Data Sets", - collapsible = FALSE, width = NULL, - selectizeInput(.ui_dataset_columns, label = "Show columns:", - choices = setdiff(colnames(datasets_available_table), c(.datasets_id, .datasets_uri, .datasets_description)), - selected = c(.datasets_title), - multiple = TRUE, - options = list(plugins=list('remove_button', 'drag_drop'))), - DTOutput(.ui_dataset_table) - )), - column(width = 5L, - shinydashboard::tabBox(id = .ui_box_dataset, title = "Selected dataset", - side = "left", - width = NULL, - tabPanel("Info", - uiOutput(.ui_markdown_overview)), - tabPanel("Configure and launch", - fluidRow( - column(width = 10L, - selectizeInput(.ui_initial, label = "Initial settings:", - choices = character(0))), - column(width = 2L, - br(), - actionButton(.ui_launch_button, label="Launch!", - style="color: #ffffff; background-color: #0092AC; border-color: #2e6da4")) - ), - uiOutput(.ui_initial_overview)) - ) - ) - ), - body.footer - ) # tagList - }) # renderUI - - ## Disable navbar buttons that are not linked to any observer yet - shinyjs::disable(iSEE:::.generalOrganizePanels) # organize panels - shinyjs::disable(iSEE:::.generalLinkGraph) # link graph - shinyjs::disable(iSEE:::.generalExportOutput) # export content - shinyjs::disable(iSEE:::.generalCodeTracker) # tracked code - shinyjs::disable(iSEE:::.generalPanelSettings) # panel settings - shinyjs::disable(iSEE:::.generalVignetteOpen) # open vignette - shinyjs::disable(iSEE:::.generalSessionInfo) # session info - shinyjs::disable(iSEE:::.generalCitationInfo) # citation info - - pObjects <- .create_persistent_objects( - datasets_available_table, - initial_available_table) - rObjects <- reactiveValues( - rerender_datasets=1L, - rerender_overview=1L, - rerender_initial=1L) - - .create_observers(input, session, pObjects, rObjects, FUN.initial, default.add, default.position) - - .create_launch_observers_runr(FUN, bfc, input, session, pObjects) - - .render_datasets_table(output, pObjects, rObjects) - - .render_markdown_overview(output, pObjects, rObjects) - - .render_initial_overview(output, pObjects, rObjects) - - invisible(NULL) - # nocov end - } -} - - - - #' Create persistent objects #' #' @param datasets_table A `data.frame` of metadata for all available data sets. -#' @param initial_table A `data.frame` of metadata for all available initial configuration scripts. +#' @param initial_table A `data.frame` of metadata for all available initial +#' configuration scripts. #' -#' @return An environment containing several global variables for use throughout the application. +#' @return An environment containing several global variables for use throughout +#' the application. #' #' @author Kevin Rue-Albrecht #' diff --git a/R/observers.R b/R/observers.R index 86eaff8..9731fd8 100644 --- a/R/observers.R +++ b/R/observers.R @@ -4,13 +4,16 @@ #' #' `.create_observers()` initialises observers for the \pkg{iSEEindex} landing page. #' -#' `.create_launch_observers()` initialises observers for launching the \pkg{iSEE} main app. +#' `.create_launch_observers()` initialises observers for launching the +#' \pkg{iSEE} main app. #' #' @param input The Shiny input object from the server function. #' @param session The Shiny session object from the server function. -#' @param pObjects An environment containing global parameters generated in the landing page. +#' @param pObjects An environment containing global parameters generated in the +#' landing page. #' @param rObjects A reactive list of values generated in the landing page. -#' @param FUN.initial A function that returns available scripts for initial configurations states for a given data set identifier. +#' @param FUN.initial A function that returns available scripts for initial +#' configurations states for a given data set identifier. #' @param default.add Logical scalar indicating whether a default #' initial configuration should be added as a choice in the Shiny `selectizeInput()`. #' See [iSEEindex()]. @@ -28,7 +31,13 @@ #' @importFrom rintrojs introjs #' #' @rdname INTERNAL_create_observers -.create_observers <- function(input, session, pObjects, rObjects, FUN.initial, default.add, default.position) { +.create_observers <- function(input, + session, + pObjects, + rObjects, + FUN.initial, + default.add, + default.position) { # nocov start observeEvent(input[[.dataset_selected_row]], { @@ -67,6 +76,10 @@ #' architecture. Refer to [iSEE::createLandingPage()] for more details. #' @param bfc A [BiocFileCache()] object. #' landing page. +#' @param input The Shiny input object from the server function. +#' @param session The Shiny session object from the server function. +#' @param pObjects An environment containing global parameters generated in the +#' landing page. #' #' @importFrom shiny observeEvent #' @@ -81,17 +94,3 @@ invisible(NULL) } - - - -.create_launch_observers_runr <- function(FUN, bfc, input, session, pObjects) { - - # nocov start - observeEvent(input[[.ui_launch_button]], { - .launch_isee_runr(FUN, bfc, session, pObjects) - }, ignoreNULL=TRUE, ignoreInit=TRUE) - # nocov end - - invisible(NULL) -} - diff --git a/R/outputs.R b/R/outputs.R index a102296..814fda9 100644 --- a/R/outputs.R +++ b/R/outputs.R @@ -1,14 +1,15 @@ #' Render Table of Available Data Sets #' #' @param output The Shiny output object from the server function. -#' @param pObjects An environment containing global parameters generated in the landing page. +#' @param pObjects An environment containing global parameters generated in the +#' landing page. #' @param rObjects A reactive list of values generated in the landing page. #' #' @return Adds a rendered [DT::datatable()] to `output`. #' A \code{NULL} value is invisibly returned. -#' +#' #' @author Kevin Rue-Albrecht -#' +#' #' @importFrom DT datatable renderDT #' #' @rdname INTERNAL_render_datasets_table @@ -32,17 +33,20 @@ } #' Render Overview of Selected Data Set -#' +#' #' @description -#' -#' `.render_markdown_overview()` renders an overview of the selected data set using Markdown. -#' -#' `.render_initial_overview()` renders an overview of the selected initial configuration using Markdown. +#' +#' `.render_markdown_overview()` renders an overview of the selected data set +#' using Markdown. +#' +#' `.render_initial_overview()` renders an overview of the selected initial +#' configuration using Markdown. #' #' @param output The Shiny output object from the server function. -#' @param pObjects An environment containing global parameters generated in the landing page. +#' @param pObjects An environment containing global parameters generated in the +#' landing page. #' @param rObjects A reactive list of values generated in the landing page. -#' +#' #' @details #' Currently, those functions expect a column named `uri` in the metadata table #' of available data sets, which it uses as the identifier for each data set. @@ -50,9 +54,9 @@ #' @return #' `.render_markdown_overview()` and `.render_initial_overview()` both #' add a rendered [shiny::markdown()] to `output`. -#' +#' #' In both cases, a \code{NULL} value is invisibly returned. -#' +#' #' @author Kevin Rue-Albrecht #' #' @importFrom shiny markdown renderUI @@ -97,7 +101,7 @@ initial_id <- pObjects[[.ui_initial]] dataset_id <- pObjects[[.dataset_selected_id]] which_initial <- which( - pObjects$initial_table[[.initial_config_id]] == initial_id & + pObjects$initial_table[[.initial_config_id]] == initial_id & pObjects$initial_table[[.initial_datasets_id]] == dataset_id ) initial_info <- pObjects$initial_table[which_initial, .initial_description, drop=TRUE] diff --git a/R/utils-datasets.R b/R/utils-datasets.R index 59ab4ba..8496c40 100644 --- a/R/utils-datasets.R +++ b/R/utils-datasets.R @@ -7,7 +7,8 @@ #' #' @param bfc A [BiocFileCache()] object. #' @param id A data set identifier as a character scalar. -#' @param metadata Named list of metadata. See individual resource classes for required and optional metadata. +#' @param metadata Named list of metadata. See individual resource classes for +#' required and optional metadata. #' #' @return #' For `.load_sce()`, a [SingleCellExperiment()] object. @@ -30,68 +31,52 @@ #' #' iSEEindex:::.load_sce(bfc, id, metadata) #' -.load_sce <- function(bfc, id, metadata) { - bfc_result <- bfcquery(bfc, id, field = "rname", exact = TRUE) - # nocov start - if (nrow(bfc_result) == 0) { - uri_object <- .metadata_to_object(metadata) - object_path <- precache(uri_object, bfc, id) +#' ## Alternatively, using the runr approach +#' id_tonsil <- "demo_load_sce_tonsil" +#' metadata_tonsil <- list( +#' uri="runr://HCATonsilData::HCATonsilData(assayType = 'RNA', cellType = 'epithelial')" +#' ) +#' iSEEindex:::.load_sce(bfc, id_tonsil, metadata_tonsil) +.load_sce <- function(bfc, + id, + metadata) { + resource_obj <- .metadata_to_object(metadata) + if (is(resource_obj, "iSEEindexHttpsResource") | + is(resource_obj, "iSEEindexLocalhostResource") | + is(resource_obj, "iSEEindexRcallResource") | + is(resource_obj, "iSEEindexS3Resource")) { + already_se_object <- FALSE + } else if (is(resource_obj, "iSEEindexRunrResource")) { + already_se_object <- TRUE } else { - object_path <- bfc[[bfc_result$rid]] + # without knowing too much about it... + already_se_object <- FALSE } - # nocov end - object <- readRDS(object_path) - object <- .convert_to_sce(object) - object -} - - - - -#' @examples -#' -#' library(BiocFileCache) -#' bfc <- BiocFileCache(tempdir()) -#' id <- "demo_load_sce_tonsil" -#' metadata <- list(uri="runr://HCATonsilData::HCATonsilData(assayType = 'RNA', cellType = 'epithelial')") -#' -#' ## Usage --- -#' -#' iSEEindex:::.load_sce_runr(bfc, id, metadata) -.load_sce_runr <- function(bfc, id, metadata) { - bfc_result <- bfcquery(bfc, id, field = "rname", exact = TRUE) - # nocov start - # if (nrow(bfc_result) == 0) { - # uri_object <- .metadata_to_object(metadata) - # object_path <- precache(uri_object, bfc, id) - # } else { - # object_path <- bfc[[bfc_result$rid]] - # } - - - uri_object <- .metadata_to_object_runr(metadata) - - object_call <- precache(uri_object, bfc, id) - # if (nrow(bfc_result) == 0) { - # uri_object <- .metadata_to_object(metadata) - # object_path <- precache(uri_object, bfc, id) - # } else { - # object_path <- bfc[[bfc_result$rid]] - # } - - # nocov end - # object <- readRDS(object_path) - - object <- object_call + if (!already_se_object) { + bfc_result <- bfcquery(bfc, id, field = "rname", exact = TRUE) + # nocov start + if (nrow(bfc_result) == 0) { + uri_object <- .metadata_to_object(metadata) + object_path <- precache(uri_object, bfc, id) + } else { + object_path <- bfc[[bfc_result$rid]] + } + # nocov end + object <- readRDS(object_path) + object <- .convert_to_sce(object) + } else { + # if providing directly an SE object, e.g. via data packages... + uri_object <- .metadata_to_object(metadata) - object <- .convert_to_sce(object) - object + object <- precache(uri_object, bfc, id) + object <- .convert_to_sce(object) + } + object } - #' @param x An object Coercible to SingleCellExperiment #' #' @return @@ -113,6 +98,7 @@ x } + #' Convert Metadata to Class #' #' @param x Named list of metadata. @@ -135,6 +121,11 @@ #' )) #' iSEEindex:::.metadata_to_object(list(uri="s3://your-bucket/your-prefix/file.rds")) #' iSEEindex:::.metadata_to_object(list(uri="s3://your-bucket/your-prefix/file.rds")) +#' iSEEindex:::.metadata_to_object( +#' list( +#' uri="runr://HCATonsilData::HCATonsilData(assayType = 'RNA', cellType = 'epithelial')" +#' ) +#' ) .metadata_to_object <- function(x) { scheme <- urltools::url_parse(x[[.datasets_uri]])$scheme scheme_titled <- str_to_title(scheme) @@ -152,34 +143,12 @@ } -#' @examples -#' iSEEindex:::.metadata_to_object(list(uri="runr://HCATonsilData::HCATonsilData(assayType = 'RNA', cellType = 'epithelial')")) -.metadata_to_object_runr <- function(x) { - scheme <- urltools::url_parse(x[[.datasets_uri]])$scheme - scheme_titled <- str_to_title(scheme) - target_class <- sprintf("iSEEindex%sResource", scheme_titled) - constructor.FUN <- try({ - get(target_class) - }, silent = TRUE) - if (is(constructor.FUN, "try-error")) { - stop( - sprintf("No constructor function available for scheme '%s'. ", scheme), - sprintf("Consider implementing the constructor function '%s()'.", target_class) - ) - } - constructor.FUN(x) -} - - - - - - #' Check Validity of Data Sets Metadata #' #' @param x `list` of of lists of metadata. #' -#' @return Invisible `NULL` if the metadata table is valid. Otherwise, throw an error. +#' @return Invisible `NULL` if the metadata table is valid. Otherwise, throw an +#' error. #' #' @author Kevin Rue-Albrecht #' diff --git a/R/utils-initial.R b/R/utils-initial.R index 993aa5c..48161a2 100644 --- a/R/utils-initial.R +++ b/R/utils-initial.R @@ -18,10 +18,10 @@ #' added to the choices for all data sets. #' That default option launches an initial state that includes one panel of #' each type compatible with the information present in the data set. -#' +#' #' The default initial configuration is always added for data sets which are not #' associated with any custom initial configuration. -#' +#' #' The option `default.add` controls whether the default initial configuration #' is added to the choices for data sets associated with at least one custom #' initial configuration. @@ -32,13 +32,16 @@ #' @author Kevin Rue-Albrecht #' #' @rdname INTERNAL_initial_choices -.initial_choices <- function(id, available, default.add = TRUE, default.position = c("first", "last")) { +.initial_choices <- function(id, + available, + default.add = TRUE, + default.position = c("first", "last")) { default.position <- match.arg(default.position) which_initial <- available[[.initial_datasets_id]] == id config_subset_table <- available[which_initial, , drop=FALSE] initial_choices <- config_subset_table[[.initial_config_id]] names(initial_choices) <- config_subset_table[[.initial_title]] - + if (default.add) { default_choice <- c("Default" = .initial_default_choice) if (identical(default.position, "first")) { @@ -47,27 +50,29 @@ initial_choices <- c(initial_choices, default_choice) } } - + initial_choices } #' Parses an Initial Application State script -#' +#' #' @description #' Parses the selected initial application state script. #' This can be a custom R script or the default initial state that creates #' one panel of each class compatible with the contents of the data set. -#' +#' #' The same script may define two objects `initial` and `tour` (see 'Value'). #' #' @param bfc A [BiocFileCache()] object. #' @param dataset_id Character scalar. Identifier of the data set selected. #' @param config_id Character scalar. Identifier of the configuration file to load. -#' @param metadata Named list of metadata. See individual resource classes for required and optional metadata. +#' @param metadata Named list of metadata. See individual resource classes for +#' required and optional metadata. #' #' @return A `list` of two elements. #' \describe{ -#' \item{initial}{A list of [Panel-class] objects, representing an initial app state} +#' \item{initial}{A list of [Panel-class] objects, representing an initial app +#' state} #' \item{tour}{A `data.frame` representing an \pkg{rintrojs} interactive tour} #' } #' @@ -113,7 +118,8 @@ #' #' @param x `list` of lists of metadata. #' -#' @return Invisible `NULL` if the metadata table is valid. Otherwise, throw an error. +#' @return Invisible `NULL` if the metadata table is valid. Otherwise, throw an +#' error. #' #' @author Kevin Rue-Albrecht #' diff --git a/inst/tonsils_example/tonsil_and_mix.yml b/inst/tonsils_example/tonsil_and_mix.yml new file mode 100644 index 0000000..a9e30fc --- /dev/null +++ b/inst/tonsils_example/tonsil_and_mix.yml @@ -0,0 +1,48 @@ +datasets: + - id: Tonsil_epithelial + title: Tonsil Data - Epithelial + uri: runr://HCATonsilData::HCATonsilData(assayType = "RNA", cellType = "epithelial") + description: | + Tonsil Data - Epithelial, 396 cells + +
+ +
+ - id: ReprocessedAllenData + title: ReprocessedAllenData + uri: https://zenodo.org/record/8076991/files/ReprocessedAllenData.rds + description: | + Reprocessed Allen Data. + - id: BuettnerESCData + title: BuettnerESCData + uri: https://zenodo.org/record/8076991/files/BuettnerESCData.rds + description: | + Reprocessed Allen Data (copy). +initial: + - id: Tonsil_epithelial_full5 + datasets: + - Tonsil_epithelial + title: Tonsil_epithelial configuration 1 (R call) + uri: rcall://system.file(package='iSEEindex','tonsils_example/hca_tonsil_epithelial_v2.R') + # uri: localhost:///Users/fede/Development/iSEEindex/hca_tonsil_epithelial_v2.R + # uri: rcall://system.file(package='iSEEindex','ReprocessedAllenData_config_01.R') + description: | + touring around, first go + - id: config01rcall + datasets: + - ReprocessedAllenData + title: Example configuration 1 (R call) + uri: rcall://system.file(package='iSEEindex','ReprocessedAllenData_config_01.R') + description: | + One `ReducedDimensionPlot` panel, one `ColumnDataTable` panel. + + File distributed with the `iSEEindex` package. + - id: config02rcall + datasets: + - ReprocessedAllenData + title: Example configuration 2 (R call) + uri: rcall://system.file(package='iSEEindex','ReprocessedAllenData_config_02.R') + description: | + One `RowDataTable` panel, one `ColumnDataTable` panel. + + File distributed with the `iSEEindex` package. diff --git a/man/iSEEindex.Rd b/man/iSEEindex.Rd index e1eb505..3dfaf4e 100644 --- a/man/iSEEindex.Rd +++ b/man/iSEEindex.Rd @@ -41,7 +41,8 @@ info on the versions of the \code{iSEEindex} and \code{iSEE} packages.} \item{body.footer}{UI element to display \emph{below} the main landing page body.} } \value{ -An \code{\link[iSEE:iSEE]{iSEE::iSEE()}} app with a custom landing page using a \code{\link[=BiocFileCache]{BiocFileCache()}} to cache a selection of data sets. +An \code{\link[iSEE:iSEE]{iSEE::iSEE()}} app with a custom landing page using a +\code{\link[=BiocFileCache]{BiocFileCache()}} to cache a selection of data sets. } \description{ Generate an \pkg{iSEE} app that includes a landing page enabling @@ -50,57 +51,75 @@ states prepared by the app maintainer. } \section{Data Sets}{ -The function passed to the argument \code{FUN.datasets} must return a \code{list} that contains metadata about the available data sets. +The function passed to the argument \code{FUN.datasets} must return a \code{list} that +contains metadata about the available data sets. For each data set, required metadata are: \describe{ \item{id}{A unique identifier for the data set.} -\item{title}{A short human-readable title for the data set, displayed in the 'Info' panel when the data set is selected.} -\item{uri}{A Uniform Resource Identifier (URI) that indicates the location of the data file that contains the data set.} -\item{description}{A more detailed description of the data set, displayed in the 'Info' panel when the data set is selected.} +\item{title}{A short human-readable title for the data set, displayed in the +'Info' panel when the data set is selected.} +\item{uri}{A Uniform Resource Identifier (URI) that indicates the location of +the data file that contains the data set.} +\item{description}{A more detailed description of the data set, displayed in +the 'Info' panel when the data set is selected.} } Example: \if{html}{\out{