---
title: "Data Transformations as shiny Module"
author: "NEST CoreDev"
output: 
  rmarkdown::html_vignette:
    toc: true
vignette: >
  %\VignetteIndexEntry{Data Transformations as shiny Module}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

## Introduction

`teal` version `0.16` introduced new argument in `teal::module` called `transformers`. 
This argument allows to pass a `list` of `teal_data_module` objects that are created using `teal_transform_module()` function.

The main benefit of `teal_transform_module()` is the ability to transform data before passing it 
to the module. This feature allows to extend the regular behavior of existing modules by specifying custom data operations on data inside this module.

`teal_transform_module()` is a Shiny module that takes `ui` and `server` arguments. When provided, `teal` will execute data transformations for the specified module when it is loaded and whenever the data changes. `server` extend the logic behind data manipulations, where `ui` extends filter panel with new UI elements that orchestrate the transformer inputs.

This vignette presents the way on how to manage custom data transformations in `teal` apps.

## Creating your first custom data transformation module

We initialize a simple `teal` app where we pass `iris` and `mtcars` as the input datasets.

```{r, message = FALSE, warning = FALSE}
library(teal)
```

```{r}
data <- within(teal_data(), {
  iris <- iris
  mtcars <- mtcars
})

app <- init(
  data = data,
  modules = teal::example_module()
)

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

### Single Transformer

Let's create a simple `teal_transform_module` that returns the first `n` number of rows of `iris` based on the user input.

We do this by creating the `ui` with the `numericInput` for the user to input the number of rows to be displayed.
In the `server` function we take in the reactive `data` and perform this transformation and return the new reactive `data`.

```{r}
data <- within(teal_data(), {
  iris <- iris
  mtcars <- mtcars
})
datanames(data) <- c("iris", "mtcars")

my_transformers <- list(
  teal_transform_module(
    label = "Custom transform for iris",
    ui = function(id) {
      ns <- NS(id)
      tags$div(
        numericInput(ns("n_rows"), "Number of rows to subset", value = 6, min = 1, max = 150, step = 1)
      )
    },
    server = function(id, data) {
      moduleServer(id, function(input, output, session) {
        reactive({
          within(data(),
            {
              iris <- head(iris, num_rows)
            },
            num_rows = input$n_rows
          )
        })
      })
    }
  )
)

app <- init(
  data = data,
  modules = teal::example_module(transformers = my_transformers)
)

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

_Note_: It is recommended to return `reactive()` with `teal_data()` in `server` code of a `teal_transform_module` as this is more robust for maintaining the reactivity of Shiny.
If you are planning on using `eventReactive()` in the server, the event should include `data()` _(example `eventReactive(list(input$a, data()), {...})`)_.
More in [this discussion](https://github.com/insightsengineering/teal/issues/1303#issuecomment-2286239832).

### Multiple Transformers

Note that we can add multiple `teal` transformers by including `teal_transform_module` in a list.

Let's add another transformation to the `mtcars` dataset that creates a column with `rownames` of `mtcars`.
Note that this module does not have interactive UI elements.

```{r}
data <- within(teal_data(), {
  iris <- iris
  mtcars <- mtcars
})
datanames(data) <- c("iris", "mtcars")

my_transformers <- list(
  teal_transform_module(
    label = "Custom transform for iris",
    ui = function(id) {
      ns <- NS(id)
      tags$div(
        numericInput(ns("n_rows"), "Number of rows to subset", value = 6, min = 1, max = 150, step = 1)
      )
    },
    server = function(id, data) {
      moduleServer(id, function(input, output, session) {
        reactive({
          within(data(),
            {
              iris <- head(iris, num_rows)
            },
            num_rows = input$n_rows
          )
        })
      })
    }
  ),
  teal_transform_module(
    label = "Custom transform for mtcars",
    ui = function(id) {
      ns <- NS(id)
      tags$div(
        "Adding rownames column to mtcars"
      )
    },
    server = function(id, data) {
      moduleServer(id, function(input, output, session) {
        reactive({
          within(data(), {
            mtcars$rownames <- rownames(mtcars)
            rownames(mtcars) <- NULL
          })
        })
      })
    }
  )
)

app <- init(
  data = data,
  modules = teal::example_module(transformers = my_transformers)
)

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