Skip to content

Fix decorator problems after a good first run#1515

Open
averissimo wants to merge 18 commits into
mainfrom
1511-fix_decorator@main
Open

Fix decorator problems after a good first run#1515
averissimo wants to merge 18 commits into
mainfrom
1511-fix_decorator@main

Conversation

@averissimo

@averissimo averissimo commented Apr 14, 2025

Copy link
Copy Markdown
Contributor

Pull Request

Fixes #1511

Changes description

  • Captures errors in teal_transform_data on initialization
    • Which should only be applicable to decorators
  • Error is propagated downstream
🤖 Example app
options(
  teal.log_level = "ERROR",
  teal.show_js_log = TRUE,
  # teal.bs_theme = bslib::bs_theme(version = 5),
  shiny.bookmarkStore = "server"
)

pkgload::load_all("teal")

tm_decorated_plot <- function(label = "module", transformators = list(), decorators = list(), datanames = "all") {
  checkmate::assert_list(decorators, "teal_transform_module")
  module(
    label = label,
    ui = function(id, decorators) {
      ns <- NS(id)
      div(
        selectInput(ns("dataname"), label = "select dataname", choices = NULL, multiple = TRUE),
        selectInput(ns("x"), label = "select x", choices = NULL, multiple = TRUE),
        selectInput(ns("y"), label = "select y", choices = NULL, multiple = TRUE),
        ui_transform_teal_data(ns("decorate"), transformators = decorators),
        plotOutput(ns("plot")),
        verbatimTextOutput(ns("text"))
      )
    },
    server = function(id, data, decorators) {
      moduleServer(id, function(input, output, session) {
        observeEvent(data(), {
          dataname <- if (length(input$dataname)) input$dataname else names(data())[1]
          updateSelectInput(inputId = "dataname", choices = names(data()), selected = dataname)
        })

        observeEvent(input$dataname, {
          req(input$dataname)
          updateSelectInput(inputId = "x", choices = colnames(data()[[input$dataname]]))
          updateSelectInput(inputId = "y", choices = colnames(data()[[input$dataname]]))
        })

        dataname <- reactive(req(input$dataname))
        x <- reactive({
          req(input$x, input$x %in% colnames(data()[[dataname()]]))
          input$x
        })

        y <- reactive({
          req(input$y, input$y %in% colnames(data()[[dataname()]]))
          input$y
        })
        plot_data <- reactive({
          # todo: make sure it triggers once on init
          #       and once on change of its input and once on change in previous stages
          req(dataname(), x(), y())
          Sys.sleep(5) # to mimic relatively long computation
          within(data(),
            {
              plot <- ggplot2::ggplot(dataname, ggplot2::aes(x = x, y = y)) +
                ggplot2::geom_point()
            },
            dataname = as.name(dataname()),
            x = as.name(x()),
            y = as.name(y())
          )
        })

        plot_data_decorated_no_print <- srv_transform_teal_data(
          "decorate",
          data = plot_data,
          transformators = decorators
        )
        plot_data_decorated <- reactive(
          within(req(plot_data_decorated_no_print()), expr = plot)
        )

        plot_r <- reactive({
          plot_data_decorated()[["plot"]]
        })

        output$plot <- renderPlot(plot_r())
        output$text <- renderText({
          teal.code::get_code(req(plot_data_decorated()))
        })
      })
    },
    ui_args = list(decorators = decorators),
    server_args = list(decorators = decorators),
    datanames = datanames,
    transformators = transformators
  )
}

make_data <- function(datanames = c("ADSL", "ADTTE")) {
  data_obj <- teal.data::teal_data()
  if ("ADSL" %in% datanames) {
    data_obj <- within(data_obj, ADSL <- teal.data::rADSL)
  }
  if ("ADTTE" %in% datanames) {
    data_obj <- within(data_obj, ADTTE <- teal.data::rADTTE)
  }
  join_keys(data_obj) <- default_cdisc_join_keys[datanames]
  data_obj
}

