Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Function to disable download feature #1114

Closed
3 tasks done
arunkumarmahesh opened this issue Feb 27, 2024 · 5 comments
Closed
3 tasks done

Function to disable download feature #1114

arunkumarmahesh opened this issue Feb 27, 2024 · 5 comments
Assignees
Labels
core enhancement New feature or request

Comments

@arunkumarmahesh
Copy link

Feature description

When we work in a GxP environment most of the clinical data would be lying on the server and data is not shared between systems or downloaded from the server directly to desktop folders or even download them.

While working on teal module, have observed teal reporter have an option for downloading them to downloads folder directly, which would be against GxP policies. Is there any way to remove or disable them while launching shiny app.

Code of Conduct

  • I agree to follow this project's Code of Conduct.

Contribution Guidelines

  • I agree to follow this project's Contribution Guidelines.

Security Policy

  • I agree to follow this project's Security Policy.
@arunkumarmahesh arunkumarmahesh added the enhancement New feature or request label Feb 27, 2024
@gogonzo
Copy link
Contributor

gogonzo commented Feb 27, 2024

User require to disable or to remove all download buttons in the teal application.


Example app to demonstrate how difficult is to disable buttons in teal app.

library(teal.modules.general)
library(teal.widgets)

data <- teal_data()
data <- within(data, {
  library(nestcolor)
  ADSL <- rADSL
})
datanames <- c("ADSL")
datanames(data) <- datanames
join_keys(data) <- default_cdisc_join_keys[datanames]

app <- init(
  data = data,
  modules = modules(
    teal.modules.general::tm_a_pca(
      "PCA",
      dat = data_extract_spec(
        dataname = "ADSL",
        select = select_spec(
          choices = variable_choices(
            data = data[["ADSL"]], c("BMRKR1", "AGE", "EOSDY")
          ),
          selected = c("BMRKR1", "AGE"),
          multiple = TRUE
        ),
        filter = NULL
      ),
      ggplot2_args = ggplot2_args(
        labs = list(subtitle = "Plot generated by PCA Module")
      )
    )
  )
)

