diff --git a/R/dummy_functions.R b/R/dummy_functions.R index becb2706de..872786d9df 100644 --- a/R/dummy_functions.R +++ b/R/dummy_functions.R @@ -11,6 +11,16 @@ #' @inheritParams teal_modules #' @param decorators `r lifecycle::badge("experimental")` (`list` of `teal_transform_module`) optional, #' decorator for `object` included in the module. +#' +#' @section Reporting: +#' +#' This module returns an object of class `teal_module`, that contains a `server` function. +#' Since the server function returns a `teal_report` object, this makes this module reportable, which means that +#' the reporting functionality will be turned on automatically by the `teal` framework. +#' +#' For more information on reporting in `teal`, see the vignettes: +#' - `vignette("managing-reproducible-report-documents-in-teal", package = "teal")` +#' - `vignette("adding-support-for-reporting-to-custom-modules", package = "teal")` #' #' @return A `teal` module which can be included in the `modules` argument to [init()]. #' diff --git a/_pkgdown.yml b/_pkgdown.yml index f0bc2d1578..6427e7f5ac 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -68,6 +68,7 @@ articles: contents: - creating-custom-modules - adding-support-for-reporting + - managing-reproducible-report-documents-in-teal - transform-module-output - title: Using `teal` navbar: Using `teal` diff --git a/man/example_module.Rd b/man/example_module.Rd index 7e5251a87a..797d3bf60a 100644 --- a/man/example_module.Rd +++ b/man/example_module.Rd @@ -43,6 +43,20 @@ The object can be anything that can be handled by \code{renderPrint()}. See the \code{vignette("transform-module-output", package = "teal")} or \code{\link{teal_transform_module}} to read more about decorators. } +\section{Reporting}{ + + +This module returns an object of class \code{teal_module}, that contains a \code{server} function. +Since the server function returns a \code{teal_report} object, this makes this module reportable, which means that +the reporting functionality will be turned on automatically by the \code{teal} framework. + +For more information on reporting in \code{teal}, see the vignettes: +\itemize{ +\item \code{vignette("managing-reproducible-report-documents-in-teal", package = "teal")} +\item \code{vignette("adding-support-for-reporting-to-custom-modules", package = "teal")} +} +} + \examples{ app <- init( data = teal_data(IRIS = iris, MTCARS = mtcars), @@ -56,8 +70,8 @@ if (interactive()) { \describe{ \item{example-1}{ \href{https://shinylive.io/r/app/#code=NobwRAdghgtgpmAXGKAHVA6ASmANGAYwHsIAXOMpMAGwEsAjAJykYE8AKcqagSgB0ItMnGYFStAG5wABAB4AtNIBmAVwhjaJdj2kAVLAFUAogIFpUcxUNql2A6dIAmUUlGkBeaV2oB9Z6-YASSxAgGUPaVpGWgBnXGkAWV0AYQBBLHDPGFICFhieXHtpGCJHFWo4GIi4AA9YVAqfErKK7QF+QSVpdiFyUXEpbR0QIpiACyFWVPR2cwASFVp4+ZiRKUYOgF8wTYBdIA}{Open in Shinylive} - \if{html}{\out{}} - \if{html}{\out{}} + \if{html}{\out{}} + \if{html}{\out{}} } } } diff --git a/vignettes/adding-support-for-reporting.Rmd b/vignettes/adding-support-for-reporting.Rmd index 4e04e3fb8f..c754f2ef22 100644 --- a/vignettes/adding-support-for-reporting.Rmd +++ b/vignettes/adding-support-for-reporting.Rmd @@ -16,412 +16,94 @@ The `teal` package offers an integrated reporting feature utilizing the `teal.re For a comprehensive explanation of the reporting functionality itself, please refer to the documentation therein. This article is intended for module developers and aims to provide guidance on enhancing a custom `teal` module with an automatic reporting feature. -This enhancement enables users to incorporate snapshots of the module outputs into a report which can then be reviewed in another module automatically provided by `teal`. +This enhancement enables users to incorporate snapshots of the module outputs into a report, which can be viewed in another module called `Reporter previewer` (automatically provided by `teal`). Thus the app user can interact with the report. The responsibilities of a module developer include: -- Adding support for reporting to their module. +- Adding support for reporting to their module, by returning a reactive `teal_data` object from the module's `server` function. - Specifying the outputs that constitute a snapshot of their module. The entire life cycle of objects involved in creating the report and configuring the module to preview the report is handled by `teal`. -## Custom module - -```{r setup, include=FALSE} -library(teal) -library(teal.reporter) -``` -```{r as_interactive, eval=FALSE, echo=FALSE} -interactive <- function() TRUE -``` - -Let us consider an example module, based on the example module from `teal`: -```{r module_1} -library(teal) -library(teal.reporter) - -my_module <- function(label = "example teal module") { - module( - label = label, - server = function(id, data) { - checkmate::assert_class(isolate(data()), "teal_data") - - moduleServer(id, function(input, output, session) { - updateSelectInput(session, "dataname", choices = isolate(names(data()))) - output$dataset <- renderPrint({ - req(input$dataname) - data()[[input$dataname]] - }) - }) - }, - ui = function(id) { - ns <- NS(id) - sidebarLayout( - sidebarPanel(selectInput(ns("dataname"), "Choose a dataset", choices = NULL)), - mainPanel(verbatimTextOutput(ns("dataset"))) - ) - } - ) -} -``` - -Using `teal`, you can launch this example module with the following: - -```{r app_1} -app <- init( - data = teal_data(IRIS = iris, MTCARS = mtcars), - modules = my_module() -) - -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( - knitr::knit_code$get("as_interactive"), - knitr::knit_code$get("module_1"), - knitr::knit_code$get("app_1") -), collapse = "\n") - -url <- roxy.shinylive::create_shinylive_url(code) -knitr::include_url(url, height = "800px") -``` - - ## Add support for reporting -### Modify the declaration of the server function +To inform `teal` that the module requires reporting, return a `reactive` containing `teal_report` from the `server` function. +Current outputs of the modules and reproducible code will be included in the module when "add to report" button is clicked. -The first step is to add an additional argument to the server function declaration - `reporter`. -This informs `teal` that the module requires `reporter`, and it will be included when the module is called. -See below: +Every input `teal_data` object in `teal` gets converted to a `teal_report` object, which you can then enhance with additional content. -```{r module_2} -my_module_with_reporting <- function(label = "example teal module") { - module( - label = label, - server = function(id, data, reporter) { - moduleServer(id, function(input, output, session) { - updateSelectInput(session, "dataname", choices = isolate(names(data()))) - output$dataset <- renderPrint({ - req(input$dataname) - data()[[input$dataname]] - }) - }) - }, - ui = function(id) { - ns <- NS(id) - sidebarLayout( - sidebarPanel(selectInput(ns("dataname"), "Choose a dataset", choices = NULL)), - mainPanel(verbatimTextOutput(ns("dataset"))) - ) - } - ) -} -``` - -With these modifications, the module is now ready to be launched with `teal`: +For detailed information about the `teal_report` class and its capabilities, see the [teal_report Class vignette](https://insightsengineering.github.io/teal.reporter/latest-tag/articles/teal-report-class.html). -```{r app_2} -app <- init( - data = teal_data(IRIS = iris, MTCARS = mtcars), - modules = my_module_with_reporting() -) - -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( - knitr::knit_code$get("as_interactive"), - knitr::knit_code$get("setup"), - knitr::knit_code$get("module_2"), - knitr::knit_code$get("app_2") -), collapse = "\n") - -url <- roxy.shinylive::create_shinylive_url(code) -knitr::include_url(url, height = "800px") -``` - - -`teal` adds another tab to the application, titled `Report previewer`. -However, there is no visible change in how the module operates and appears and the user cannot add content to the report from this module. -That requires inserting UI and server elements of the `teal.reporter` module into the module body. - -### Insert `teal.reporter` module - -The UI and the server logic necessary for adding cards from `my_module_with_reporting` to the report are provided by `teal.reporter::simple_reporter_ui` and `teal.reporter::simple_reporter_srv`. - -```{r module_3} -my_module_with_reporting <- function(label = "example teal module") { - module( - label = label, - server = function(id, data, reporter) { - moduleServer(id, function(input, output, session) { - teal.reporter::simple_reporter_srv( - id = "reporter", - reporter = reporter, - card_fun = function(card) card - ) - updateSelectInput(session, "dataname", choices = isolate(names(data()))) - output$dataset <- renderPrint({ - req(input$dataname) - data()[[input$dataname]] - }) - }) - }, - ui = function(id) { - ns <- NS(id) - sidebarLayout( - sidebarPanel( - teal.reporter::simple_reporter_ui(ns("reporter")), - selectInput(ns("dataname"), "Choose a dataset", choices = NULL) - ), - mainPanel(verbatimTextOutput(ns("dataset"))) - ) - } - ) -} -``` - -This updated module is now ready to be launched: - -```{r app_3} -app <- init( - data = teal_data(IRIS = iris, MTCARS = mtcars), - modules = my_module_with_reporting() -) - -if (interactive()) { - shinyApp(app$ui, app$server) -} -``` - -A new piece of `UI` has been added, and the buttons are clickable. -The user can now add a card to the report and view it in the `Report previewer` module but the preview is still empty since we have not instructed our module what to put on the card. - -```{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( - knitr::knit_code$get("as_interactive"), - knitr::knit_code$get("setup"), - knitr::knit_code$get("module_3"), - knitr::knit_code$get("app_3") -), collapse = "\n") - -url <- roxy.shinylive::create_shinylive_url(code) -knitr::include_url(url, height = "800px") -``` +## Example -### Add content to the card +Below is an example demonstrating how to build a `teal` module with reporting functionality. -To add content to a card, we will utilize the public API exposed by the `TealReportCard` class. -The `teal.reporter::simple_reporter_srv` module accepts the `card_fun` argument that determines the appearance of the output from our custom module. -`ReportCard` and its derivatives allow the sequential addition of content according to the order of method calls. -To explore the content, we can use the `$get_content` method. -For further details, refer to the documentation of `TealReportCard` and `teal.reporter::ReportCard`. +The key aspects of this implementation are: -We will add simple text to the card by modifying the `card_fun` argument passed to `teal.reporter::simple_reporter_srv`. -The function must return the `teal_card` object, otherwise errors may occur in `teal`. +1. **Return a reactive `teal_report`**: The module's server function returns a reactive containing an enhanced `teal_report` object +2. **Add content with `teal_card()`**: Commentary and section headers are added using `teal_card()` +3. **Capture computations with `eval_code()`**: Code execution and results are captured for reproducibility -```{r module_4} -custom_function <- function(card = teal.reporter::ReportCard$new()) { - card$append_text("This is content from a custom teal module!") - card -} +```{r app_5} +library(teal) +library(teal.reporter) -my_module_with_reporting <- function(label = "example teal module") { +tm_simple_regression <- function(label = "Simple Regression") { module( label = label, - server = function(id, data, reporter) { - moduleServer(id, function(input, output, session) { - teal.reporter::simple_reporter_srv( - id = "reporter", - reporter = reporter, - card_fun = custom_function - ) - updateSelectInput(session, "dataname", choices = isolate(names(data()))) - output$dataset <- renderPrint({ - req(input$dataname) - data()[[input$dataname]] - }) + server = function(input, output, session, data, reporter, ...) { + + output$plot <- renderPlot({ + req(input$x_var, input$y_var) + dataset <- data()[["iris"]] + plot(dataset[[input$x_var]], dataset[[input$y_var]]) }) - }, - ui = function(id) { - ns <- NS(id) - sidebarLayout( - sidebarPanel( - teal.reporter::simple_reporter_ui(ns("reporter")), - selectInput(ns("dataname"), "Choose a dataset", choices = NULL) - ), - mainPanel(verbatimTextOutput(ns("dataset"))) - ) - } - ) -} -``` - -```{r app_4} -app <- init( - data = teal_data(IRIS = iris, MTCARS = mtcars), - modules = my_module_with_reporting() -) - -if (interactive()) { - shinyApp(app$ui, app$server) -} -``` - -Now, an application user can see the text added by `custom_function` in the `Report previewer` module. - -```{r shinylive_iframe_4, 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( - knitr::knit_code$get("as_interactive"), - knitr::knit_code$get("setup"), - knitr::knit_code$get("module_4"), - knitr::knit_code$get("app_4") -), collapse = "\n") - -url <- roxy.shinylive::create_shinylive_url(code) -knitr::include_url(url, height = "800px") -``` - -### Add non-text content to the card - -`teal.reporter` supports the addition of tables, charts, and more. -For more information, explore the API of `teal.reporter::ReportCard` to learn about the supported content types. - -### `TealReportCard` - -`teal` exports the `TealReportCard` class, which extends the `teal.reporter::ReportCard` class and provides several convenient methods to facilitate working with `teal` features like the filter panel or source code. -For more details, refer to the documentation of `TealReportCard`. - -To support `TealReportCard`, the function that is passed to `teal.reporter::simple_reporter_srv` must define a default value for the card, as shown below: - -```{r} -custom_function <- function(card = TealReportCard$new()) { - # ... some code ... # - card -} -``` - -Without this definition, the API of `TealReportCard` will not be available within the function. - -## Example - -In conclusion, we have demonstrated how to build a standard `teal` app with code reproducibility and reporter functionalities. -Note that the server function requires the `filter_panel_api` argument so that the filter panel state can be added to the report. - -In the final example, we have incorporated `teal.code` snippets. -`teal.code` is an `R` library that offers utilities for storing code and associating it with an execution environment. -This allows `ReporterCard` to store the code necessary to generate the table along with the table itself. -To learn more about `teal.code` see the vignette _`qenv`_ in `teal.code`. - -```{r app_5} -example_reporter_module <- function(label = "Example") { - module( - label = label, - server = function(id, data, reporter, filter_panel_api) { - with_filter <- !missing(filter_panel_api) && inherits(filter_panel_api, "FilterPanelApi") - moduleServer(id, function(input, output, session) { - updateSelectInput(session, "dataname", choices = isolate(names(data()))) - dat <- reactive(data()[[input$dataname]]) - observe({ - req(input$dataname) - req(dat()) - updateSliderInput(session, "nrow", max = nrow(dat()), value = floor(nrow(dat()) / 5)) - }) - - table_q <- reactive({ - req(input$dataname) - req(input$nrow) - within( - data(), - result <- head(dataset, nrows), - dataset = as.name(input$dataname), - nrows = input$nrow - ) - }) - - output$table <- renderTable(table_q()[["result"]]) - - ### REPORTER - card_fun <- function(card = teal.reporter::ReportCard$new(), comment) { - card$set_name("Table Module") - card$append_text(paste("Selected dataset", input$dataname), "header2") - card$append_text("Selected Filters", "header3") - if (with_filter) { - card$append_text(filter_panel_api$get_filter_state(), "verbatim") - } - card$append_text("Encoding", "header3") - card$append_text( - yaml::as.yaml( - stats::setNames( - lapply(c("dataname", "nrow"), function(x) input[[x]]), c("dataname", "nrow") - ) - ), - "verbatim" - ) - card$append_text("Module Table", "header3") - card$append_table(table_q()[["result"]]) - card$append_text("Show R Code", "header3") - card$append_text(teal.code::get_code(table_q()), "verbatim") - if (!comment == "") { - card$append_text("Comment", "header3") - card$append_text(comment) - } - card - } - teal.reporter::add_card_button_srv( - "addReportCard", - reporter = reporter, - card_fun = card_fun + + report_data <- reactive({ + obj <- data() + teal_card(obj) <- + c(teal_card(obj), "# Module's computation") + eval_code( + obj, + sprintf( + "correlation <- cor(iris[['%s']], iris[['%s']]);correlation", + input$x_var, + input$y_var + ), + keep_output = TRUE ) - teal.reporter::download_report_button_srv("downloadButton", reporter = reporter) - teal.reporter::reset_report_button_srv("resetButton", reporter) - ### }) + report_data }, + ui = function(id) { ns <- NS(id) - - sidebarLayout( - sidebarPanel(selectInput(ns("dataname"), "Choose a dataset", choices = NULL)), - mainPanel( - teal.reporter::simple_reporter_ui(ns("reporter")), - verbatimTextOutput(ns("dataset")) - ) - ) - - sidebarLayout( - sidebarPanel( - tags$div( - teal.reporter::add_card_button_ui(ns("addReportCard")), - teal.reporter::download_report_button_ui(ns("downloadButton")), - teal.reporter::reset_report_button_ui(ns("resetButton")) + fluidPage( + h3("Simple Regression Plot"), + fluidRow( + column(6, + selectInput(ns("x_var"), "X Variable:", + choices = names(iris)[1:4], selected = "Sepal.Length") ), - selectInput(ns("dataname"), "Choose a dataset", choices = NULL), - sliderInput(ns("nrow"), "Number of rows", min = 1, max = 1, value = 1, step = 1) + column(6, + selectInput(ns("y_var"), "Y Variable:", + choices = names(iris)[1:4], selected = "Sepal.Width") + ) ), - mainPanel(tableOutput(ns("table"))) + plotOutput(ns("plot")) ) } ) } app <- init( - data = teal_data(AIR = airquality, IRIS = iris), - modules = list( - example_reporter_module(label = "with Reporter"), - my_module(label = "without Reporter") + data = teal_data(iris = iris), + modules = modules( + tm_simple_regression("Regression Analysis") ), - filter = teal_slices(teal_slice(dataname = "AIR", varname = "Temp", selected = c(72, 85))) -) |> - modify_header(tags$h2("Example teal app with reporter")) + header = "Simple Teal App with Reporting" +) if (interactive()) { shinyApp(app$ui, app$server) diff --git a/vignettes/getting-started-with-teal.Rmd b/vignettes/getting-started-with-teal.Rmd index 472dab4740..96c8c73d6a 100644 --- a/vignettes/getting-started-with-teal.Rmd +++ b/vignettes/getting-started-with-teal.Rmd @@ -126,9 +126,18 @@ For further details see [Filter Panel vignette](filter-panel.html). ### Reporting -If any of the `modules` in your `teal` application support reporting (see [`teal.reporter`](https://insightsengineering.github.io/teal.reporter/) for more details), users of your application can add the outputs of the modules to a report. -This report can then be downloaded and a special _Report Previewer_ module will be added to your application as an additional tab, where users can view and configure their reports before downloading them. See more details in [this vignette](adding-support-for-reporting.html). +The `init` function includes a `reporter` parameter that controls the global reporting functionality of your `teal` application. This parameter determines whether and how users can generate reports from the analyses performed in your application. +The `reporter` parameter in `init` accepts three types of values: + +* `parameter is omitted`: reporting is enabled by default globally. +* `NULL`: reporting is disabled globally. +* `teal.reporter::Report$new()`: predefined reporter object, possibly containing some initial cards. + +For more details on reporting functionality and how to implement it in your modules, see: +- [adding support for reporting](adding-support-for-reporting.html), +- [managing reproducible report documents in teal](managing-reproducible-report-documents-in-teal.html), +- [`teal.reporter`](https://insightsengineering.github.io/teal.reporter/) package documentation. ### Reproducible code diff --git a/vignettes/managing-reproducible-report-documents-in-teal.rmd b/vignettes/managing-reproducible-report-documents-in-teal.rmd new file mode 100644 index 0000000000..075f33a241 --- /dev/null +++ b/vignettes/managing-reproducible-report-documents-in-teal.rmd @@ -0,0 +1,195 @@ +--- +title: "Managing Reproducible Report Documents in teal" +author: "NEST CoreDev" +output: rmarkdown::html_vignette +vignette: > + %\VignetteIndexEntry{Managing Reproducible Report Documents in teal} + %\VignetteEngine{knitr::rmarkdown} + %\VignetteEncoding{UTF-8} +--- + +## Introduction + +`teal` provides reporting capabilities that allow app users to compose reproducible documents from their analysis modules. This vignette covers the key components for managing report documents in `teal` applications. + +The reporting system offers two types of control: + +1. **Global reporting control** via the `reporter` parameter in `init()` +2. **Module-level control** via `after()` and `disable_report()` + +This vignette demonstrates how to use these features effectively to create flexible and customized reporting workflows. + +## Global Reporting Control + +##### 1. Default Reporting (parameter omitted) +When you don't specify the `reporter` parameter, `teal` automatically enables default reporting functionality: + +```{r default_reporting} +# Default reporting - automatically enabled +app_default <- init( + data = teal_data(IRIS = iris, MTCARS = mtcars), + modules = modules( + example_module("Analysis 1"), + example_module("Analysis 2") + ) +) +# Start app with +# if (interactive()) shinyApp(app$ui, app$server) +``` + +With default reporting enabled: +- A "Report previewer" tab is automatically added to your application +- Users can add module outputs to reports and download them +- Reports include reproducible code for all analyses + +##### 2. Disabled Reporting (`reporter = NULL`) +To completely disable reporting functionality across your entire application: + +```{r disabled_reporting} +# Reporting disabled - no report functionality available +app_no_reporting <- init( + data = teal_data(IRIS = iris, MTCARS = mtcars), + modules = modules( + example_module("Analysis 1"), + example_module("Analysis 2") + ), + reporter = NULL +) +# Start app with +# if (interactive()) shinyApp(app_no_reporting$ui, app_no_reporting$server) +``` + +When `reporter = NULL`: +- No "Report previewer" tab appears +- Modules cannot add content to reports +- All reporting-related UI elements are hidden + +##### 3. Custom Reporter +For advanced use cases, you can provide a custom initial reporter object: + +```{r custom_reporter} +library(teal.reporter) + +# Create a custom reporter with specific configuration +custom_reporter <- Reporter$new() +custom_card <- teal_card("## Welcome", "Welcome message", head(iris)) +metadata(custom_card, "title") <- "The title" +custom_reporter$append_cards(list(WelcomeCard = custom_card)) + +# Use the custom reporter in your app +app_custom <- init( + data = teal_data(IRIS = iris, MTCARS = mtcars), + modules = modules( + example_module("Analysis 1"), + example_module("Analysis 2") + ), + reporter = custom_reporter # TODO: doesn't work yet, app do not start with a predefined card +) +# Start app with +# if (interactive()) shinyApp(app_custom$ui, app_custom$server) +``` + +##### 4. Custom Reporter with a template + +Custom initial reporter can also include a template function that will be applied to every card. +With the templated approach, you can included disclaimers, data sources, data privacy information, etc. + +```{r custom_reporter_template} +library(teal.reporter) + +# Create a custom reporter with specific configuration +custom_reporter_template <- Reporter$new() +custom_reporter_template$set_template( + # Need to return named `teal_card` + function(card) { + tcard <- c("Templated information", card) + attributes(tcard) <- attributes(card) + tcard + } +) + +# Use the custom reporter in your app +app_custom <- init( + data = teal_data(IRIS = iris, MTCARS = mtcars), + modules = modules( + example_module("Analysis 1"), + example_module("Analysis 2") + ), + reporter = custom_reporter_template +) +# Start app with +# if (interactive()) shinyApp(app_custom$ui, app_custom$server) +``` + +#### Module-Level Reporting Control + +While the `reporter` parameter controls reporting globally, you can also disable reporting for specific modules using `disable_report()`. This allows you to have fine-grained control over which modules support reporting: + +```{r module_level_control} +# App with global reporting enabled, but specific modules excluded +app_selective <- init( + data = teal_data(IRIS = iris, MTCARS = mtcars), + modules = modules( + example_module("Analysis 1"), # Reporting enabled + disable_report(example_module("Analysis 2")), # Reporting disabled for this module + example_module("Analysis 3") # Reporting enabled + ) +) +# Start app with +# if (interactive()) shinyApp(app_selective$ui, app_selective$server) +``` + +In this example: +- "Analysis 1" and "Analysis 3" modules can add content to reports +- "Analysis 2" module cannot add content to reports (no "Add to Report" buttons appear) +- The "Report previewer" tab is still available since reporting is globally enabled + +#### Choosing the Right Configuration + +* **Use default reporting** (omit parameter) for most applications where you want standard reporting functionality +* **Use `reporter = NULL`** when you want to disable reporting entirely, perhaps for: + - Demo applications + - Apps where reporting is not needed + - Performance-critical applications +* **Use a custom reporter** when you need: + - Special report formatting + - Custom report templates + - Integration with external reporting systems +* **Use `disable_report()`** for individual modules when you want: + - Most modules to support reporting but exclude specific ones + - To disable reporting for modules that don't produce meaningful report content + - Fine-grained control over the reporting experience + + +## Adjusting the Report Card + +### Using the `after` Function to Customize Module Report Cards + +The `after` function in `teal` provides a mechanism to customize and enhance the report cards generated by modules. This function allows you to inject additional content, modify existing content, or perform post-processing operations on report cards before they are finalized. + +#### Understanding the `after` Function + +The `after` function is typically used within module definitions to specify custom logic that should be executed after the module's standard report card is created. This enables you to: + +- Add custom text, plots, or tables to the report +- Modify the formatting or structure of the report card +- Include additional metadata or context +- Perform validation or quality checks on the report content + +#### Basic Usage Pattern + +```{r after_basic_usage} +# Example module with custom report card adjustment +app_after <- init( + data = teal_data(IRIS = iris, MTCARS = mtcars), + modules = modules( + example_module("Analysis 1") |> + after(server = function(data) { + teal_card(data) <- c(teal_card(data), "## Custom Analysis Summary", head(iris)) + return(data) + }) + ) +) +# Start app with +# if (interactive()) shinyApp(app_after$ui, app_after$server) +```