data <- teal_data_module(
  once = FALSE,
  ui = function(id) {
    ns <- NS(id)
    tagList(
      selectizeInput(
        ns("errortype"),
        label = "Error Type",
        choices = c(
          "ok", "insufficient datasets", "no data",
          "qenv.error", "error in reactive", "validate error", "silent.shiny.error", "not a reactive"
        )
      )
    )
  },
  server = function(id, ...) {
    moduleServer(id, function(input, output, session) {
      logger::log_trace("example_module_transform2 initializing.")
      reactive({
        switch(input$errortype,
          ok = make_data(),
          `insufficient datasets` = make_data(datanames = "ADSL"),
          `no data` = teal_data(),
          qenv.error = within(data(), stop("\nthis is qenv.error in teal_data_module\n")),
          `error in reactive` = stop("\nerror in a reactive in teal_data_module\n"),
          `validate error` = validate(need(FALSE, "\nvalidate error in teal_data_module\n")),
          `silent.shiny.error` = req(FALSE)
        )
      })
    })
  }
)

trans <- teal_transform_module(
  ui = function(id) {
    ns <- NS(id)
    tagList(
      selectizeInput(
        ns("errortype"),
        label = "Error Type",
        choices = c(
          "ok", "insufficient datasets", "no data",
          "qenv.error", "error in reactive", "validate error", "silent.shiny.error", "not a reactive"
        )
      )
    )
  },
  server = function(id, data) {
    moduleServer(id, function(input, output, session) {
      logger::log_trace("example_module_transform2 initializing.")
      # to check if data with error causes problems
      data2 <- reactive(data())
      data3 <- eventReactive(data2(), data2())
      observeEvent(data3(), {
        # do nothing
      })
      reactive({
        # notes: make sure it:
        #        - triggers once on init
        #        - once on change of its input
        #        - once on change in data input
        new_data <- switch(input$errortype,
          ok = data3(),
          `insufficient datasets` = data3()["ADSL"],
          `no data` = teal_data(),
          qenv.error = within(teal_data(), stop("\nthis is qenv.error in teal_transform_module\n")),
          `error in reactive` = stop("\nerror in a reactive in teal_transform_module\n"),
          `validate error` = validate(need(FALSE, "\nvalidate error in teal_transform_module\n")),
          `silent.shiny.error` = req(FALSE)
        )
        new_data
      })
    })
  }
)

trans_empty <- teal_transform_module(
  server = function(id, data) {
    moduleServer(id, function(input, output, session) {
      reactive({
        validate(need(nrow(data()$ADSL) > 250, "ADSL needs 250 rows"))
        data()
      })
    })
  }
)

decor <- teal_transform_module(
  label = "X-axis decorator",
  ui = function(id) {
    ns <- NS(id)
    tagList(
      selectizeInput(
        ns("action"),
        label = "Action type",
        choices = c(
          "nothing", "decorate", "no data",
          "qenv.error", "error in reactive", "validate error", "silent.shiny.error", "not a reactive"
        )
      )
    )
  },
  server = function(id, data) {
    moduleServer(id, function(input, output, session) {
      logger::log_trace("example_module_transform2 initializing.")
      reactive({
        switch(input$action,
          nothing = data(),
          `decorate` = data() |> within(plot <- plot + ggplot2::ggtitle("Decorated Title")),
          `no data` = teal_data(),
          qenv.error = within(teal_data(), stop("\nthis is qenv.error in teal_transform_module\n")),
          `error in reactive` = stop("\nerror in a reactive in teal_transform_module\n"),
          `validate error` = validate(need(FALSE, "\nvalidate error in teal_transform_module\n")),
          `silent.shiny.error` = req(FALSE)
        )
      })
    })
  }
)

app <- teal::init(
  data = data,
  modules = modules(
    modules(
      label = "first tab",
      tm_decorated_plot(
        "mod-2",
        transformators = list(trans, trans, trans_empty),
        decorators = list(decor, decor),
        datanames = c("ADSL", "ADTTE")
      ),
      example_module()
    )
  ),
  filter = teal_slices(
    teal_slice("ADSL", "SEX"),
    teal_slice("ADSL", "AGE", selected = c(18L, 65L)),
    teal_slice("ADTTE", "PARAMCD", selected = "CRSD"),
    include_varnames = list(
      ADSL = c("SEX", "AGE")
    )
  )
)

runApp(app)

image

@averissimo averissimo marked this pull request as ready for review May 7, 2025 13:47
@averissimo

Copy link
Copy Markdown
Contributor Author

This PR will minimize the srv_decorate_teal_data function definition repeated in {tmc} and {tmg}.

@github-actions

github-actions Bot commented May 7, 2025

Copy link
Copy Markdown
Contributor

Unit Test Performance Difference

