From 8dfa57f03a8b603aa78a62e3892e57c2a6215a0a Mon Sep 17 00:00:00 2001 From: Ramiro Magno Date: Fri, 24 May 2024 01:01:40 +0100 Subject: [PATCH] Basic support for "conditioned" data frames - Adds a new S3 class (cnd_df) for represented conditioned data frames, i.e. data frames that carry metadata about what records should be used for derivations - Adds support for basic pretty printing of cnd_df objects - Adds a user-facing function for creating such cnd_df objects: `condition_by` - Adds experimental "mutate"-version function for these conditioned data frames: `derive_by_condition()` --- DESCRIPTION | 3 +- NAMESPACE | 4 + R/assertions.R | 16 + R/cnd_df.R | 299 +++++++ man/condition_by.Rd | 27 + man/eval_conditions.Rd | 74 ++ man/new_cnd_df.Rd | 40 + man/rm_cnd_df.Rd | 18 + man/tbl_sum.cnd_df.Rd | 16 + renv/profiles/4.4/renv.lock | 1279 +++++++++++++++++++++++++++++ renv/profiles/4.4/renv/.gitignore | 7 + 11 files changed, 1782 insertions(+), 1 deletion(-) create mode 100644 R/assertions.R create mode 100644 R/cnd_df.R create mode 100644 man/condition_by.Rd create mode 100644 man/eval_conditions.Rd create mode 100644 man/new_cnd_df.Rd create mode 100644 man/rm_cnd_df.Rd create mode 100644 man/tbl_sum.cnd_df.Rd create mode 100644 renv/profiles/4.4/renv.lock create mode 100644 renv/profiles/4.4/renv/.gitignore diff --git a/DESCRIPTION b/DESCRIPTION index 721a589a..4cd995cb 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -46,7 +46,8 @@ Imports: tibble, vctrs, readr, - glue + glue, + pillar Suggests: knitr, rmarkdown, diff --git a/NAMESPACE b/NAMESPACE index 41e0d4cd..472cb825 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,10 +1,12 @@ # Generated by roxygen2: do not edit by hand S3method(print,iso8601) +S3method(tbl_sum,cnd_df) export(assign_ct) export(assign_datetime) export(assign_no_ct) export(clear_cache) +export(condition_by) export(create_iso8601) export(ct_map) export(ct_spec_example) @@ -16,6 +18,8 @@ export(hardcode_no_ct) export(problems) export(read_ct_spec) export(read_ct_spec_example) +export(rm_cnd_df) +importFrom(pillar,tbl_sum) importFrom(rlang,"%||%") importFrom(rlang,":=") importFrom(rlang,.data) diff --git a/R/assertions.R b/R/assertions.R new file mode 100644 index 00000000..8228c4ee --- /dev/null +++ b/R/assertions.R @@ -0,0 +1,16 @@ +# Surprisingly, admiraldev doesn't provide `assert_logical_vector`. +assert_logical_vector <- function(arg, optional = FALSE) { + if (optional && is.null(arg)) { + return(invisible(arg)) + } + + if (!is.logical(arg)) { + err_msg <- sprintf( + "`arg` must be a logical vector but is %s.", + admiraldev::what_is_it(arg) + ) + rlang::abort(err_msg) + } + + invisible(arg) +} diff --git a/R/cnd_df.R b/R/cnd_df.R new file mode 100644 index 00000000..f9640294 --- /dev/null +++ b/R/cnd_df.R @@ -0,0 +1,299 @@ +# ------------------------------------------------------------------------------ +# File: cnd_df.R +# Package: sdtm.oak +# Author: Ramiro Magno +# Created: 2024-05-23 +# Last Modified: 2024-05-23 +# ------------------------------------------------------------------------------ +# Description: +# +# This file contains functions and scripts related to the so-called conditioned +# data frames, i.e. those data frames extended with class `cnd_df`. +# +# Functions: +# +# - `new_cnd_df()`: Create a "conditioned data set" (class cnd_df). +# - `is_cnd_df()`: Assess whether the argument is a `cnd_df` data frame. +# - `get_cnd_df_cnd()`: Extract the attribute "cnd" from a `cnd_df` data frame. +# - `get_cnd_df_cnd_sum()`: Extract the attribute "cnd_sum" from a `cnd_df` data frame. +# - `rm_cnd_df()`: De-class a cnd_df data frame. +# - `tbl_sum.cnd_df()`: Provide a tibble header print method for `cnd_df` tibbles. +# - `ctl_new_rowid_pillar.cnd_df()`: A print method for the row ids cnd_df` tibbles. +# - `eval_conditions()`: Find which rows match a set of conditions. +# - `condition_by()`: Create a conditioned data frame (user facing). +# - `derive_by_condition()`: Perform a derivation on a conditioned data frame. + +#' Create a data frame with filtering tags +#' +#' [new_cnd_df()] creates a _conditioned_ data frame, classed `cnd_df`, meaning +#' that this function extends the data frame passed as argument by storing a +#' logical vector `cnd` (as attribute) that marks rows for posterior conditional +#' transformation by methods that support _conditioned_ data frames. +#' +#' @param dat A data frame. +#' @param cnd A logical vector. Length must match the number of rows in `dat`. +#' @param .warn Whether to warn about creating a new _conditioned_ data frame +#' in case that `dat` already is one. +#' +#' @returns A data frame `dat` with the additional class `"cnd_df"` and the +#' following attributes: +#' +#' - `cnd`: The logical vector passed as argument `cnd`: `TRUE` values mark +#' rows in `dat` to be used for transformations; rows marked with `FALSE` are +#' not transformed; and `NA` mark rows whose transformations are to be applied +#' resulting in `NA`. +#' - `cnd_sum`: An integer vector of three elements providing the sum of `TRUE`, +#' `FALSE` and `NA` values in `cnd`, respectively. +#' +#' @examples +#' df <- data.frame(x = 1:3, y = letters[1:3]) +#' sdtm.oak:::new_cnd_df(dat = df, cnd = c(FALSE, NA, TRUE)) +#' +#' @keywords internal +new_cnd_df <- function(dat, cnd, .warn = TRUE) { + + admiraldev::assert_data_frame(dat) + assert_logical_vector(cnd) + + if (!identical(nrow(dat), length(cnd))) { + msg <- c( + "Number of rows in `dat` must match length of `cond`." + ) + rlang::abort(message = msg) + } + + is_cnd_df <- inherits(dat, "cnd_df") + if (.warn && is_cnd_df) { + msg <- "`dat` is already a conditioned data frame (`cnd_df`)." + rlang::warn(message = msg) + } + + if (!is_cnd_df) { + class(dat) <- c("cnd_df", class(dat)) + } + + n_true <- sum(cnd, na.rm = TRUE) + n_false <- sum(!cnd, na.rm = TRUE) + n_na <- length(cnd) - (n_true + n_false) + cnd_sum <- c(n_true = n_true, n_false = n_false, n_na = n_na) + + attr(dat, "cnd") <- cnd + attr(dat, "cnd_sum") <- cnd_sum + + return(dat) +} + +is_cnd_df <- function(dat) { + inherits(dat, "cnd_df") +} + +get_cnd_df_cnd <- function(dat) { + if (is_cnd_df(dat)) { + attr(dat, "cnd") + } else { + NULL + } +} + +get_cnd_df_cnd_sum <- function(dat) { + if (is_cnd_df(dat)) { + attr(dat, "cnd_sum") + } else { + NULL + } +} + +#' Remove the cnd_df class from a data frame +#' +#' This function removes the 'cnd_df' class, along with its attributes, if +#' applicable. +#' +#' @param dat A data frame. +#' @return The input `dat` without the 'cnd_df' class. +#' +#' @export +rm_cnd_df <- function(dat) { + if (is_cnd_df(dat)) { + class(dat) <- class(dat)[class(dat) != "cnd_df"] + attr(dat, "cnd") <- NULL + attr(dat, "cnd_sum") <- NULL + } + return(dat) +} + +#' Print +#' +#' Blah +#' +#' @param x A conditioned tibble of class 'cnd_df'. +#' @param ... Additional arguments passed to the default print method. +#' +#' @importFrom pillar tbl_sum +#' @export +tbl_sum.cnd_df <- function(x, ...) { + default_header <- NextMethod() + + tally <- get_cnd_df_cnd_sum(x) + h2 <- sprintf("%d/%d/%d", tally[1], tally[2], tally[3]) + c(default_header, "Cond. tbl" = h2) +} + +lgl_to_chr <- function(x) { + ifelse(is.na(x), "-", ifelse(x, "T", "F")) +} + +ctl_new_rowid_pillar.cnd_df <- function(controller, x, width, ...) { + + out <- NextMethod() + n_row <- nrow(x) + idx <- seq_len(n_row) + i <- sprintf("%d", idx) + i_width <- nchar(as.character(i)) + i_max_width <- max(i_width) + max_width <- i_max_width + 2 + ws <- strrep(" ", max_width - i_width - 1) + abb_lgl <- lgl_to_chr(attr(controller, "cnd")[idx]) + + row_ids <- paste0(i, ws, abb_lgl) + width <- max(nchar(as.character(row_ids))) + pillar::new_pillar( + list( + title = out$title, + type = out$type, + data = pillar::pillar_component( + pillar::new_pillar_shaft(list(row_ids = row_ids), + width = width, + class = "pillar_rif_shaft" + ) + ) + ), + width = width + ) +} + +#' Evaluate conditions +#' +#' @description +#' [eval_conditions()] evaluates a set of conditions in the context of a +#' data frame and an optional environment. +#' +#' The utility of this function is to provide an easy way to generate a logical +#' vector of matching records from a set of logical conditions involving +#' variables in a data frame (`dat`) and optionally in a supplementary +#' environment (`.env`). The set of logical conditions are provided as +#' expressions to be evaluated in the context of `dat` and `.env`. +#' +#' Variables are looked up in `dat`, then in `.env`, then in the calling +#' function's environment, followed by its parent environments. +#' +#' @param dat A data frame +#' @param ... A set of logical conditions, e.g. `y & z, x | z` (`x`, `y`, `z` +#' would have to exist either as columns in `dat` or in the enviroment +#' `.env`). If multiple expressions are included, they are combined with the +#' `&` operator. +#' @param .na Return value to be used when the conditions evalute to `NA`. +#' @param .env An optional environment to look for variables involved in logical +#' expression passed in `...`. A data frame or a list can also be passed that +#' will be coerced to an environment internally. +#' +#' @returns A logical vector reflecting matching rows in `dat`. +#' +#' @examples +#' # Create a sample data frame +#' df <- data.frame( +#' x = c(1, 2, 3, 4, 5), +#' y = c(TRUE, FALSE, TRUE, FALSE, TRUE), +#' z = c("a", "b", "a", "b", "a") +#' ) +#' +#' # Simple condition on one column +#' sdtm.oak:::eval_conditions(df, x > 2) +#' +#' # Combined conditions on multiple columns +#' sdtm.oak:::eval_conditions(df, x > 2, y) +#' +#' # Using conditions with NA handling +#' df_with_na <- data.frame( +#' x = c(1, 2, NA, 4, 5), +#' y = c(TRUE, FALSE, TRUE, FALSE, TRUE) +#' ) +#' sdtm.oak:::eval_conditions(df_with_na, x > 2, .na = FALSE) +#' +#' # The environment where `eval_conditions()` is called is also inspected +#' # when evaluating conditions in `...`. +#' w <- 1 +#' sdtm.oak:::eval_conditions(df, x > w) +#' +#' # Using an environment +#' env <- rlang::env(w = 2) +#' sdtm.oak:::eval_conditions(df, x > w, .env = env) +#' +#' # In place of an environment, you may alternatively pass a list or data frame. +#' sdtm.oak:::eval_conditions(df, x > w, .env = list(w = 3)) +#' sdtm.oak:::eval_conditions(df, x > w, .env = tibble::tibble(w = 4)) +#' +#' @keywords internal +eval_conditions <- function(dat, + ..., + .na = NA, + .env = rlang::env()) { + + conditions <- rlang::enexprs(...) + + # List (or data frame). + if (is.list(.env)) { + .env <- rlang::as_environment(.env, parent = rlang::env()) + } + + lgl_vctrs <- + conditions |> + purrr::map(~ rlang::eval_tidy(.x, dat, env = .env)) |> + purrr::map(~ dplyr::if_else(is.na(.x), .na, .x)) + + cnd <- purrr::reduce(lgl_vctrs, `&`, .init = rep(TRUE, nrow(dat))) + + cnd +} + +#' Condition a data set based on specified conditions +#' +#' This function tags records in a data set, indicating which rows match the +#' specified conditions. +#' +#' @param dat A tibble. +#' @param ... Conditions to filter the tibble. +#' @return A tibble with an additional class 'cnd_df' and a logical vector +#' attribute indicating matching rows. +#' @param .na Return value to be used when the conditions evalute to `NA`. +#' @param .env An optional environment to look for variables involved in logical +#' expression passed in `...`. A data frame or a list can also be passed that +#' will be coerced to an environment internally. +#' +#' @export +condition_by <- function(dat, ..., .na = NA, .env = rlang::env()) { + + if (is_cnd_df(dat)) { + rlang::warn( + c("`dat` is already a conditioned data frame (`cnd_df`).", + "The previous condition will be replaced by the new one.") + ) + } + + cnd <- eval_conditions(dat = dat, ..., .na = .na, .env = .env) + new_cnd_df(dat, cnd = cnd, .warn = FALSE) +} + +#' @keywords internal +derive_by_condition <- function(dat, ...) { + + cnd <- get_cnd_df_cnd(dat) + derivations <- rlang::enquos(...) + derived_vars <- names(derivations) + + lst <- purrr::map(derivations, ~ rlang::expr(dplyr::if_else({{cnd}}, !!.x, NA))) + lst <- rlang::set_names(lst, derived_vars) + dat2 <- dplyr::mutate({{dat}}, !!!lst) + rm_cnd_df(dat2) +} + + diff --git a/man/condition_by.Rd b/man/condition_by.Rd new file mode 100644 index 00000000..f3d0cf7e --- /dev/null +++ b/man/condition_by.Rd @@ -0,0 +1,27 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/cnd_df.R +\name{condition_by} +\alias{condition_by} +\title{Condition a data set based on specified conditions} +\usage{ +condition_by(dat, ..., .na = NA, .env = rlang::env()) +} +\arguments{ +\item{dat}{A tibble.} + +\item{...}{Conditions to filter the tibble.} + +\item{.na}{Return value to be used when the conditions evalute to \code{NA}.} + +\item{.env}{An optional environment to look for variables involved in logical +expression passed in \code{...}. A data frame or a list can also be passed that +will be coerced to an environment internally.} +} +\value{ +A tibble with an additional class 'cnd_df' and a logical vector +attribute indicating matching rows. +} +\description{ +This function tags records in a data set, indicating which rows match the +specified conditions. +} diff --git a/man/eval_conditions.Rd b/man/eval_conditions.Rd new file mode 100644 index 00000000..3de50b96 --- /dev/null +++ b/man/eval_conditions.Rd @@ -0,0 +1,74 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/cnd_df.R +\name{eval_conditions} +\alias{eval_conditions} +\title{Evaluate conditions} +\usage{ +eval_conditions(dat, ..., .na = NA, .env = rlang::env()) +} +\arguments{ +\item{dat}{A data frame} + +\item{...}{A set of logical conditions, e.g. \verb{y & z, x | z} (\code{x}, \code{y}, \code{z} +would have to exist either as columns in \code{dat} or in the enviroment +\code{.env}). If multiple expressions are included, they are combined with the +\code{&} operator.} + +\item{.na}{Return value to be used when the conditions evalute to \code{NA}.} + +\item{.env}{An optional environment to look for variables involved in logical +expression passed in \code{...}. A data frame or a list can also be passed that +will be coerced to an environment internally.} +} +\value{ +A logical vector reflecting matching rows in \code{dat}. +} +\description{ +\code{\link[=eval_conditions]{eval_conditions()}} evaluates a set of conditions in the context of a +data frame and an optional environment. + +The utility of this function is to provide an easy way to generate a logical +vector of matching records from a set of logical conditions involving +variables in a data frame (\code{dat}) and optionally in a supplementary +environment (\code{.env}). The set of logical conditions are provided as +expressions to be evaluated in the context of \code{dat} and \code{.env}. + +Variables are looked up in \code{dat}, then in \code{.env}, then in the calling +function's environment, followed by its parent environments. +} +\examples{ +# Create a sample data frame +df <- data.frame( + x = c(1, 2, 3, 4, 5), + y = c(TRUE, FALSE, TRUE, FALSE, TRUE), + z = c("a", "b", "a", "b", "a") +) + +# Simple condition on one column +sdtm.oak:::eval_conditions(df, x > 2) + +# Combined conditions on multiple columns +sdtm.oak:::eval_conditions(df, x > 2, y) + +# Using conditions with NA handling +df_with_na <- data.frame( + x = c(1, 2, NA, 4, 5), + y = c(TRUE, FALSE, TRUE, FALSE, TRUE) +) +sdtm.oak:::eval_conditions(df_with_na, x > 2, .na = FALSE) + +# The environment where `eval_conditions()` is called is also inspected +# when evaluating conditions in `...`. +w <- 1 +sdtm.oak:::eval_conditions(df, x > w) + +# Using an environment +env <- rlang::env(w = 2) +sdtm.oak:::eval_conditions(df, x > w, .env = env) + +# In place of an environment, you may alternatively pass a list or data frame. +sdtm.oak:::eval_conditions(df, x > w, .env = list(w = 3)) +sdtm.oak:::eval_conditions(df, x > w, .env = tibble::tibble(w = 4)) + +} +\keyword{internal} diff --git a/man/new_cnd_df.Rd b/man/new_cnd_df.Rd new file mode 100644 index 00000000..f40b140b --- /dev/null +++ b/man/new_cnd_df.Rd @@ -0,0 +1,40 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/cnd_df.R +\name{new_cnd_df} +\alias{new_cnd_df} +\title{Create a data frame with filtering tags} +\usage{ +new_cnd_df(dat, cnd, .warn = TRUE) +} +\arguments{ +\item{dat}{A data frame.} + +\item{cnd}{A logical vector. Length must match the number of rows in \code{dat}.} + +\item{.warn}{Whether to warn about creating a new \emph{conditioned} data frame +in case that \code{dat} already is one.} +} +\value{ +A data frame \code{dat} with the additional class \code{"cnd_df"} and the +following attributes: +\itemize{ +\item \code{cnd}: The logical vector passed as argument \code{cnd}: \code{TRUE} values mark +rows in \code{dat} to be used for transformations; rows marked with \code{FALSE} are +not transformed; and \code{NA} mark rows whose transformations are to be applied +resulting in \code{NA}. +\item \code{cnd_sum}: An integer vector of three elements providing the sum of \code{TRUE}, +\code{FALSE} and \code{NA} values in \code{cnd}, respectively. +} +} +\description{ +\code{\link[=new_cnd_df]{new_cnd_df()}} creates a \emph{conditioned} data frame, classed \code{cnd_df}, meaning +that this function extends the data frame passed as argument by storing a +logical vector \code{cnd} (as attribute) that marks rows for posterior conditional +transformation by methods that support \emph{conditioned} data frames. +} +\examples{ +df <- data.frame(x = 1:3, y = letters[1:3]) +sdtm.oak:::new_cnd_df(dat = df, cnd = c(FALSE, NA, TRUE)) + +} +\keyword{internal} diff --git a/man/rm_cnd_df.Rd b/man/rm_cnd_df.Rd new file mode 100644 index 00000000..21f096b6 --- /dev/null +++ b/man/rm_cnd_df.Rd @@ -0,0 +1,18 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/cnd_df.R +\name{rm_cnd_df} +\alias{rm_cnd_df} +\title{Remove the cnd_df class from a data frame} +\usage{ +rm_cnd_df(dat) +} +\arguments{ +\item{dat}{A data frame.} +} +\value{ +The input \code{dat} without the 'cnd_df' class. +} +\description{ +This function removes the 'cnd_df' class, along with its attributes, if +applicable. +} diff --git a/man/tbl_sum.cnd_df.Rd b/man/tbl_sum.cnd_df.Rd new file mode 100644 index 00000000..5a0f301f --- /dev/null +++ b/man/tbl_sum.cnd_df.Rd @@ -0,0 +1,16 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/cnd_df.R +\name{tbl_sum.cnd_df} +\alias{tbl_sum.cnd_df} +\title{Print} +\usage{ +\method{tbl_sum}{cnd_df}(x, ...) +} +\arguments{ +\item{x}{A conditioned tibble of class 'cnd_df'.} + +\item{...}{Additional arguments passed to the default print method.} +} +\description{ +Blah +} diff --git a/renv/profiles/4.4/renv.lock b/renv/profiles/4.4/renv.lock new file mode 100644 index 00000000..aafeeb4b --- /dev/null +++ b/renv/profiles/4.4/renv.lock @@ -0,0 +1,1279 @@ +{ + "R": { + "Version": "4.4.0", + "Repositories": [ + { + "Name": "CRAN", + "URL": "https://cloud.r-project.org" + } + ] + }, + "Packages": { + "R6": { + "Package": "R6", + "Version": "2.5.1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R" + ], + "Hash": "470851b6d5d0ac559e9d01bb352b4021" + }, + "Rcpp": { + "Package": "Rcpp", + "Version": "1.0.12", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "methods", + "utils" + ], + "Hash": "5ea2700d21e038ace58269ecdbeb9ec0" + }, + "admiraldev": { + "Package": "admiraldev", + "Version": "1.0.0", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "dplyr", + "hms", + "lifecycle", + "lubridate", + "magrittr", + "purrr", + "rlang", + "stringr", + "tidyr", + "tidyselect" + ], + "Hash": "4ab0476ca36f502f6cdd2080f8d0f261" + }, + "askpass": { + "Package": "askpass", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "sys" + ], + "Hash": "cad6cf7f1d5f6e906700b9d3e718c796" + }, + "assertthat": { + "Package": "assertthat", + "Version": "0.2.1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "tools" + ], + "Hash": "50c838a310445e954bc13f26f26a6ecf" + }, + "base64enc": { + "Package": "base64enc", + "Version": "0.1-3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "543776ae6848fde2f48ff3816d0628bc" + }, + "bit": { + "Package": "bit", + "Version": "4.0.5", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R" + ], + "Hash": "d242abec29412ce988848d0294b208fd" + }, + "bit64": { + "Package": "bit64", + "Version": "4.0.5", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "bit", + "methods", + "stats", + "utils" + ], + "Hash": "9fe98599ca456d6552421db0d6772d8f" + }, + "brio": { + "Package": "brio", + "Version": "1.1.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "c1ee497a6d999947c2c224ae46799b1a" + }, + "bslib": { + "Package": "bslib", + "Version": "0.7.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "cachem", + "fastmap", + "grDevices", + "htmltools", + "jquerylib", + "jsonlite", + "lifecycle", + "memoise", + "mime", + "rlang", + "sass" + ], + "Hash": "8644cc53f43828f19133548195d7e59e" + }, + "cachem": { + "Package": "cachem", + "Version": "1.0.8", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "fastmap", + "rlang" + ], + "Hash": "c35768291560ce302c0a6589f92e837d" + }, + "callr": { + "Package": "callr", + "Version": "3.7.6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "processx", + "utils" + ], + "Hash": "d7e13f49c19103ece9e58ad2d83a7354" + }, + "cli": { + "Package": "cli", + "Version": "3.6.2", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "utils" + ], + "Hash": "1216ac65ac55ec0058a6f75d7ca0fd52" + }, + "clipr": { + "Package": "clipr", + "Version": "0.8.0", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "utils" + ], + "Hash": "3f038e5ac7f41d4ac41ce658c85e3042" + }, + "commonmark": { + "Package": "commonmark", + "Version": "1.9.1", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "5d8225445acb167abf7797de48b2ee3c" + }, + "cpp11": { + "Package": "cpp11", + "Version": "0.4.7", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R" + ], + "Hash": "5a295d7d963cc5035284dcdbaf334f4e" + }, + "crayon": { + "Package": "crayon", + "Version": "1.5.2", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "grDevices", + "methods", + "utils" + ], + "Hash": "e8a1e41acf02548751f45c718d55aa6a" + }, + "credentials": { + "Package": "credentials", + "Version": "2.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "askpass", + "curl", + "jsonlite", + "openssl", + "sys" + ], + "Hash": "c7844b32098dcbd1c59cbd8dddb4ecc6" + }, + "curl": { + "Package": "curl", + "Version": "5.2.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "411ca2c03b1ce5f548345d2fc2685f7a" + }, + "desc": { + "Package": "desc", + "Version": "1.4.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "cli", + "utils" + ], + "Hash": "99b79fcbd6c4d1ce087f5c5c758b384f" + }, + "diffobj": { + "Package": "diffobj", + "Version": "0.3.5", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "crayon", + "methods", + "stats", + "tools", + "utils" + ], + "Hash": "bcaa8b95f8d7d01a5dedfd959ce88ab8" + }, + "digest": { + "Package": "digest", + "Version": "0.6.35", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "698ece7ba5a4fa4559e3d537e7ec3d31" + }, + "downlit": { + "Package": "downlit", + "Version": "0.4.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "brio", + "desc", + "digest", + "evaluate", + "fansi", + "memoise", + "rlang", + "vctrs", + "withr", + "yaml" + ], + "Hash": "14fa1f248b60ed67e1f5418391a17b14" + }, + "dplyr": { + "Package": "dplyr", + "Version": "1.1.4", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "R6", + "cli", + "generics", + "glue", + "lifecycle", + "magrittr", + "methods", + "pillar", + "rlang", + "tibble", + "tidyselect", + "utils", + "vctrs" + ], + "Hash": "fedd9d00c2944ff00a0e2696ccf048ec" + }, + "evaluate": { + "Package": "evaluate", + "Version": "0.23", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "daf4a1246be12c1fa8c7705a0935c1a0" + }, + "fansi": { + "Package": "fansi", + "Version": "1.0.6", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "grDevices", + "utils" + ], + "Hash": "962174cf2aeb5b9eea581522286a911f" + }, + "fastmap": { + "Package": "fastmap", + "Version": "1.1.1", + "Source": "Repository", + "Repository": "RSPM", + "Hash": "f7736a18de97dea803bde0a2daaafb27" + }, + "fontawesome": { + "Package": "fontawesome", + "Version": "0.5.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "htmltools", + "rlang" + ], + "Hash": "c2efdd5f0bcd1ea861c2d4e2a883a67d" + }, + "fs": { + "Package": "fs", + "Version": "1.6.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "methods" + ], + "Hash": "15aeb8c27f5ea5161f9f6a641fafd93a" + }, + "generics": { + "Package": "generics", + "Version": "0.1.3", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "methods" + ], + "Hash": "15e9634c0fcd294799e9b2e929ed1b86" + }, + "gert": { + "Package": "gert", + "Version": "2.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "askpass", + "credentials", + "openssl", + "rstudioapi", + "sys", + "zip" + ], + "Hash": "f70d3fe2d9e7654213a946963d1591eb" + }, + "gh": { + "Package": "gh", + "Version": "1.4.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "gitcreds", + "glue", + "httr2", + "ini", + "jsonlite", + "lifecycle", + "rlang" + ], + "Hash": "fbbbc48eba7a6626a08bb365e44b563b" + }, + "gitcreds": { + "Package": "gitcreds", + "Version": "0.1.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "ab08ac61f3e1be454ae21911eb8bc2fe" + }, + "glue": { + "Package": "glue", + "Version": "1.7.0", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "methods" + ], + "Hash": "e0b3a53876554bd45879e596cdb10a52" + }, + "highr": { + "Package": "highr", + "Version": "0.10", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "xfun" + ], + "Hash": "06230136b2d2b9ba5805e1963fa6e890" + }, + "hms": { + "Package": "hms", + "Version": "1.1.3", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "lifecycle", + "methods", + "pkgconfig", + "rlang", + "vctrs" + ], + "Hash": "b59377caa7ed00fa41808342002138f9" + }, + "htmltools": { + "Package": "htmltools", + "Version": "0.5.8.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "base64enc", + "digest", + "fastmap", + "grDevices", + "rlang", + "utils" + ], + "Hash": "81d371a9cc60640e74e4ab6ac46dcedc" + }, + "httr": { + "Package": "httr", + "Version": "1.4.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "curl", + "jsonlite", + "mime", + "openssl" + ], + "Hash": "ac107251d9d9fd72f0ca8049988f1d7f" + }, + "httr2": { + "Package": "httr2", + "Version": "1.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "cli", + "curl", + "glue", + "lifecycle", + "magrittr", + "openssl", + "rappdirs", + "rlang", + "vctrs", + "withr" + ], + "Hash": "03d741c92fda96d98c3a3f22494e3b4a" + }, + "hunspell": { + "Package": "hunspell", + "Version": "3.0.3", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "Rcpp", + "digest" + ], + "Hash": "e957e989ea17f937964f0d46b0f0bca0" + }, + "ini": { + "Package": "ini", + "Version": "0.3.1", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "6154ec2223172bce8162d4153cda21f7" + }, + "jquerylib": { + "Package": "jquerylib", + "Version": "0.1.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "htmltools" + ], + "Hash": "5aab57a3bd297eee1c1d862735972182" + }, + "jsonlite": { + "Package": "jsonlite", + "Version": "1.8.8", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "methods" + ], + "Hash": "e1b9c55281c5adc4dd113652d9e26768" + }, + "knitr": { + "Package": "knitr", + "Version": "1.46", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "evaluate", + "highr", + "methods", + "tools", + "xfun", + "yaml" + ], + "Hash": "6e008ab1d696a5283c79765fa7b56b47" + }, + "lifecycle": { + "Package": "lifecycle", + "Version": "1.0.4", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "cli", + "glue", + "rlang" + ], + "Hash": "b8552d117e1b808b09a832f589b79035" + }, + "lubridate": { + "Package": "lubridate", + "Version": "1.9.3", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "generics", + "methods", + "timechange" + ], + "Hash": "680ad542fbcf801442c83a6ac5a2126c" + }, + "magrittr": { + "Package": "magrittr", + "Version": "2.0.3", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R" + ], + "Hash": "7ce2733a9826b3aeb1775d56fd305472" + }, + "memoise": { + "Package": "memoise", + "Version": "2.0.1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "cachem", + "rlang" + ], + "Hash": "e2817ccf4a065c5d9d7f2cfbe7c1d78c" + }, + "mime": { + "Package": "mime", + "Version": "0.12", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "tools" + ], + "Hash": "18e9c28c1d3ca1560ce30658b22ce104" + }, + "openssl": { + "Package": "openssl", + "Version": "2.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "askpass" + ], + "Hash": "2bcca3848e4734eb3b16103bc9aa4b8e" + }, + "pillar": { + "Package": "pillar", + "Version": "1.9.0", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "cli", + "fansi", + "glue", + "lifecycle", + "rlang", + "utf8", + "utils", + "vctrs" + ], + "Hash": "15da5a8412f317beeee6175fbc76f4bb" + }, + "pkgbuild": { + "Package": "pkgbuild", + "Version": "1.4.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "callr", + "cli", + "desc", + "processx" + ], + "Hash": "a29e8e134a460a01e0ca67a4763c595b" + }, + "pkgconfig": { + "Package": "pkgconfig", + "Version": "2.0.3", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "utils" + ], + "Hash": "01f28d4278f15c76cddbea05899c5d6f" + }, + "pkgdown": { + "Package": "pkgdown", + "Version": "2.0.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "bslib", + "callr", + "cli", + "desc", + "digest", + "downlit", + "fs", + "httr", + "jsonlite", + "magrittr", + "memoise", + "purrr", + "ragg", + "rlang", + "rmarkdown", + "tibble", + "whisker", + "withr", + "xml2", + "yaml" + ], + "Hash": "8bf1151ed1a48328d71b937e651117a6" + }, + "pkgload": { + "Package": "pkgload", + "Version": "1.3.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "crayon", + "desc", + "fs", + "glue", + "methods", + "pkgbuild", + "rlang", + "rprojroot", + "utils", + "withr" + ], + "Hash": "876c618df5ae610be84356d5d7a5d124" + }, + "praise": { + "Package": "praise", + "Version": "1.0.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "a555924add98c99d2f411e37e7d25e9f" + }, + "prettyunits": { + "Package": "prettyunits", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R" + ], + "Hash": "6b01fc98b1e86c4f705ce9dcfd2f57c7" + }, + "processx": { + "Package": "processx", + "Version": "3.8.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "ps", + "utils" + ], + "Hash": "0c90a7d71988856bad2a2a45dd871bb9" + }, + "progress": { + "Package": "progress", + "Version": "1.2.3", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "R6", + "crayon", + "hms", + "prettyunits" + ], + "Hash": "f4625e061cb2865f111b47ff163a5ca6" + }, + "ps": { + "Package": "ps", + "Version": "1.7.6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "utils" + ], + "Hash": "dd2b9319ee0656c8acf45c7f40c59de7" + }, + "purrr": { + "Package": "purrr", + "Version": "1.0.2", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "cli", + "lifecycle", + "magrittr", + "rlang", + "vctrs" + ], + "Hash": "1cba04a4e9414bdefc9dcaa99649a8dc" + }, + "ragg": { + "Package": "ragg", + "Version": "1.3.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "systemfonts", + "textshaping" + ], + "Hash": "e3087db406e079a8a2fd87f413918ed3" + }, + "rappdirs": { + "Package": "rappdirs", + "Version": "0.3.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "5e3c5dc0b071b21fa128676560dbe94d" + }, + "readr": { + "Package": "readr", + "Version": "2.1.5", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "R6", + "cli", + "clipr", + "cpp11", + "crayon", + "hms", + "lifecycle", + "methods", + "rlang", + "tibble", + "tzdb", + "utils", + "vroom" + ], + "Hash": "9de96463d2117f6ac49980577939dfb3" + }, + "rematch2": { + "Package": "rematch2", + "Version": "2.1.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "tibble" + ], + "Hash": "76c9e04c712a05848ae7a23d2f170a40" + }, + "renv": { + "Package": "renv", + "Version": "1.0.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "utils" + ], + "Hash": "41b847654f567341725473431dd0d5ab" + }, + "rlang": { + "Package": "rlang", + "Version": "1.1.3", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "utils" + ], + "Hash": "42548638fae05fd9a9b5f3f437fbbbe2" + }, + "rmarkdown": { + "Package": "rmarkdown", + "Version": "2.26", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "bslib", + "evaluate", + "fontawesome", + "htmltools", + "jquerylib", + "jsonlite", + "knitr", + "methods", + "tinytex", + "tools", + "utils", + "xfun", + "yaml" + ], + "Hash": "9b148e7f95d33aac01f31282d49e4f44" + }, + "rprojroot": { + "Package": "rprojroot", + "Version": "2.0.4", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R" + ], + "Hash": "4c8415e0ec1e29f3f4f6fc108bef0144" + }, + "rstudioapi": { + "Package": "rstudioapi", + "Version": "0.16.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "96710351d642b70e8f02ddeb237c46a7" + }, + "sass": { + "Package": "sass", + "Version": "0.4.9", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R6", + "fs", + "htmltools", + "rappdirs", + "rlang" + ], + "Hash": "d53dbfddf695303ea4ad66f86e99b95d" + }, + "spelling": { + "Package": "spelling", + "Version": "2.3.0", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "commonmark", + "hunspell", + "knitr", + "xml2" + ], + "Hash": "632e9e83d3dc774d361b9415b15642bb" + }, + "stringi": { + "Package": "stringi", + "Version": "1.8.3", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "stats", + "tools", + "utils" + ], + "Hash": "058aebddea264f4c99401515182e656a" + }, + "stringr": { + "Package": "stringr", + "Version": "1.5.1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "cli", + "glue", + "lifecycle", + "magrittr", + "rlang", + "stringi", + "vctrs" + ], + "Hash": "960e2ae9e09656611e0b8214ad543207" + }, + "sys": { + "Package": "sys", + "Version": "3.4.2", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "3a1be13d68d47a8cd0bfd74739ca1555" + }, + "systemfonts": { + "Package": "systemfonts", + "Version": "1.1.0", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cpp11", + "lifecycle" + ], + "Hash": "213b6b8ed5afbf934843e6c3b090d418" + }, + "testthat": { + "Package": "testthat", + "Version": "3.2.1.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "brio", + "callr", + "cli", + "desc", + "digest", + "evaluate", + "jsonlite", + "lifecycle", + "magrittr", + "methods", + "pkgload", + "praise", + "processx", + "ps", + "rlang", + "utils", + "waldo", + "withr" + ], + "Hash": "3f6e7e5e2220856ff865e4834766bf2b" + }, + "textshaping": { + "Package": "textshaping", + "Version": "0.3.7", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cpp11", + "systemfonts" + ], + "Hash": "997aac9ad649e0ef3b97f96cddd5622b" + }, + "tibble": { + "Package": "tibble", + "Version": "3.2.1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "fansi", + "lifecycle", + "magrittr", + "methods", + "pillar", + "pkgconfig", + "rlang", + "utils", + "vctrs" + ], + "Hash": "a84e2cc86d07289b3b6f5069df7a004c" + }, + "tidyr": { + "Package": "tidyr", + "Version": "1.3.1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "cli", + "cpp11", + "dplyr", + "glue", + "lifecycle", + "magrittr", + "purrr", + "rlang", + "stringr", + "tibble", + "tidyselect", + "utils", + "vctrs" + ], + "Hash": "915fb7ce036c22a6a33b5a8adb712eb1" + }, + "tidyselect": { + "Package": "tidyselect", + "Version": "1.2.1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "cli", + "glue", + "lifecycle", + "rlang", + "vctrs", + "withr" + ], + "Hash": "829f27b9c4919c16b593794a6344d6c0" + }, + "timechange": { + "Package": "timechange", + "Version": "0.3.0", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "cpp11" + ], + "Hash": "c5f3c201b931cd6474d17d8700ccb1c8" + }, + "tinytex": { + "Package": "tinytex", + "Version": "0.51", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "xfun" + ], + "Hash": "d44e2fcd2e4e076f0aac540208559d1d" + }, + "tzdb": { + "Package": "tzdb", + "Version": "0.4.0", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "cpp11" + ], + "Hash": "f561504ec2897f4d46f0c7657e488ae1" + }, + "usethis": { + "Package": "usethis", + "Version": "2.2.3", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "clipr", + "crayon", + "curl", + "desc", + "fs", + "gert", + "gh", + "glue", + "jsonlite", + "lifecycle", + "purrr", + "rappdirs", + "rlang", + "rprojroot", + "rstudioapi", + "stats", + "utils", + "whisker", + "withr", + "yaml" + ], + "Hash": "d524fd42c517035027f866064417d7e6" + }, + "utf8": { + "Package": "utf8", + "Version": "1.2.4", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R" + ], + "Hash": "62b65c52671e6665f803ff02954446e9" + }, + "vctrs": { + "Package": "vctrs", + "Version": "0.6.5", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "cli", + "glue", + "lifecycle", + "rlang" + ], + "Hash": "c03fa420630029418f7e6da3667aac4a" + }, + "vroom": { + "Package": "vroom", + "Version": "1.6.5", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "bit64", + "cli", + "cpp11", + "crayon", + "glue", + "hms", + "lifecycle", + "methods", + "progress", + "rlang", + "stats", + "tibble", + "tidyselect", + "tzdb", + "vctrs", + "withr" + ], + "Hash": "390f9315bc0025be03012054103d227c" + }, + "waldo": { + "Package": "waldo", + "Version": "0.5.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "diffobj", + "fansi", + "glue", + "methods", + "rematch2", + "rlang", + "tibble" + ], + "Hash": "c7d3fd6d29ab077cbac8f0e2751449e6" + }, + "whisker": { + "Package": "whisker", + "Version": "0.4.1", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "c6abfa47a46d281a7d5159d0a8891e88" + }, + "withr": { + "Package": "withr", + "Version": "3.0.0", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "grDevices", + "graphics" + ], + "Hash": "d31b6c62c10dcf11ec530ca6b0dd5d35" + }, + "xfun": { + "Package": "xfun", + "Version": "0.44", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "grDevices", + "stats", + "tools" + ], + "Hash": "317a0538d32f4a009658bcedb7923f4b" + }, + "xml2": { + "Package": "xml2", + "Version": "1.3.6", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "cli", + "methods", + "rlang" + ], + "Hash": "1d0336142f4cd25d8d23cd3ba7a8fb61" + }, + "yaml": { + "Package": "yaml", + "Version": "2.3.8", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "29240487a071f535f5e5d5a323b7afbd" + }, + "zip": { + "Package": "zip", + "Version": "2.3.1", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "fcc4bd8e6da2d2011eb64a5e5cc685ab" + } + } +} diff --git a/renv/profiles/4.4/renv/.gitignore b/renv/profiles/4.4/renv/.gitignore new file mode 100644 index 00000000..0ec0cbba --- /dev/null +++ b/renv/profiles/4.4/renv/.gitignore @@ -0,0 +1,7 @@ +library/ +local/ +cellar/ +lock/ +python/ +sandbox/ +staging/