From 94ca7ffdf47d66b33ad6b867d8581362431c6b9b Mon Sep 17 00:00:00 2001 From: Dawid Kaledkowski Date: Tue, 27 May 2025 11:34:51 +0200 Subject: [PATCH 1/8] WIP --- R/teal_data-class.R | 23 ++++++++--------------- R/teal_data-constructor.R | 3 +-- man/teal_data-class.Rd | 15 --------------- man/teal_data.Rd | 2 +- 4 files changed, 10 insertions(+), 33 deletions(-) diff --git a/R/teal_data-class.R b/R/teal_data-class.R index 46baa004b..db74165e2 100644 --- a/R/teal_data-class.R +++ b/R/teal_data-class.R @@ -35,11 +35,7 @@ setOldClass("join_keys") setClass( Class = "teal_data", contains = "qenv", - slots = c(join_keys = "join_keys", verified = "logical"), - prototype = list( - join_keys = join_keys(), - verified = logical(0) - ) + slots = c(join_keys = "join_keys", verified = "logical") ) #' It initializes the `teal_data` class @@ -50,13 +46,13 @@ setClass( setMethod( "initialize", "teal_data", - function(.Object, .xData = list(), join_keys = join_keys(), code = list(), ...) { # nolint: object_name. - # Allow .xData to be a list and convert it to an environment - if (!missing(.xData) && inherits(.xData, "list")) { - .xData <- rlang::env_clone(list2env(.xData), parent = parent.env(.GlobalEnv)) # nolint: object_name. - lockEnvironment(.xData, bindings = TRUE) - } + function(.Object, .xData, join_keys, code, ...) { # nolint: object_name. + print("init teal_data") + if (missing(.xData)) .xData <- new.env() + if (missing(join_keys)) join_keys <- teal.data::join_keys() + if (missing(code)) code <- character(0L) args <- list(...) + checkmate::assert_environment(.xData) checkmate::assert_class(join_keys, "join_keys") checkmate::assert_list(args, names = "named") @@ -67,15 +63,12 @@ setMethod( if (is.language(code)) { code <- paste(lang2calls(code), collapse = "\n") } - if (length(code)) { - code <- paste(code, collapse = "\n") - } methods::callNextMethod( .Object, .xData, join_keys = join_keys, - verified = (length(args$code) == 0L && length(.xData) == 0L), + verified = (length(code) == 0L && length(.xData) == 0L), code = code2list(code), ... ) diff --git a/R/teal_data-constructor.R b/R/teal_data-constructor.R index 25a705342..2c308f723 100644 --- a/R/teal_data-constructor.R +++ b/R/teal_data-constructor.R @@ -48,13 +48,12 @@ teal_data <- function(..., if (inherits(join_keys, "join_key_set")) { join_keys <- teal.data::join_keys(join_keys) } - if (length(data_objects) > 0 && !checkmate::test_names(names(data_objects), type = "named")) { stop("Dot (`...`) arguments on `teal_data()` must be named.") } methods::new( "teal_data", - .xData = data_objects, + .xData = list2env(data_objects), code = code, join_keys = join_keys ) diff --git a/man/teal_data-class.Rd b/man/teal_data-class.Rd index d046b4bd3..a232a40ca 100644 --- a/man/teal_data-class.Rd +++ b/man/teal_data-class.Rd @@ -36,19 +36,4 @@ proven to yield contents of \verb{@.xData}. Used internally. See \code{\link[=verify]{verify()}} for more details.} }} -\section{Code}{ - - - -Each code element is a character representing one call. Each element is named with the random -identifier to make sure uniqueness when joining. Each element has possible attributes: -\itemize{ -\item \code{warnings} (\code{character}) the warnings output when evaluating the code element. -\item \code{messages} (\code{character}) the messages output when evaluating the code element. -\item \code{dependency} (\code{character}) names of objects that appear in this call and gets affected by this call, -separated by \verb{<-} (objects on LHS of \verb{<-} are affected by this line, and objects on RHS are affecting this line). -} - -} - \keyword{internal} diff --git a/man/teal_data.Rd b/man/teal_data.Rd index a53804d60..e7cf04588 100644 --- a/man/teal_data.Rd +++ b/man/teal_data.Rd @@ -41,7 +41,7 @@ A \code{teal_data} is meant to be used for reproducibility purposes. The class i \item It inherits from the environment and methods such as \code{\link{$}}, \code{\link[=get]{get()}}, \code{\link[=ls]{ls()}}, \code{\link[=as.list]{as.list()}}, \code{\link[=parent.env]{parent.env()}} work out of the box. \item \code{teal_data} is a locked environment, and data modification is only possible through the -\code{\link[teal.code:eval_code]{teal.code::eval_code()}} and \code{\link[teal.code:eval_code]{within.qenv()}} functions. +\code{\link[teal.code:eval_code]{teal.code::eval_code()}} and \code{\link[teal.code:within.qenv]{within.qenv()}} functions. \item It stores metadata about the code used to create the data (see \code{\link[=get_code,teal_data-method]{get_code()}}). \item It supports slicing (see \code{\link[teal.code:subset-qenv]{teal.code::subset-qenv}}) \item Is immutable which means that each code evaluation does not modify the original \code{teal_data} From 246bd309154d1d892711238c5ab95aef167df8d5 Mon Sep 17 00:00:00 2001 From: Dawid Kaledkowski Date: Tue, 27 May 2025 12:05:54 +0200 Subject: [PATCH 2/8] roxy --- man/teal_data-class.Rd | 15 +++++++++++++++ man/teal_data.Rd | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/man/teal_data-class.Rd b/man/teal_data-class.Rd index a232a40ca..d046b4bd3 100644 --- a/man/teal_data-class.Rd +++ b/man/teal_data-class.Rd @@ -36,4 +36,19 @@ proven to yield contents of \verb{@.xData}. Used internally. See \code{\link[=verify]{verify()}} for more details.} }} +\section{Code}{ + + + +Each code element is a character representing one call. Each element is named with the random +identifier to make sure uniqueness when joining. Each element has possible attributes: +\itemize{ +\item \code{warnings} (\code{character}) the warnings output when evaluating the code element. +\item \code{messages} (\code{character}) the messages output when evaluating the code element. +\item \code{dependency} (\code{character}) names of objects that appear in this call and gets affected by this call, +separated by \verb{<-} (objects on LHS of \verb{<-} are affected by this line, and objects on RHS are affecting this line). +} + +} + \keyword{internal} diff --git a/man/teal_data.Rd b/man/teal_data.Rd index e7cf04588..a53804d60 100644 --- a/man/teal_data.Rd +++ b/man/teal_data.Rd @@ -41,7 +41,7 @@ A \code{teal_data} is meant to be used for reproducibility purposes. The class i \item It inherits from the environment and methods such as \code{\link{$}}, \code{\link[=get]{get()}}, \code{\link[=ls]{ls()}}, \code{\link[=as.list]{as.list()}}, \code{\link[=parent.env]{parent.env()}} work out of the box. \item \code{teal_data} is a locked environment, and data modification is only possible through the -\code{\link[teal.code:eval_code]{teal.code::eval_code()}} and \code{\link[teal.code:within.qenv]{within.qenv()}} functions. +\code{\link[teal.code:eval_code]{teal.code::eval_code()}} and \code{\link[teal.code:eval_code]{within.qenv()}} functions. \item It stores metadata about the code used to create the data (see \code{\link[=get_code,teal_data-method]{get_code()}}). \item It supports slicing (see \code{\link[teal.code:subset-qenv]{teal.code::subset-qenv}}) \item Is immutable which means that each code evaluation does not modify the original \code{teal_data} From 427586b9224edf8f69d3b0b228a325163218e938 Mon Sep 17 00:00:00 2001 From: Dawid Kaledkowski Date: Tue, 27 May 2025 13:01:36 +0200 Subject: [PATCH 3/8] fix split_code error --- R/teal_data-class.R | 1 + 1 file changed, 1 insertion(+) diff --git a/R/teal_data-class.R b/R/teal_data-class.R index db74165e2..56d3538b7 100644 --- a/R/teal_data-class.R +++ b/R/teal_data-class.R @@ -90,6 +90,7 @@ code2list <- function(code) { if (length(code) == 0) { return(list()) } + code <- paste(code, collapse = "\n") parsed_code <- parse(text = code, keep.source = TRUE) From bdcba9c4b0de0c0c40d8ecb838a9034f7994a036 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Ver=C3=ADssimo?= <211358+averissimo@users.noreply.github.com> Date: Thu, 29 May 2025 14:48:51 +0100 Subject: [PATCH 4/8] cleanup: remove print debug --- R/teal_data-class.R | 1 - 1 file changed, 1 deletion(-) diff --git a/R/teal_data-class.R b/R/teal_data-class.R index 56d3538b7..0b9d6918d 100644 --- a/R/teal_data-class.R +++ b/R/teal_data-class.R @@ -47,7 +47,6 @@ setMethod( "initialize", "teal_data", function(.Object, .xData, join_keys, code, ...) { # nolint: object_name. - print("init teal_data") if (missing(.xData)) .xData <- new.env() if (missing(join_keys)) join_keys <- teal.data::join_keys() if (missing(code)) code <- character(0L) From 98bd4cafcfdc2337571c2959c948ed1c38fa0c65 Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Wed, 4 Jun 2025 10:51:34 +0200 Subject: [PATCH 5/8] change message in show method for `teal_data` (#372) I've suggested "code verified/unverified" because: 1. It's concise and clear 2. It focuses on what's actually being verified (the code that created the data) 3. It removes the redundant "teal_data object" since this is already known from the context --- R/teal_data-show.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/teal_data-show.R b/R/teal_data-show.R index 4ce54d8b8..96f10acc2 100644 --- a/R/teal_data-show.R +++ b/R/teal_data-show.R @@ -12,9 +12,9 @@ #' @export setMethod("show", signature = "teal_data", function(object) { if (object@verified) { - cat("\u2705\ufe0e", "verified teal_data object\n") + cat("\u2705\ufe0e", "code verified\n") } else { - cat("\u2716", "unverified teal_data object\n") + cat("\u2716", "code unverified\n") } methods::callNextMethod(object) invisible(object) From 243acf4fca8a592b344bd0b93374a2c8f7481996 Mon Sep 17 00:00:00 2001 From: m7pr Date: Wed, 4 Jun 2025 14:20:09 +0200 Subject: [PATCH 6/8] append_join_keys to teal_data object --- DESCRIPTION | 1 + NAMESPACE | 1 + R/join_keys-append.R | 31 +++++++++++++++++++++++++++++++ man/append_join_keys.Rd | 35 +++++++++++++++++++++++++++++++++++ 4 files changed, 68 insertions(+) create mode 100644 R/join_keys-append.R create mode 100644 man/append_join_keys.Rd diff --git a/DESCRIPTION b/DESCRIPTION index d3e76b0b6..923c91047 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -63,6 +63,7 @@ Collate: 'deprecated.R' 'dummy_function.R' 'join_key.R' + 'join_keys-append.R' 'join_keys-c.R' 'join_keys-extract.R' 'join_keys-names.R' diff --git a/NAMESPACE b/NAMESPACE index 92d44016a..f2159c7eb 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -25,6 +25,7 @@ export("col_labels<-") export("datanames<-") export("join_keys<-") export("parents<-") +export(append_join_keys) export(cdisc_data) export(col_labels) export(col_relabel) diff --git a/R/join_keys-append.R b/R/join_keys-append.R new file mode 100644 index 000000000..104070c10 --- /dev/null +++ b/R/join_keys-append.R @@ -0,0 +1,31 @@ +#' Append elements to join_keys +#' +#' This function appends elements to the join_keys slot of a teal_data object. +#' It follows the same pattern as base R's append() function. +#' +#' @param x A teal_data object +#' @param values (`join_keys`) object to append +#' @param after Integer, the position after which the elements are to be appended. +#' If negative or zero, the values are prepended to the join_keys. +#' If missing, the values are appended at the end. +#' +#' @return A teal_data object with updated join_keys +#' +#' @examples +#' data <- teal_data() +#' keys1 <- join_keys( +#' join_key("ADSL", "ADSL", "USUBJID"), +#' join_key("ADAE", "ADAE", "USUBJID"), +#' join_key("ADSL", "ADAE", c(USUBJID = "USUBJID")) +#' ) +#' data <- append_join_keys(data, keys1) +#' join_keys(data)data +#' +#' @export +append_join_keys <- function(x, values, after = length(join_keys(x))) { + checkmate::assert_class(x, "teal_data") + checkmate::assert_int(after, lower = 0, upper = length(join_keys(x)), null.ok = TRUE) + + join_keys(x) <- append(join_keys(x), values, after = after) + x +} diff --git a/man/append_join_keys.Rd b/man/append_join_keys.Rd new file mode 100644 index 000000000..d152b19db --- /dev/null +++ b/man/append_join_keys.Rd @@ -0,0 +1,35 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/join_keys-append.R +\name{append_join_keys} +\alias{append_join_keys} +\title{Append elements to join_keys} +\usage{ +append_join_keys(x, values, after = length(join_keys(x))) +} +\arguments{ +\item{x}{A teal_data object} + +\item{values}{(\code{join_keys}) object to append} + +\item{after}{Integer, the position after which the elements are to be appended. +If negative or zero, the values are prepended to the join_keys. +If missing, the values are appended at the end.} +} +\value{ +A teal_data object with updated join_keys +} +\description{ +This function appends elements to the join_keys slot of a teal_data object. +It follows the same pattern as base R's append() function. +} +\examples{ +data <- teal_data() +keys1 <- join_keys( + join_key("ADSL", "ADSL", "USUBJID"), + join_key("ADAE", "ADAE", "USUBJID"), + join_key("ADSL", "ADAE", c(USUBJID = "USUBJID")) +) +data <- append_join_keys(data, keys1) +join_keys(data)data + +} From 6d198da7ec4a2f839ac33e571c86094ab7fcf0ba Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Fri, 6 Jun 2025 09:43:33 +0200 Subject: [PATCH 7/8] Update R/join_keys-append.R MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: André Veríssimo <211358+averissimo@users.noreply.github.com> Signed-off-by: Marcin <133694481+m7pr@users.noreply.github.com> --- R/join_keys-append.R | 1 + 1 file changed, 1 insertion(+) diff --git a/R/join_keys-append.R b/R/join_keys-append.R index 104070c10..cd0603efb 100644 --- a/R/join_keys-append.R +++ b/R/join_keys-append.R @@ -25,6 +25,7 @@ append_join_keys <- function(x, values, after = length(join_keys(x))) { checkmate::assert_class(x, "teal_data") checkmate::assert_int(after, lower = 0, upper = length(join_keys(x)), null.ok = TRUE) + checkmate::assert_class(value, "join_keys") join_keys(x) <- append(join_keys(x), values, after = after) x From 3c224c68c47ef04c0d8d8e4c3c127051abc2eb4f Mon Sep 17 00:00:00 2001 From: Marcin <133694481+m7pr@users.noreply.github.com> Date: Fri, 6 Jun 2025 09:43:38 +0200 Subject: [PATCH 8/8] Update R/join_keys-append.R MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: André Veríssimo <211358+averissimo@users.noreply.github.com> Signed-off-by: Marcin <133694481+m7pr@users.noreply.github.com> --- R/join_keys-append.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/join_keys-append.R b/R/join_keys-append.R index cd0603efb..ba0b708d4 100644 --- a/R/join_keys-append.R +++ b/R/join_keys-append.R @@ -19,7 +19,7 @@ #' join_key("ADSL", "ADAE", c(USUBJID = "USUBJID")) #' ) #' data <- append_join_keys(data, keys1) -#' join_keys(data)data +#' join_keys(data) #' #' @export append_join_keys <- function(x, values, after = length(join_keys(x))) {