Test Suite $Status$ Time on main $±Time$ $±Tests$ $±Skipped$ $±Failures$ $±Errors$
module_teal 💔 $129.53$ $+4.15$ $+1$ $0$ $0$ $0$
Additional test case details
Test Suite $Status$ Time on main $±Time$ Test Case
module_teal 💔 $0.70$ $+1.09$ teal_src_Show_R_code_button_contains_is_disabled_reason_when_disabled_source_code

Results for commit 93dce3c

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented May 7, 2025

Copy link
Copy Markdown
Contributor

badge

Code Coverage Summary

Filename                          Stmts    Miss  Cover    Missing
------------------------------  -------  ------  -------  ---------------------------------------------------------------------------------------
R/after.R                            59      21  64.41%   42-52, 64, 69, 77-79, 81-89, 100, 104-105
R/checkmate.R                        24       0  100.00%
R/dummy_functions.R                  61       2  96.72%   54, 56
R/include_css_js.R                   11       0  100.00%
R/init.R                            136       0  100.00%
R/module_bookmark_manager.R          99      54  45.45%   78-133
R/module_data_summary.R             177       8  95.48%   40, 50, 205, 236-240
R/module_filter_data.R               64       0  100.00%
R/module_filter_manager.R           207       7  96.62%   116-117, 313, 340, 352, 359-360
R/module_init_data.R                 84       0  100.00%
R/module_nested_tabs.R              364      36  90.11%   163, 267-282, 302-306, 324, 361, 472-475, 479-482, 486-489
R/module_session_info.R              18       0  100.00%
R/module_snapshot_manager.R         272       9  96.69%   302-306, 373, 376-378
R/module_source_code.R               69       0  100.00%
R/module_teal_lockfile.R            131      53  59.54%   45-57, 60-62, 76, 86-88, 100-102, 110-119, 122, 124, 126-127, 142-146, 161-162, 177-186
R/module_teal_reporter.R            122       9  92.62%   60, 77-78, 81, 98, 128, 142, 144, 158
R/module_teal.R                     211       7  96.68%   127, 142-143, 183, 217, 260-261
R/module_transform_data.R           134       7  94.78%   58, 118, 151-155
R/module_validate_error.R            73       0  100.00%
R/modules.R                         291      61  79.04%   170-174, 229-232, 357-377, 385, 391, 568-574, 587-595, 610-625, 658, 670-678
R/reporter_previewer_module.R        41      12  70.73%   41, 45, 68-85
R/teal_data_module-eval_code.R       23       0  100.00%
R/teal_data_module-within.R           7       0  100.00%
R/teal_data_module.R                 20       0  100.00%
R/teal_data_utils.R                  49       0  100.00%
R/teal_modifiers.R                   57       0  100.00%
R/teal_slices-store.R                29       0  100.00%
R/teal_slices.R                      63       0  100.00%
R/teal_transform_module.R            45       0  100.00%
R/TealAppDriver.R                   344     344  0.00%    50-713
R/utils.R                           291      48  83.51%   403-452, 540-549
R/validate_inputs.R                  32       0  100.00%
R/validations.R                      58       0  100.00%
R/zzz.R                              19       0  100.00%
TOTAL                              3685     678  81.60%

Diff against main

Filename                     Stmts    Miss  Cover
-------------------------  -------  ------  -------
R/module_nested_tabs.R          -7       0  -0.19%
R/module_transform_data.R      +18      +1  -0.05%
TOTAL                          +11      +1  +0.03%

Results for commit: 72b1a66

Minimum allowed coverage is 80%

♻️ This comment has been updated with latest results

@github-actions

github-actions Bot commented May 7, 2025

Copy link
Copy Markdown
Contributor

Unit Tests Summary

  1 files   33 suites   2m 44s ⏱️
415 tests 354 ✅ 61 💤 0 ❌
638 runs  577 ✅ 61 💤 0 ❌

Results for commit 72b1a66.

♻️ This comment has been updated with latest results.

averissimo added a commit that referenced this pull request May 8, 2025
@averissimo

averissimo commented May 9, 2025

Copy link
Copy Markdown
Contributor Author

📑 Journal (rolling update):

  1. (non-documented)
  2. Use validate(need(...)) instead of stop() for better shiny handling in module
  3. Q: Keep "trigger on success" in summary data?
    • Seems to me that this should be less reactive to errors in transforms

Note: UI changes about placement and what should show where should be discussed #1509

Latest screenshot

image

Older screenshots _(nothing to see here yet)_

Comment thread R/module_nested_tabs.R
)

summary_table <- srv_data_summary("data_summary", module_teal_data)
summary_table <- srv_data_summary("data_summary", summary_data) # Only updates on success

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should not be in error state or hidden when transformators introduce an error.

