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

```{r setup, include=FALSE}
library(teal.modules.clinical)
```


## Introduction

The outputs produced by `teal` modules, like graphs or tables, are created by the module developer and look a certain way.
It is hard to design an output that will satisfy every possible user, so the form of the output should be considered a default value that can be customized.
Here we describe the concept of _decoration_, enabling the app developer to tailor outputs to their specific requirements without rewriting the original module code.

The decoration process is build upon transformation procedures, introduced in `teal`.
While `transformators` are meant to edit module's input, decorators are meant to adjust the module's output.
To distinguish the difference, modules in  `teal.modules.clinical` have 2 separate parameters:
`transformators` and `decorators`. 

To get a complete understanding refer the following vignettes:

- Transforming the input data in
[this vignette](https://insightsengineering.github.io/teal/latest-tag/articles/transform-input-data.html).
- Transforming module output in [this vignette](https://insightsengineering.github.io/teal/latest-tag/articles/transform-module-output.html).

## Outputs that can be decorated

It is important to note which output objects from a given module can be decorated.
The module function documentation's _Decorating Module_ section has this information.

You can also refer the table shown below to know which module outputs can be decorated.

| Module                           | Outputs (Class)                                                                                           |
|----------------------------------|---------------------------------------------------------------------------------------------------------|
| `tm_a_gee`                      | table (ElementaryTable)                                                                                |
| `tm_a_mmrm`                     | lsmeans_table (TableTree), lsmeans_plot (ggplot), covariance_table (ElementaryTable), fixed_effects_table (ElementaryTable), diagnostic_table (ElementaryTable), diagnostic_plot (ggplot) |
| `tm_g_barchart_simple`          | plot (ggplot)                                                                                          |
| `tm_g_ci`                       | plot (ggplot)                                                                                          |
| `tm_g_forest_rsp`               | plot (ggplot)                                                                                          |
| `tm_g_forest_tte`               | plot (ggplot)                                                                                          |
| `tm_g_ipp`                      | plot (ggplot)                                                                                          |
| `tm_g_km`                       | plot (ggplot)                                                                                          |
| `tm_g_lineplot`                 | plot (ggplot)                                                                                          |
| `tm_g_pp_adverse_events`        | table (datatables), plot (ggplot)                                                                      |
| `tm_g_pp_patient_timeline`      | plot (ggplot)                                                                                          |
| `tm_g_pp_therapy`               | plot (ggplot), table (datatables)                                                                                          |
| `tm_g_pp_vitals`                | plot (ggplot)                                                                                          |
| `tm_t_abnormality`              | table (TableTree)                                                                                      |
| `tm_t_abnormality_by_worst_grade` | table (TableTree)                                                                                    |
| `tm_t_ancova`                   | table (TableTree)                                                                                      |
| `tm_t_binary_outcome`           | table (TableTree)                                                                                      |
| `tm_t_coxreg`                   | table (TableTree)                                                                                      |
| `tm_t_events`                   | table (TableTree)                                                                                      |
| `tm_t_events_by_grade`          | table (TableTree)                                                                                      |
| `tm_t_events_patyear`           | table (ElementaryTable)                                                                                |
| `tm_t_events_summary`           | table (TableTree)                                                                                      |
| `tm_t_exposure`                 | table (ElementaryTable)                                                                                |
| `tm_t_logistic`                 | table (TableTree)                                                                                      |
| `tm_t_mult_events`              | table (TableTree)                                                                                      |
| `tm_t_pp_basic_info`            | table (datatables)                                                                                    |
| `tm_t_pp_laboratory`            | table (datatables)                                                                                    |
| `tm_t_pp_medical_history`       | table (TableTree)                                                                                      |
| `tm_t_pp_prior_medication`      | table (datatables)                                                                                    |
| `tm_t_shift_by_arm`             | table (TableTree)                                                                                      |
| `tm_t_shift_by_arm_by_worst`    | table (TableTree)                                                                                      |
| `tm_t_shift_by_grade`           | table (TableTree)                                                                                      |
| `tm_t_smq`                      | table (TableTree)                                                                                      |
| `tm_t_summary`                  | table (TableTree)                                                                                      |
| `tm_t_summary_by`               | table (TableTree)                                                                                      |
| `tm_t_tte`                      | table (TableTree)                                                                                      |


Also, note that there are three different types of objects that can be decorated:

1. `listing_df`, `ElementaryTable`, `TableTree`
2. `ggplot`
3. `datatables`

*Tip:* A general tip before trying to decorate the output from the module is to
copy the reproducible code and running them in a separate R session to
quickly iterate the decoration you want.

## Decorating `listing_df`, `ElementaryTable`, `TableTree`

Here's an example to showcase how you can edit an output of class `listing_df`, `ElementaryTable`, or `TableTree`.
All these classes are extension of objects created using `rtables` and
can be modified with the help of `rtables` modifiers like `rtables::insert_rrow`.

```{r decorate_listing_df, message=FALSE}
library(teal.modules.clinical)

data <- within(teal_data(), {
  library(dplyr)
  ADSL <- tmc_ex_adsl |>
    mutate(
      ITTFL = factor("Y") |> with_label("Intent-To-Treat Population Flag")
    ) |>
    mutate(DTHFL = case_when(!is.na(DTHDT) ~ "Y", TRUE ~ "") |> with_label("Subject Death Flag"))


  ADLB <- tmc_ex_adlb |>
    mutate(AVISIT == forcats::fct_reorder(AVISIT, AVISITN, min)) |>
    mutate(
      ONTRTFL = case_when(
        AVISIT %in% c("SCREENING", "BASELINE") ~ "",
        TRUE ~ "Y"
      ) |> with_label("On Treatment Record Flag")
    )
})
join_keys(data) <- default_cdisc_join_keys[names(data)]

insert_rrow_decorator <- function(default_caption = "I am a good new row") {
  teal_transform_module(
    label = "New row",
    ui = function(id) {
      shiny::textInput(shiny::NS(id, "new_row"), "New row", value = default_caption)
    },
    server = function(id, data) {
      moduleServer(id, function(input, output, session) {
        reactive({
          data() |>
            within(
              {
                table <- rtables::insert_rrow(table, rtables::rrow(new_row))
              },
              new_row = input$new_row
            )
        })
      })
    }
  )
}

app <- init(
  data = data,
  modules = modules(
    tm_t_abnormality(
      label = "tm_t_abnormality",
      dataname = "ADLB",
      arm_var = choices_selected(
        choices = variable_choices("ADSL", subset = c("ARM", "ARMCD")),
        selected = "ARM"
      ),
      add_total = FALSE,
      by_vars = choices_selected(
        choices = variable_choices("ADLB", subset = c("LBCAT", "PARAM", "AVISIT")),
        selected = c("LBCAT", "PARAM"),
        keep_order = TRUE
      ),
      baseline_var = choices_selected(
        variable_choices("ADLB", subset = "BNRIND"),
        selected = "BNRIND", fixed = TRUE
      ),
      grade = choices_selected(
        choices = variable_choices("ADLB", subset = "ANRIND"),
        selected = "ANRIND",
        fixed = TRUE
      ),
      abnormal = list(low = "LOW", high = "HIGH"),
      exclude_base_abn = FALSE,
      decorators = list(table = insert_rrow_decorator("I am a good new row"))
    )
  )
)

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

```{r shinylive_iframe_1, echo = FALSE, out.width = '150%', out.extra = 'style = "position: relative; z-index:1"', eval = requireNamespace("roxy.shinylive", quietly = TRUE) && knitr::is_html_output() && identical(Sys.getenv("IN_PKGDOWN"), "true")}
code <- paste0(c(
  "interactive <- function() TRUE",
  knitr::knit_code$get("setup"),
  knitr::knit_code$get("decorate_listing_df")
), collapse = "\n")

url <- roxy.shinylive::create_shinylive_url(code)
knitr::include_url(url, height = "800px")
```

## Decorating `ggplot`

Here's an example to showcase how you can edit an output of class `ggplot`.
You can extend them using `ggplot2` functions.

```{r decorate_ggplot, message=FALSE}
library(teal.modules.clinical)

data <- teal_data(join_keys = default_cdisc_join_keys[c("ADSL", "ADRS")])
data <- within(data, {
  require(nestcolor)
  ADSL <- rADSL
  ADTTE <- tmc_ex_adtte
})
join_keys(data) <- default_cdisc_join_keys[names(data)]


ggplot_caption_decorator <- function(default_caption = "I am a good decorator") {
  teal_transform_module(
    label = "Caption",
    ui = function(id) {
      shiny::textInput(shiny::NS(id, "title"), "Plot Title", value = default_caption)
    },
    server = function(id, data) {
      moduleServer(id, function(input, output, session) {
        reactive({
          data() |>
            within(
              {
                plot <- plot +
                  ggplot2::ggtitle(title) +
                  cowplot::theme_cowplot()
              },
              title = input$title
            )
        })
      })
    }
  )
}

app <- init(
  data = data,
  modules = modules(
    tm_g_km(
      label = "tm_g_km",
      dataname = "ADTTE",
      arm_var = choices_selected(
        variable_choices("ADSL", c("ARM", "ARMCD", "ACTARMCD")),
        "ARM"
      ),
      paramcd = choices_selected(
        value_choices("ADTTE", "PARAMCD", "PARAM"),
        "OS"
      ),
      arm_ref_comp = list(
        ACTARMCD = list(ref = "ARM B", comp = c("ARM A", "ARM C")),
        ARM = list(ref = "B: Placebo", comp = c("A: Drug X", "C: Combination"))
      ),
      strata_var = choices_selected(
        variable_choices("ADSL", c("SEX", "BMRKR2")),
        "SEX"
      ),
      facet_var = choices_selected(
        variable_choices("ADSL", c("SEX", "BMRKR2")),
        NULL
      ),
      decorators = list(plot = ggplot_caption_decorator())
    )
  )
)

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

```{r shinylive_iframe_2, echo = FALSE, out.width = '150%', out.extra = 'style = "position: relative; z-index:1"', eval = requireNamespace("roxy.shinylive", quietly = TRUE) && knitr::is_html_output() && identical(Sys.getenv("IN_PKGDOWN"), "true")}
code <- paste0(c(
  "interactive <- function() TRUE",
  knitr::knit_code$get("setup"),
  knitr::knit_code$get("decorate_ggplot")
), collapse = "\n")

url <- roxy.shinylive::create_shinylive_url(code)
knitr::include_url(url, height = "800px")
```

## Decorating `datatables`

Here's an example to showcase how you can edit an output of class `datatables`.
Please refer the [helper functions](https://rstudio.github.io/DT/functions.html) of the `DT` package to learn more about extending the `datatables` objects.
```{r decorate_datatable, message=FALSE}
library(teal.modules.clinical)

data <- teal_data(join_keys = default_cdisc_join_keys[c("ADSL", "ADRS")])
data <- within(data, {
  ADSL <- rADSL
  ADLB <- tmc_ex_adlb |>
    mutate(AVISIT == forcats::fct_reorder(AVISIT, AVISITN, min)) |>
    mutate(
      ONTRTFL = case_when(
        AVISIT %in% c("SCREENING", "BASELINE") ~ "",
        TRUE ~ "Y"
      ) |> with_label("On Treatment Record Flag")
    )
})
join_keys(data) <- default_cdisc_join_keys[names(data)]

dt_table_decorator <- function(color1 = "pink", color2 = "lightblue") {
  teal_transform_module(
    label = "Table color",
    ui = function(id) {
      selectInput(
        NS(id, "color"),
        "Table Color",
        choices = c("white", color1, color2),
        selected = "Default"
      )
    },
    server = function(id, data) {
      moduleServer(id, function(input, output, session) {
        reactive({
          data() |> within(
            {
              table <- DT::formatStyle(
                table,
                columns = attr(table$x, "colnames")[-1],
                target = "row",
                backgroundColor = color
              )
            },
            color = input$color
          )
        })
      })
    }
  )
}

app <- init(
  data = data,
  modules = modules(
    tm_t_pp_laboratory(
      label = "tm_t_pp_laboratory",
      dataname = "ADLB",
      patient_col = "USUBJID",
      paramcd = choices_selected(
        choices = variable_choices("ADLB", "PARAMCD"),
        selected = "PARAMCD"
      ),
      param = choices_selected(
        choices = variable_choices("ADLB", "PARAM"),
        selected = "PARAM"
      ),
      timepoints = choices_selected(
        choices = variable_choices("ADLB", "ADY"),
        selected = "ADY"
      ),
      anrind = choices_selected(
        choices = variable_choices("ADLB", "ANRIND"),
        selected = "ANRIND"
      ),
      aval_var = choices_selected(
        choices = variable_choices("ADLB", "AVAL"),
        selected = "AVAL"
      ),
      avalu_var = choices_selected(
        choices = variable_choices("ADLB", "AVALU"),
        selected = "AVALU"
      ),
      decorators = list(table = dt_table_decorator())
    )
  )
)

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

```{r shinylive_iframe_3, echo = FALSE, out.width = '150%', out.extra = 'style = "position: relative; z-index:1"', eval = requireNamespace("roxy.shinylive", quietly = TRUE) && knitr::is_html_output() && identical(Sys.getenv("IN_PKGDOWN"), "true")}
code <- paste0(c(
  "interactive <- function() TRUE",
  knitr::knit_code$get("setup"),
  knitr::knit_code$get("decorate_datatable")
), collapse = "\n")

url <- roxy.shinylive::create_shinylive_url(code)
knitr::include_url(url, height = "800px")
```
