---
title: "qenv"
author: "NEST coreDev"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{qenv}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

## Introduction to `qenv`

A `qenv` is an R object which contains code and an environment and can be used to create reproducible outputs.

### Initialization

The `qenv()` function serves as the gateway to create an initial `qenv` object:

```{r}
library(teal.code)

# create a new qenv object
empty_qenv <- qenv()
print(empty_qenv)
```

### `qenv` basic usage

The `eval_code()` function executes code within a `qenv` environment, yielding a new `qenv` object as the output. 

```{r}
library(magrittr)

# evaluate code in qenv
my_qenv <- eval_code(empty_qenv, "x <- 2")
print(my_qenv)
get_env(my_qenv)


q1 <- eval_code(my_qenv, "y <- x * 2") %>% eval_code("z <- y * 2")

# my_qenv still contains only x
print(my_qenv)
ls(get_env(my_qenv))

# q1 contains x, y and z
print(q1)
ls(get_env(q1))
```

The same result can be achieved with the `within` method for the `qenv` class.
```{r}
q2 <- within(my_qenv, y <- x * 2) %>% within(z <- y * 2)
print(q2)
```




To extract objects from a `qenv`, use `[[`; this is particularly useful for displaying them in a `shiny` app. You can retrieve the code used to generate the `qenv` using the `get_code()` function.

```{r}
print(q2[["y"]])

cat(get_code(q2))
```

### Substitutions
In some cases, one may want to substitute some elements of the code before evaluation. 
Consider a case when a subset of `iris` is defined by an input value.
```{r}
q <- qenv()
q <- eval_code(q, quote(i <- subset(iris, Species == "setosa")))
q <- eval_code(q, substitute(
  ii <- subset(iris, Species == species),
  env = list(species = "versicolor")
))
input_value <- "virginica"
q <- eval_code(q, substitute(
  iii <- subset(iris, Species == species),
  env = list(species = input_value)
))

summary(q[["i"]]$Species)
summary(q[["ii"]]$Species)
summary(q[["iii"]]$Species)
```

A more convenient way to pass code with substitution is to use the `within` method.
```{r}
qq <- qenv()
qq <- within(qq, i <- subset(iris, Species == "setosa"))
qq <- within(qq, ii <- subset(iris, Species == species), species = "versicolor")
input_value <- "virginica"
qq <- within(qq, iii <- subset(iris, Species == species), species = input_value)

summary(qq[["i"]]$Species)
summary(qq[["ii"]]$Species)
summary(qq[["iii"]]$Species)
```

See `?qenv` for more details.


### Combining `qenv` objects

Given a pair of `qenv` objects, you may be able to "join" them, creating a new `qenv` object encompassing the union of both environments, along with the requisite code for reproduction:

```{r}
common_q <- eval_code(qenv(), quote(x <- 1))

x_q <- eval_code(common_q, quote(y <- 5))
y_q <- eval_code(common_q, quote(z <- 5))

join_q <- join(x_q, y_q)

print(join_q)
ls(get_env(join_q))
```

The feasibility of joining `qenv` objects hinges on the contents of the environments and the code's order. Refer to the function documentation for further details.

### Warnings and messages in `qenv` objects

In cases where warnings or messages arise while evaluating code within a `qenv` environment, these are captured and stored within the `qenv` object. Access these messages and warnings using the `@` operator.

```{r}
q_message <- eval_code(qenv(), quote(message("this is a message")))
q_message@messages

q_warning <- eval_code(qenv(), quote(warning("and this is a warning")))
q_warning@warnings
```

If a particular line of code doesn't trigger any warnings or messages, the corresponding message/warning value will be an empty string.

```{r}
q_message@warnings
q_warning@messages
```

Additionally, a helper function, `get_warnings()`, is available to generate a formatted string comprising the warnings and the code responsible for generating them. It returns `NULL` when no warnings are present.

## Utilizing `qenv` inside `shiny` applications

These functions can be seamlessly integrated into `shiny` applications to produce reproducible outputs. In the example below, the `rcode` section showcases the code employed for generating the output.

When employing a `qenv` to evaluate code, should an error occur, an object of type `qenv.error` is generated. This object can be utilized wherever a `qenv` object is used, alleviating the need for code alterations to handle these errors. Select the `error_option` in the example below to witness `qenv` error handling in action.

```{r}
library(shiny)
library(magrittr)
# create an initial qenv with the data in
data_q <- qenv() %>% eval_code("iris_data <- iris")

ui <- fluidPage(
  radioButtons(
    "option", "Choose a column to plot:",
    c("Sepal.Length", "Sepal.Width", "Petal.Length", "Petal.Width", "error_option")
  ),
  verbatimTextOutput("rcode"),
  plotOutput("plot")
)

server <- function(input, output, session) {
  # create a qenv containing the reproducible output
  output_q <- reactive({
    req(input$option)
    eval_code(
      data_q,
      bquote(p <- hist(iris_data[, .(input$option)]))
    )
  })

  # display output
  output$plot <- renderPlot(output_q()[["p"]])
  # display code
  output$rcode <- renderText(get_code(output_q()))
}

if (interactive()) {
  shinyApp(ui, server)
}
```


## `qenv` and `teal` applications

The versatile `qenv` object can seamlessly integrate into teal modules. Explore the teal vignette [Creating Custom Modules](https://insightsengineering.github.io/teal/latest-tag/articles/creating-custom-modules.html) for detailed guidance.