The alternative is to use module_teal_data and remove the (newly) introduced summary_data as below.

Q: WDYT?

image

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like that there is not data summary as it cannot be provided, but maybe it was hidden because this makes the different order pop up the error message more for the users. But I prefer to not have hidding elements messing with the layout of the app and leave the section even if empty.

@llrs-roche llrs-roche left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left a couple of comments as I couldn't run yet the branch. The most important is to make it easier to pull the branch. I had messed my git config but I fixed it.

It would also great if the example app at the beginning of the PR could be updated to really open an app (to avoid me messing it and test different things)

Comment thread teal.Rproj
Comment thread R/module_nested_tabs.R
)

summary_table <- srv_data_summary("data_summary", module_teal_data)
summary_table <- srv_data_summary("data_summary", summary_data) # Only updates on success

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like that there is not data summary as it cannot be provided, but maybe it was hidden because this makes the different order pop up the error message more for the users. But I prefer to not have hidding elements messing with the layout of the app and leave the section even if empty.

@llrs-roche llrs-roche self-assigned this May 20, 2025

@llrs-roche llrs-roche left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code looks good I left a couple of comments but I think this resolves the issue:

2025-05-23_15-00-44.mp4

I think the code on "example app" is not the one that generates the leading screenshots. But if there is further testing to be done let me know, the checks locally (and remotely) run well

Comment thread R/module_nested_tabs.R
Comment on lines +346 to +347
!inherits(try(transformed_teal_data(), silent = TRUE), "teal_data") &&
inherits(filtered_teal_data(), "teal_data")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As transformed_teal_data depends on the output of filtered_teal_data it can be omitted and I think we would get the same result (at least on the app on the issue it works as before).

Suggested change
!inherits(try(transformed_teal_data(), silent = TRUE), "teal_data") &&
inherits(filtered_teal_data(), "teal_data")
!inherits(try(transformed_teal_data(), silent = TRUE), "teal_data")