app$ui <- shiny::tagAppendChild(
  app$ui,
  tags$head(
    tags$style(HTML('
      .simple_report_button[title="Download"] {
        opacity: 0.5;
        cursor: not-allowed;
        color: #ccc;

      }
    ')),
    tags$script(HTML('
      // this script triggers to late as buttons are inserted by shiny server
      $(document).on("shiny:connected", function() {
        var buttons = document.querySelectorAll(".simple_report_button");
        console.log(buttons);
        buttons.forEach(function(b) {
          b.disabled = true;
        })
      });
    '))
  )
)

shinyApp(app$ui, app$server)

Included css rules don't disable buttons, they are just changing style of the button.

To disable button "for real" we need to modify html element directly with js script. Problem roots in the fact that initialization of the ui is delayed and inserted by server. There might be better event than "shiny:connected" to trigger script when every module is initialized.

@gogonzo
Copy link
Contributor

gogonzo commented Feb 27, 2024

Possible to use css selector with display: none. Seems to solve the problem. Ignore previous comment

library(teal.modules.general)
library(teal.widgets)

data <- teal_data()
data <- within(data, {
  library(nestcolor)
  ADSL <- rADSL
})
datanames <- c("ADSL")
datanames(data) <- datanames
join_keys(data) <- default_cdisc_join_keys[datanames]

app <- init(
  data = data,
  modules = modules(
    teal.modules.general::tm_a_pca(
      "PCA",
      dat = data_extract_spec(
        dataname = "ADSL",
        select = select_spec(
          choices = variable_choices(
            data = data[["ADSL"]], c("BMRKR1", "AGE", "EOSDY")
          ),
          selected = c("BMRKR1", "AGE"),
          multiple = TRUE
        ),
        filter = NULL
      ),
      ggplot2_args = ggplot2_args(
        labs = list(subtitle = "Plot generated by PCA Module")
      )
    )
  )
)

app$ui <- shiny::tagAppendChild(
  app$ui,
  tags$head(
    tags$style(HTML('
      .simple_report_button[title="Download"] {
        display:none
      }
      a[id$="download_data_prev"] {
        display:none
      }
      button[id$="downbutton-downl"] {
        display:none
      }
    '))
  )
)

shinyApp(app$ui, app$server)

@averissimo
Copy link
Contributor

averissimo commented Feb 27, 2024

Another possibility: Use a teal global option that would prevent the Reporter feature from being enabled (as a tab and on encodings)

The caveat is that this would be applied globally, so the user wouldn't have access to any reports.

Proof-of-concept (with changes to `{teal}` and `{teal.reporter}`)

note: it might be safer to use !isTRUE instead of isFALSE for this feature to be resilient and only kick in if the option is explicitly TRUE

{teal.reporter}: prevent encodings from showing "Reporter"

diff --git a/R/SimpleReporter.R b/R/SimpleReporter.R
index d2da97c..8d272e2 100644
--- a/R/SimpleReporter.R
+++ b/R/SimpleReporter.R
@@ -37,6 +37,8 @@ NULL
 #' @export
 simple_reporter_ui <- function(id) {
   ns <- shiny::NS(id)
+  if (isTRUE(getOption("teal.disable_reporter"))) return(NULL)
   shiny::tagList(
     shiny::singleton(
       shiny::tags$head(shiny::includeCSS(system.file("css/custom.css", package = "teal.reporter")))

{teal}

diff --git a/R/module_nested_tabs.R b/R/module_nested_tabs.R
index 2648c9751..2be53596c 100644
--- a/R/module_nested_tabs.R
+++ b/R/module_nested_tabs.R
@@ -198,7 +198,7 @@ srv_nested_tabs.teal_module <- function(id, datasets, modules, is_module_specifi
 
     # collect arguments to run teal_module
     args <- c(list(id = "module"), modules$server_args)
-    if (is_arg_used(modules$server, "reporter")) {
+    if (is_arg_used(modules$server, "reporter") && isFALSE(getOption("teal.disable_reporter"))) {
       args <- c(args, list(reporter = reporter))
     }
 
diff --git a/R/module_teal.R b/R/module_teal.R
index b7d11cc54..775358860 100644
--- a/R/module_teal.R
+++ b/R/module_teal.R
@@ -153,7 +153,9 @@ srv_teal <- function(id, modules, teal_data_rv, filter = teal_slices()) {
     )
 
     reporter <- teal.reporter::Reporter$new()
-    if (is_arg_used(modules, "reporter") && length(extract_module(modules, "teal_module_previewer")) == 0) {
+
+    if (is_arg_used(modules, "reporter") && length(extract_module(modules, "teal_module_previewer")) == 0 &&
+        isFALSE(getOption("teal.disable_reporter"))) {
       modules <- append_module(modules, reporter_previewer_module())
     }
 
diff --git a/R/zzz.R b/R/zzz.R
index a85f3f143..09d680caa 100644
--- a/R/zzz.R
+++ b/R/zzz.R
@@ -1,6 +1,6 @@
 .onLoad <- function(libname, pkgname) { # nolint
   # adapted from https://github.com/r-lib/devtools/blob/master/R/zzz.R
-  teal_default_options <- list(teal.show_js_log = FALSE)
+  teal_default_options <- list(teal.show_js_log = FALSE, teal.disable_reporter = FALSE)
 
   op <- options()
   toset <- !(names(teal_default_options) %in% names(op))

edit: This strategy could also be applied only the download button.

@gogonzo
Copy link
Contributor

gogonzo commented Feb 27, 2024

@averissimo yes @pawelru already suggested an option. Please note that issue refers only to "download" button, not to the whole reporter. Anyway, option to hide reporter is also needed. I think this needs "strategic" decision about options structure etc.

IMO it is easier and more robust to clean html classes and allow to use JS to hide elements than to have options to control the UI. Having clear class/id structure will also be beneficial for testing

@gogonzo gogonzo mentioned this issue Jul 2, 2024
63 tasks
@gogonzo gogonzo self-assigned this Aug 5, 2024
@gogonzo
Copy link
Contributor

gogonzo commented Aug 12, 2024

Possible to disable download buttons by including simple js call

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

# pkgload::load_all("teal.data")
pkgload::load_all("teal")
library(teal.modules.general)

data <- teal_data_module(
  once = FALSE,
  ui = function(id) {
    ns <- NS(id)
    tagList(
      numericInput(ns("obs"), "Number of observations to show", 1000),
      actionButton(ns("submit"), label = "Submit")
    )
  },
  server = function(id, ...) {
    moduleServer(id, function(input, output, session) {
      logger::log_trace("example_module_transform2 initializing.")
      eventReactive(input$submit, {
        data <- teal_data() |>
          within(
            {
              logger::log_trace("Loading data")
              ADSL <- head(teal.data::rADSL, n = n)
              ADTTE <- teal.data::rADTTE
              aaa <- iris
              CO2 <- CO2
              factors <- names(Filter(isTRUE, vapply(CO2, is.factor, logical(1L))))
              CO2[factors] <- lapply(CO2[factors], as.character)
            },
            n = as.numeric(input$obs)
          )
        join_keys(data) <- default_cdisc_join_keys[c("ADSL", "ADTTE")]
        teal.data::datanames(data) <- c("CO2", "ADTTE", "aaa", "ADSL")
        data
      })
    })
  }
)

app <- init(
  data = data,
  modules = modules(
    tm_g_association(
      ref = data_extract_spec(
        dataname = "ADSL",
        select = select_spec(
          label = "Select variable:",
          choices = variable_choices(
            "ADSL",
            c("SEX", "RACE", "COUNTRY", "ARM", "STRATA1", "STRATA2", "ITTFL", "BMRKR2")
          ),
          selected = "RACE",
          fixed = FALSE
        )
      ),
      vars = data_extract_spec(
        dataname = "ADSL",
        select = select_spec(
          label = "Select variables:",
          choices = variable_choices(
            "ADSL",
            c("SEX", "RACE", "COUNTRY", "ARM", "STRATA1", "STRATA2", "ITTFL", "BMRKR2")
          ),
          selected = "BMRKR2",
          multiple = TRUE,
          fixed = FALSE
        )
      )
    ),
    tm_g_association(
      ref = data_extract_spec(
        dataname = "ADSL",
        select = select_spec(
          label = "Select variable:",
          choices = variable_choices(
            "ADSL",
            c("SEX", "RACE", "COUNTRY", "ARM", "STRATA1", "STRATA2", "ITTFL", "BMRKR2")
          ),
          selected = "RACE",
          fixed = FALSE
        )
      ),
      vars = data_extract_spec(
        dataname = "ADSL",
        select = select_spec(
          label = "Select variables:",
          choices = variable_choices(
            "ADSL",
            c("SEX", "RACE", "COUNTRY", "ARM", "STRATA1", "STRATA2", "ITTFL", "BMRKR2")
          ),
          selected = "BMRKR2",
          multiple = TRUE,
          fixed = FALSE
        )
      )
    )
  ),
  header = tags$p(tags$script("
    $(document).ready(function() {
      var elements = document.querySelectorAll(\".single_report_button,button[id$='download_button']\");
      elements.forEach(function(element) {
        element.style.display = 'none';
      });
    })
  "))
)

runApp(app)

@gogonzo gogonzo closed this as completed Aug 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
core enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants