diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1b8260f5d..4e2dde45a 100755 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,7 +6,7 @@ default_language_version: python: python3 repos: - repo: https://github.com/lorenzwalthert/precommit - rev: v0.3.2.9021 + rev: v0.3.2.9023 hooks: - id: style-files name: Style code with `styler` diff --git a/DESCRIPTION b/DESCRIPTION index 8d37bc6c6..f692e0117 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,8 +1,8 @@ Type: Package Package: teal.slice Title: Filter Module for 'teal' Applications -Version: 0.4.0.9016 -Date: 2023-10-12 +Version: 0.4.0.9018 +Date: 2023-10-19 Authors@R: c( person("Dawid", "Kaledkowski", , "dawid.kaledkowski@roche.com", role = c("aut", "cre")), person("Pawel", "Rucki", , "pawel.rucki@roche.com", role = "aut"), diff --git a/NAMESPACE b/NAMESPACE index 082fe0b30..47a9a5d3b 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -46,8 +46,6 @@ export(is.teal_slices) export(list_to_teal_slices) export(remove_filter_state) export(set_filter_state) -export(slices_restore) -export(slices_store) export(teal_slice) export(teal_slices) import(R6) diff --git a/NEWS.md b/NEWS.md index 1442e64b3..80ae507f6 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,8 +1,9 @@ -# teal.slice 0.4.0.9016 +# teal.slice 0.4.0.9018 ### Miscellaneous * Specified minimal version of package dependencies. +* Removed storing and restoring of `teal_slices` objects. # teal.slice 0.4.0 diff --git a/R/teal_slice-store.R b/R/teal_slice-store.R deleted file mode 100644 index 5bc570dd2..000000000 --- a/R/teal_slice-store.R +++ /dev/null @@ -1,58 +0,0 @@ -#' Store teal_slices object to a file -#' -#' This function takes a `teal_slices` object and saves it to a file in `JSON` format. -#' The `teal_slices` object contains information about filter states and can be used to -#' create, modify, and delete filter states. The saved file can be later loaded using -#' the `slices_restore` function. -#' -#' @param tss (`teal_slices`) object to be stored. -#' @param file (`character(1)`) The file path where `teal_slices` object will be saved. -#' The file extension should be `".json"`. -#' -#' @return `NULL`, invisibly. -#' -#' @examples -#' # Create a teal_slices object -#' tss <- teal_slices( -#' teal_slice(dataname = "data", varname = "var"), -#' teal_slice(dataname = "data", expr = "x > 0", id = "positive_x", title = "Positive x") -#' ) -#' -#' if (interactive()) { -#' # Store the teal_slices object to a file -#' slices_store(tss, "path/to/file.json") -#' } -#' -#' @export -slices_store <- function(tss, file) { - checkmate::assert_class(tss, "teal_slices") - checkmate::assert_path_for_output(file, overwrite = TRUE, extension = "json") - - cat(format(tss, trim_lines = FALSE), "\n", file = file) -} - -#' Restore teal_slices object from a file -#' -#' This function takes a file path to a `JSON` file containing a `teal_slices` object -#' and restores it to its original form. The restored `teal_slices` object can be used -#' to access filter states and their corresponding attributes. -#' -#' @param file Path to file where `teal_slices` is stored. Must have a `.json` extension and read access. -#' -#' @return A `teal_slices` object restored from the file. -#' -#' @examples -#' if (interactive()) { -#' # Restore a teal_slices object from a file -#' tss_restored <- slices_restore("path/to/file.json") -#' } -#' @export -slices_restore <- function(file) { - checkmate::assert_file_exists(file, access = "r", extension = "json") - - tss_json <- jsonlite::fromJSON(file, simplifyDataFrame = FALSE) - - tss_elements <- lapply(tss_json$slices, as.teal_slice) - - do.call(teal_slices, c(tss_elements, tss_json$attributes)) -} diff --git a/R/teal_slice.R b/R/teal_slice.R index 938c04e6f..053ac1b9d 100644 --- a/R/teal_slice.R +++ b/R/teal_slice.R @@ -58,9 +58,9 @@ #' requires `dataname` prefix, *e.g.* `data$var == "x"`. #' @param choices (optional `vector`) specifying allowed choices; #' When specified it should be a subset of values in variable denoted by `varname`; -#' Type and size depends on variable type. +#' Type and size depends on variable type. Factors are coerced to character. #' @param selected (optional `vector`) of selected values from `choices`; -#' Type and size depends on variable type. +#' Type and size depends on variable type. Factors are coerced to character. #' @param multiple (optional `logical(1)`) flag specifying whether more than one value can be selected; #' only applicable to `ChoicesFilterState` and `LogicalFilterState` #' @param keep_na (optional `logical(1)`) flag specifying whether to keep missing values @@ -78,6 +78,10 @@ #' @return A `teal.slice` object. Depending on whether `varname` or `expr` was specified, the resulting #' `teal_slice` also receives class `teal_slice_var` or `teal_slice_expr`, respectively. #' +#' @note When `teal_slice` is printed and contains a `POSIX*t` class in `selected` or `choices` fields, then those +#' fields are converted to `UTC` timezone, for enhanced and unified storage and restoring with `teal::slices_store()` +#' and `teal::slices_restore()`. +#' #' @examples #' x1 <- teal_slice( #' dataname = "data", @@ -155,6 +159,8 @@ teal_slice <- function(dataname, ) formal_args <- formal_args[ts_var_args] args <- c(formal_args, list(...)) + args[c("choices", "selected")] <- + lapply(args[c("choices", "selected")], function(x) if (is.factor(x)) as.character(x) else x) if (missing(id)) { args$id <- get_default_slice_id(args) } else { @@ -268,7 +274,7 @@ to_json <- function(x) { vars <- c("selected", "choices") if (is.list(x)) { for (var in vars) { - if (!is.null(x[[var]])) x[[var]] <- I(x[[var]]) + if (!is.null(x[[var]])) x[[var]] <- I(format_time(x[[var]])) } lapply(x, no_unbox) } else { @@ -279,6 +285,14 @@ to_json <- function(x) { jsonlite::toJSON(no_unbox(x), pretty = TRUE, auto_unbox = TRUE, digits = 16, null = "null") } +format_time <- function(x) { + if ("POSIXt" %in% class(x)) { + format(x, format = "%Y-%m-%d %H:%M:%S", usetz = TRUE, tz = "UTC") + } else { + x + } +} + #' Justify Colons in `JSON` String #' #' This function takes a `JSON` string as input, splits it into lines, and pads element names diff --git a/R/teal_slices.R b/R/teal_slices.R index ec0a7dbb5..cfad12948 100644 --- a/R/teal_slices.R +++ b/R/teal_slices.R @@ -30,6 +30,10 @@ #' @param i (`character` or `numeric` or `logical`) indicating which elements to extract #' @param recursive (`logical(1)`) flag specifying whether to also convert to list the elements of this `teal_slices` #' +#' @note When `teal_slices` are printed and any of `teal_slice` elements contain a `POSIX*t` class in `selected` or +#' `choices` fields, then those fields are converted to `UTC` timezone, for enhanced and unified storage and restoring +#' with `teal::slices_store()` and `teal::slices_restore()`. +#' #' @return #' `teal_slices`, which is an unnamed list of `teal_slice` objects. #' diff --git a/_pkgdown.yml b/_pkgdown.yml index 6bcf44357..b469c81de 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -32,8 +32,6 @@ reference: - clear_filter_states - teal_slice - teal_slices - - slices_store - - slices_restore - title: For Developers subtitle: R6 Classes desc: Abstract and concrete classes used to build teal functionality. diff --git a/man/slices_restore.Rd b/man/slices_restore.Rd deleted file mode 100644 index 34784b96e..000000000 --- a/man/slices_restore.Rd +++ /dev/null @@ -1,25 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/teal_slice-store.R -\name{slices_restore} -\alias{slices_restore} -\title{Restore teal_slices object from a file} -\usage{ -slices_restore(file) -} -\arguments{ -\item{file}{Path to file where \code{teal_slices} is stored. Must have a \code{.json} extension and read access.} -} -\value{ -A \code{teal_slices} object restored from the file. -} -\description{ -This function takes a file path to a \code{JSON} file containing a \code{teal_slices} object -and restores it to its original form. The restored \code{teal_slices} object can be used -to access filter states and their corresponding attributes. -} -\examples{ -if (interactive()) { - # Restore a teal_slices object from a file - tss_restored <- slices_restore("path/to/file.json") -} -} diff --git a/man/slices_store.Rd b/man/slices_store.Rd deleted file mode 100644 index 1d16c1ccb..000000000 --- a/man/slices_store.Rd +++ /dev/null @@ -1,36 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/teal_slice-store.R -\name{slices_store} -\alias{slices_store} -\title{Store teal_slices object to a file} -\usage{ -slices_store(tss, file) -} -\arguments{ -\item{tss}{(\code{teal_slices}) object to be stored.} - -\item{file}{(\code{character(1)}) The file path where \code{teal_slices} object will be saved. -The file extension should be \code{".json"}.} -} -\value{ -\code{NULL}, invisibly. -} -\description{ -This function takes a \code{teal_slices} object and saves it to a file in \code{JSON} format. -The \code{teal_slices} object contains information about filter states and can be used to -create, modify, and delete filter states. The saved file can be later loaded using -the \code{slices_restore} function. -} -\examples{ -# Create a teal_slices object -tss <- teal_slices( - teal_slice(dataname = "data", varname = "var"), - teal_slice(dataname = "data", expr = "x > 0", id = "positive_x", title = "Positive x") -) - -if (interactive()) { - # Store the teal_slices object to a file - slices_store(tss, "path/to/file.json") -} - -} diff --git a/man/teal_slice.Rd b/man/teal_slice.Rd index bab8f4c9d..5de699169 100644 --- a/man/teal_slice.Rd +++ b/man/teal_slice.Rd @@ -50,10 +50,10 @@ requires \code{dataname} prefix, \emph{e.g.} \code{data$var == "x"}.} \item{choices}{(optional \code{vector}) specifying allowed choices; When specified it should be a subset of values in variable denoted by \code{varname}; -Type and size depends on variable type.} +Type and size depends on variable type. Factors are coerced to character.} \item{selected}{(optional \code{vector}) of selected values from \code{choices}; -Type and size depends on variable type.} +Type and size depends on variable type. Factors are coerced to character.} \item{keep_na}{(optional \code{logical(1)}) flag specifying whether to keep missing values} @@ -121,6 +121,11 @@ In a \code{FilterState} instantiated with \code{fixed = TRUE} the features Note that a \code{FilterStateExpr} is always considered to have \code{fixed = TRUE}. A \code{FilterState} instantiated with \code{anchored = TRUE} cannot be removed. } +\note{ +When \code{teal_slice} is printed and contains a \code{POSIX*t} class in \code{selected} or \code{choices} fields, then those +fields are converted to \code{UTC} timezone, for enhanced and unified storage and restoring with \code{teal::slices_store()} +and \code{teal::slices_restore()}. +} \section{Filters in \code{SumarizedExperiment} and \code{MultiAssayExperiment} objects}{ diff --git a/man/teal_slices.Rd b/man/teal_slices.Rd index 681db4e65..9658283b1 100644 --- a/man/teal_slices.Rd +++ b/man/teal_slices.Rd @@ -83,6 +83,11 @@ The former enumerates allowed variables, the latter enumerates forbidden values. Since these could be mutually exclusive, it is impossible to set both allowed and forbidden variables for one data set in one \code{teal_slices}. } +\note{ +When \code{teal_slices} are printed and any of \code{teal_slice} elements contain a \code{POSIX*t} class in \code{selected} or +\code{choices} fields, then those fields are converted to \code{UTC} timezone, for enhanced and unified storage and restoring +with \code{teal::slices_store()} and \code{teal::slices_restore()}. +} \examples{ filter_1 <- teal_slice( dataname = "dataname1", diff --git a/tests/testthat/test-teal_slice.R b/tests/testthat/test-teal_slice.R index b0996b7b2..579319798 100644 --- a/tests/testthat/test-teal_slice.R +++ b/tests/testthat/test-teal_slice.R @@ -219,3 +219,21 @@ testthat::test_that("teal_slice dataname has to be a string when expr is specifi teal_slice(dataname = character(0), id = "x", title = "x", expr = "x == 'x'"), "length" ) }) + +testthat::test_that( + "teal_slice converts factors to characters for 'selected' and 'choices' parameters", + { + slices_path <- withr::local_file("slices.json") + tss <- teal_slices( + teal_slice( + dataname = "ADSL", + varname = "EOSDTM", + choices = factor(c("a", "b", "c")), + selected = factor(c("a", "b")) + ) + ) + + testthat::expect_type(shiny::isolate(tss[[1]]$selected), "character") + testthat::expect_type(shiny::isolate(tss[[1]]$choices), "character") + } +)