Comment thread R/module_transform_data.R
# Disable all elements if original data is not yet a teal_data
observeEvent(data_original_handled(), {
shinyjs::toggleState(
"wrapper_panel",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there is a div with id = "wrapper" for modules tabs, but where does this _panel come from? Would it work on teal modules used directly on shiny? Just thinking aloud after resolving an issue coming from an id not matching between different parts.

Comment thread R/module_transform_data.R
Comment on lines +111 to +112
data_handled()
data_original_handled()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice trick, never thought about adding two expressions as a single event. Would be the same as to have only data_handled()? Or in reversed order?

Comment thread teal.Rproj
@averissimo

Copy link
Copy Markdown
Contributor Author

@llrs-roche this is not forgotten, @gogonzo wants to add some functionality on top after we get an understanding of teal_report new data structure

@llrs-roche

Copy link
Copy Markdown
Contributor

I was checking the board and I don't know were we stay on this PR/feature. The coments I left on the review are minor and at the time I thought it could be merged. Do we merge this @gogonzo ?

@gogonzo

gogonzo commented Oct 14, 2025

Copy link
Copy Markdown
Contributor

I was checking the board and I don't know were we stay on this PR/feature. The comments I left on the review are minor and at the time I thought it could be merged. Do we merge this @gogonzo ?

PR was not enough as reason of failure lays deeper. I'd expect making a solution which can standardize error handling and would rather remove lines than add. There was one PR at that time I raised but I'm sceptical if anybody will review that anytime soon. I don't have capacity to handle this topic now :/

@gogonzo

gogonzo commented Oct 14, 2025

Copy link
Copy Markdown
Contributor

I was checking the board and I don't know were we stay on this PR/feature. The comments I left on the review are minor and at the time I thought it could be merged. Do we merge this @gogonzo ?

PR was not enough as reason of failure lays deeper. I'd expect making a solution which can standardize error handling and would rather remove lines than add. There was one PR at that time I raised but I'm sceptical if anybody will review that anytime soon. I don't have capacity to handle this topic now :/

Sorry @llrs-roche I confused this PR with #1509
I'm fine with your merge decision if everything works. Please check-out an app below where you can test various failures config.

teal-app-failures
options(
  teal.log_level = "ERROR",
  teal.show_js_log = TRUE,
  # teal.bs_theme = bslib::bs_theme(version = 5),
  shiny.bookmarkStore = "server"
)

pkgload::load_all("teal")

tm_decorated_plot <- function(label = "module", transformators = list(), decorators = list(), datanames = "all") {
  checkmate::assert_list(decorators, "teal_transform_module")
  module(
    label = label,
    ui = function(id, decorators) {
      ns <- NS(id)
      div(
        selectInput(ns("dataname"), label = "select dataname", choices = NULL, multiple = TRUE),
        selectInput(ns("x"), label = "select x", choices = NULL, multiple = TRUE),
        selectInput(ns("y"), label = "select y", choices = NULL, multiple = TRUE),
        ui_transform_teal_data(ns("decorate"), transformators = decorators),
        plotOutput(ns("plot")),
        verbatimTextOutput(ns("text"))
      )
    },
    server = function(id, data, decorators) {
      moduleServer(id, function(input, output, session) {
        observeEvent(data(), {
          dataname <- if (length(input$dataname)) input$dataname else names(data())[1]
          updateSelectInput(inputId = "dataname", choices = names(data()), selected = dataname)
        })

        observeEvent(input$dataname, {
          req(input$dataname)
          updateSelectInput(inputId = "x", choices = colnames(data()[[input$dataname]]))
          updateSelectInput(inputId = "y", choices = colnames(data()[[input$dataname]]))
        })

        dataname <- reactive(req(input$dataname))
        x <- reactive({
          req(input$x, input$x %in% colnames(data()[[dataname()]]))
          input$x
        })

        y <- reactive({
          req(input$y, input$y %in% colnames(data()[[dataname()]]))
          input$y
        })
        plot_data <- reactive({
          # todo: make sure it triggers once on init
          #       and once on change of its input and once on change in previous stages
          req(dataname(), x(), y())
          Sys.sleep(5) # to mimic relatively long computation
          within(data(),
            {
              plot <- ggplot2::ggplot(dataname, ggplot2::aes(x = x, y = y)) +
                ggplot2::geom_point()
            },
            dataname = as.name(dataname()),
            x = as.name(x()),
            y = as.name(y())
          )
        })

        plot_data_decorated_no_print <- srv_transform_teal_data(
          "decorate",
          data = plot_data,
          transformators = decorators
        )
        plot_data_decorated <- reactive(
          within(req(plot_data_decorated_no_print()), expr = plot)
        )

        plot_r <- reactive({
          plot_data_decorated()[["plot"]]
        })

        output$plot <- renderPlot(plot_r())
        output$text <- renderText({
          teal.code::get_code(req(plot_data_decorated()))
        })
      })
    },
    ui_args = list(decorators = decorators),
    server_args = list(decorators = decorators),
    datanames = datanames,
    transformators = transformators
  )
}

make_data <- function(datanames = c("ADSL", "ADTTE")) {
  data_obj <- teal.data::teal_data()
  if ("ADSL" %in% datanames) {
    data_obj <- within(data_obj, ADSL <- teal.data::rADSL)
  }
  if ("ADTTE" %in% datanames) {
    data_obj <- within(data_obj, ADTTE <- teal.data::rADTTE)
  }
  join_keys(data_obj) <- default_cdisc_join_keys[datanames]
  data_obj
}

data <- teal_data_module(
  once = FALSE,
  ui = function(id) {
    ns <- NS(id)
    tagList(
      selectizeInput(
        ns("errortype"),
        label = "Error Type",
        choices = c(
          "ok", "insufficient datasets", "no data",
          "qenv.error", "error in reactive", "validate error", "silent.shiny.error", "not a reactive"
        )
      )
    )
  },
  server = function(id, ...) {
    moduleServer(id, function(input, output, session) {
      logger::log_trace("example_module_transform2 initializing.")
      reactive({
        switch(input$errortype,
          ok = make_data(),
          `insufficient datasets` = make_data(datanames = "ADSL"),
          `no data` = teal_data(),
          qenv.error = within(data(), stop("\nthis is qenv.error in teal_data_module\n")),
          `error in reactive` = stop("\nerror in a reactive in teal_data_module\n"),
          `validate error` = validate(need(FALSE, "\nvalidate error in teal_data_module\n")),
          `silent.shiny.error` = req(FALSE)
        )
      })
    })
  }
)

trans <- teal_transform_module(
  ui = function(id) {
    ns <- NS(id)
    tagList(
      selectizeInput(
        ns("errortype"),
        label = "Error Type",
        choices = c(
          "ok", "insufficient datasets", "no data",
          "qenv.error", "error in reactive", "validate error", "silent.shiny.error", "not a reactive"
        )
      )
    )
  },
  server = function(id, data) {
    moduleServer(id, function(input, output, session) {
      logger::log_trace("example_module_transform2 initializing.")
      # to check if data with error causes problems
      data2 <- reactive(data())
      data3 <- eventReactive(data2(), data2())
      observeEvent(data3(), {
        # do nothing
      })
      reactive({
        # notes: make sure it:
        #        - triggers once on init
        #        - once on change of its input
        #        - once on change in data input
        new_data <- switch(input$errortype,
          ok = data3(),
          `insufficient datasets` = data3()["ADSL"],
          `no data` = teal_data(),
          qenv.error = within(teal_data(), stop("\nthis is qenv.error in teal_transform_module\n")),
          `error in reactive` = stop("\nerror in a reactive in teal_transform_module\n"),
          `validate error` = validate(need(FALSE, "\nvalidate error in teal_transform_module\n")),
          `silent.shiny.error` = req(FALSE)
        )
        new_data
      })
    })
  }
)

trans_empty <- teal_transform_module(
  server = function(id, data) {
    moduleServer(id, function(input, output, session) {
      reactive({
        validate(need(nrow(data()$ADSL) > 250, "ADSL needs 250 rows"))
        data()
      })
    })
  }
)

decor <- teal_transform_module(
  label = "X-axis decorator",
  ui = function(id) {
    ns <- NS(id)
    tagList(
      selectizeInput(
        ns("action"),
        label = "Action type",
        choices = c(
          "nothing", "decorate", "no data",
          "qenv.error", "error in reactive", "validate error", "silent.shiny.error", "not a reactive"
        )
      )
    )
  },
  server = function(id, data) {
    moduleServer(id, function(input, output, session) {
      logger::log_trace("example_module_transform2 initializing.")
      reactive({
        switch(input$action,
          nothing = data(),
          `decorate` = data() |> within(plot <- plot + ggplot2::ggtitle("Decorated Title")),
          `no data` = teal_data(),
          qenv.error = within(teal_data(), stop("\nthis is qenv.error in teal_transform_module\n")),
          `error in reactive` = stop("\nerror in a reactive in teal_transform_module\n"),
          `validate error` = validate(need(FALSE, "\nvalidate error in teal_transform_module\n")),
          `silent.shiny.error` = req(FALSE)
        )
      })
    })
  }
)

app <- teal::init(
  data = data,
  modules = modules(
    modules(
      label = "first tab",
      tm_decorated_plot(
        "mod-2",
        transformators = list(trans, trans, trans_empty),
        decorators = list(decor, decor),
        datanames = c("ADSL", "ADTTE")
      ),
      example_module()
    )
  ),
  filter = teal_slices(
    teal_slice("ADSL", "SEX"),
    teal_slice("ADSL", "AGE", selected = c(18L, 65L)),
    teal_slice("ADTTE", "PARAMCD", selected = "CRSD"),
    include_varnames = list(
      ADSL = c("SEX", "AGE")
    )
  )
)

runApp(app)

@llrs-roche

Copy link
Copy Markdown
Contributor

Thanks to the app, I reviewed this again:

Data summary is present even when there is an error on qenv:

Screenshot

image

Errors are duplicated for each transform module:

Screenshot

image

Error messages are different on the UI between the transform Data panel and the main panel:

Screenshot

image

Initial error pops a modal and then the summary data is different from what the error says:

Screenshots

image

In conclusion. it fixes the second issue (Last state is shown, be it an error or a plot) but fails to provide "consistent error messages in decorators".
Not merging for now.

@llrs-roche llrs-roche left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment above

@averissimo

averissimo commented Oct 31, 2025

Copy link
Copy Markdown
Contributor Author

Summary of the meeting with @llrs-roche

Data summary is present even when there is an error on qenv:

It makes sense, the summary table should be reactive

Action: grey out the "no datasets to show" in summary table

Errors are duplicated for each transform module:

Plan: only show in transformators if the error occurred there

Error messages are different on the UI between the transform Data panel and the main panel:

The error should also appear in the main module UI

note from Lluis: see if namespace is wrong

Initial error pops a modal and then the summary data is different from what the error says:

Bug when there is no UI for the transfomators and it enters an error state

Expected result is for the pseudo-UI to be visible with the error showing


Extra mile would be to modify the notification when filters are removed due to incompatible datanames.

  • "Some filters..." only when they are partially removed
  • "All filters..." when all are removed
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: decoration error state changes with first good run

4 participants