SafetyGraphics / safetyGraphics

Clinical Trial Safety Graphics with R
https://safetygraphics.github.io/safetyGraphics/
Other
93 stars 24 forks source link

Error when using `safetyGraphicsInit()` in non-interactive R Session #674

Closed paulinebaur closed 2 years ago

paulinebaur commented 2 years ago

Hello everybody,

I would like to share the safetyGraphics app within my organization and therefore tried to host it using an Ubuntu Shiny Server.

My app contains the code from safetyGraphicsInit() (I defined the objects charts, delayTime and maxFileSize at the beginning of the script and then pasted the code of the safetyGraphicsInit() function below). The app starts to run on the server, loads required packages and charts, but then an error occurs.

The last two lines of the log-file are:

Couldn't get a file descriptor referring to the console
Execution halted

Has anybody faced similar problems in the past? I’d appreciate any help or suggestions on how to solve this issue.

Thanks for your help in advance!

jwildfire commented 2 years ago

@elimillera @xni7 Any chance you've seen this error?

xni7 commented 2 years ago

No, I can't reproduce this error. The following adaptation to safetyGraphicsInit() worked for me. Perhaps @paulinebaur can provide a reproducible example?

library(safetyData)
library(safetyGraphics)
library(shiny)
library(dplyr)
library(purrr)
library(shinyjs)

charts=makeChartConfig() 
delayTime=1000 
maxFileSize = NULL

#safetyGraphicsInit <- function(charts=makeChartConfig(), delayTime=1000, maxFileSize = NULL){

charts_init<-charts
all_domains <- charts_init %>% map(~.x$domain) %>% unlist() %>% unique()

#increase maximum file upload limit
if(!is.null(maxFileSize)){
  options(shiny.maxRequestSize=(maxFileSize*1024^2))
  if(maxFileSize > 100){
    message("NOTE: Loading very large files may cause performance issues in the safetyGraphics app.")
  }
}

css_path <- system.file("www","index.css", package = "safetyGraphics")
app_css <-  HTML(readLines(css_path))

ui<-fluidPage(
  useShinyjs(),
  tags$head(tags$style(app_css)),
  div(
    id="init",
    titlePanel("safetyGraphics Initializer"),
    sidebarLayout(
      position="right",
      sidebarPanel(
        h4("Data Loader"),
        all_domains %>% map(~loadDataUI(.x, domain=.x)),
        textOutput("dataSummary"),
        hr(),
        shinyjs::disabled(
          actionButton("runApp","Run App",class = "btn-block")
        )
      ),
      mainPanel(  
        p(
          icon("info-circle"),
          "First, select charts by dragging items between the lists below. Next, load the required data domains using the controls on the right. Finally, click Run App to start the safetyGraphics Shiny App. Reload the webpage to select new charts/data.",
          class="info"
        ),
        loadChartsUI("load-charts", charts=charts_init),
      )
    ),
  ),
  shinyjs::hidden(
    div(
      id="sg-app",
      uiOutput("sg")
    )
  )
)

server <- function(input,output,session){ 
  #initialize the chart selection moduls
  charts<-callModule(loadCharts, "load-charts",charts=charts_init) 
  domainDataR<-all_domains %>% map(~callModule(loadData,.x,domain=.x))
  names(domainDataR) <- all_domains
  domainData<- reactive({domainDataR %>% map(~.x())})

  current_domains <- reactive({
    charts() %>% map(~.x$domain) %>% unlist() %>% unique()
  })

  observe({
    for(domain in all_domains){
      if(domain %in% current_domains()){
        shinyjs::show(id=paste0(domain,"-wrap"))
      }else{
        shinyjs::hide(id=paste0(domain,"-wrap"))
      }
    }
  })

  initStatus <- reactive({
    currentData <- domainData()
    chartCount<-length(charts())
    domainCount<-length(current_domains())
    loadCount<-sum(currentData %>% map_lgl(~!is.null(.x)))
    notAllLoaded <- sum(currentData %>% map_lgl(~!is.null(.x))) < domainCount
    ready<-FALSE
    if(domainCount==0){
      status<-paste("No charts selected. Select one or more charts and then load domain data to initilize app.")
    }else if(notAllLoaded) {
      status<-paste(chartCount, " charts selected. ",loadCount," of ",domainCount," data domains loaded. Load remaining data domains to initialize app.")
    }else{
      status<-paste("Loaded ",loadCount," data domains for ",chartCount," charts. Click 'Run App' button to initialize app.")
      ready<-TRUE
    }
    return(
      list(
        status=status,
        ready=ready
      )
    )
  })

  output$dataSummary <- renderText({initStatus()$status})
  observe({
    if(initStatus()$ready){
      shinyjs::enable(id="runApp")
    } else {
      shinyjs::disable(id="runApp")
    }
  })

  observeEvent(input$runApp,{
    shinyjs::hide(id="init")
    shinyjs::show(id="sg-app")
    config<- app_startup(
      domainData = domainData() %>% keep(~!is.null(.x)),
      meta = NULL, 
      charts= charts(),
      #mapping=NULL, 
      filterDomain="dm", 
      autoMapping=TRUE, 
      #chartSettingsPaths = NULL
    )

    output$sg <- renderUI({
      safetyGraphicsUI(
        "sg",
        config$meta, 
        config$domainData, 
        config$mapping, 
        config$standards
      )    
    })

    # delay is needed to get the appendTab in mod_chartsNav to trigger properly 
    shinyjs::delay(
      delayTime,
      callModule(
        safetyGraphicsServer,
        "sg",
        config$meta, 
        config$mapping, 
        config$domainData, 
        config$charts, 
        config$filterDomain
      )
    )
  })
}

app <- shinyApp(ui = ui, server = server)
runApp(app, launch.browser = TRUE)
#}
paulinebaur commented 2 years ago

Hello @xni7, thank you for your answer. My code is below, it is almost identical to yours except for the loaded packages. I’ll try to figure out if your code solves my problem and get back in touch.

library(shiny)
require(safetyGraphics)
library(tidyverse)
library(shinyjs)
library(yaml)

charts=makeChartConfig()
delayTime=5000
maxFileSize = NULL

charts_init<-charts
all_domains <- charts_init %>% map(~.x$domain) %>% unlist() %>% unique()

#increase maximum file upload limit
if(!is.null(maxFileSize)){
  options(shiny.maxRequestSize=(maxFileSize*1024^2))
  if(maxFileSize > 100){
    message("NOTE: Loading very large files may cause performance issues in the safetyGraphics app.")
  }
}

css_path <- system.file("www","index.css", package = "safetyGraphics")
app_css <-  HTML(readLines(css_path))

ui<-fluidPage(
  useShinyjs(),
  tags$head(tags$style(app_css)),
  div(
    id="init",
    titlePanel("safetyGraphics Initializer"),
    sidebarLayout(
      position="right",
      sidebarPanel(
        h4("Data Loader"),
        all_domains %>% map(~loadDataUI(.x, domain=.x)),
        textOutput("dataSummary"),
        hr(),
        shinyjs::disabled(
          actionButton("runApp","Run App",class = "btn-block")
        )
      ),
      mainPanel(  
        p(
          icon("info-circle"),
          "First, select charts by dragging items between the lists below. Next, load the required data domains using the controls on the right. Finally, click Run App to start the safetyGraphics Shiny App. Reload the webpage to select new charts/data.",
          class="info"
        ),
        loadChartsUI("load-charts", charts=charts_init),
      )
    ),
  ),
  shinyjs::hidden(
    div(
      id="sg-app",
      uiOutput("sg")
    )
  )
)

server <- function(input,output,session){ 
  #initialize the chart selection moduls
  charts<-callModule(loadCharts, "load-charts",charts=charts_init) 
  domainDataR<-all_domains %>% map(~callModule(loadData,.x,domain=.x))
  names(domainDataR) <- all_domains
  domainData<- reactive({domainDataR %>% map(~.x())})

  current_domains <- reactive({
    charts() %>% map(~.x$domain) %>% unlist() %>% unique()
  })

  observe({
    for(domain in all_domains){
      if(domain %in% current_domains()){
        shinyjs::show(id=paste0(domain,"-wrap"))
      }else{
        shinyjs::hide(id=paste0(domain,"-wrap"))
      }
    }
  })

  initStatus <- reactive({
    currentData <- domainData()
    chartCount<-length(charts())
    domainCount<-length(current_domains())
    loadCount<-sum(currentData %>% map_lgl(~!is.null(.x)))
    notAllLoaded <- sum(currentData %>% map_lgl(~!is.null(.x))) < domainCount
    ready<-FALSE
    if(domainCount==0){
      status<-paste("No charts selected. Select one or more charts and then load domain data to initilize app.")
    }else if(notAllLoaded) {
      status<-paste(chartCount, " charts selected. ",loadCount," of ",domainCount," data domains loaded. Load remaining data domains to initialize app.")
    }else{
      status<-paste("Loaded ",loadCount," data domains for ",chartCount," charts. Click 'Run App' button to initialize app.")
      ready<-TRUE
    }
    return(
      list(
        status=status,
        ready=ready
      )
    )
  })

  output$dataSummary <- renderText({initStatus()$status})
  observe({
    if(initStatus()$ready){
      shinyjs::enable(id="runApp")
    } else {
      shinyjs::disable(id="runApp")
    }
  })

  observeEvent(input$runApp,{
    shinyjs::hide(id="init")
    shinyjs::show(id="sg-app")
    config<- app_startup(
      domainData = domainData() %>% keep(~!is.null(.x)),
      meta = NULL, 
      charts= charts(),
      #mapping=NULL, 
      filterDomain="dm", 
      autoMapping=TRUE, 
      #chartSettingsPaths = NULL
    )

    output$sg <- renderUI({
      safetyGraphicsUI(
        "sg",
        config$meta, 
        config$domainData, 
        config$mapping, 
        config$standards
      )    
    })

    # delay is needed to get the appendTab in mod_chartsNav to trigger properly 
    shinyjs::delay(
      delayTime,
      callModule(
        safetyGraphicsServer,
        "sg",
        config$meta, 
        config$mapping, 
        config$domainData, 
        config$charts, 
        config$filterDomain
      )
    )
  })
}

app <- shinyApp(ui = ui, server = server)
runApp(app, launch.browser = TRUE)
paulinebaur commented 2 years ago

Unfortunately, I am still running into the same error with your code, @xni7. I will paste the contents of my log file below, hopefully it can help to explain my issue.

Attaching package: ‘dplyr’

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union

Attaching package: ‘shinyjs’

The following object is masked from ‘package:shiny’:

    runExample

The following objects are masked from ‘package:methods’:

   removeClass, show

Loading required package: safetyCharts
- Found the {safetyCharts} package and added /opt/data/R/R-4.0.3/lib/R/library/safetyCharts/config to list of chart locations.
aeExplorer: Loaded 4 functions: init_aeExplorer,ui,server,main
aeTimelines: Loaded 4 functions: init_aeTimelines,ui,server,main
demogRTF: Loaded 5 functions: demogRTF_server,demogRTF_ui,ui,server,main
hepExplorer: Loaded 5 functions: hepExplorer,hepExplorer,ui,server,main
labdist: Loaded 5 functions: lab_distribution_ui,lab_distribution_server,ui,server,main
paneledOutlierExplorer: Loaded 4 functions: init_paneledOutlierExplorer,ui,server,main
qTexplorer: Loaded 5 functions: QT_OutlierExplorer_ui,QT_OutlierExplorer_server,ui,server,main
QTpaneledOutlierExplorer: Loaded 4 functions: init_paneledOutlierExplorer,ui,server,main
QTsafetyDeltaDelta: Loaded 3 functions: ui,server,main
QTsafetyHistogram: Loaded 3 functions: ui,server,main
QTsafetyOutlierExplorer: Loaded 4 functions: init_safetyOutlierExplorer,ui,server,main
QTsafetyResultsOverTime: Loaded 4 functions: init_safetyResultsOverTime,ui,server,main
QTsafetyShiftPlot: Loaded 4 functions: init_safetyShiftPlot,ui,server,main
safetyDeltaDelta: Loaded 3 functions: ui,server,main
safetyHistogram: Loaded 3 functions: ui,server,main
safetyOutlierExplorer: Loaded 4 functions: init_safetyOutlierExplorer,ui,server,main
safetyOutlierExplorerModule: Loaded 5 functions: safetyOutlierExplorer_ui,safetyOutlierExplorer_server,ui,server,main
safetyOutlierExplorerStatic: Loaded 4 functions: safety_outlier_explorer,ui,server,main
safetyResultsOverTime: Loaded 4 functions: init_safetyResultsOverTime,ui,server,main
safetyResultsOverTimeStatic: Loaded 4 functions: safety_results_over_time,ui,server,main
safetyShiftPlot: Loaded 4 functions: init_safetyShiftPlot,ui,server,main
tendril: Loaded 4 functions: tendril_chart,ui,server,main
- Found 22 charts in the following location(s): /opt/data/R/R-4.0.3/lib/R/library/safetyCharts/config
  * aeExplorer
  * aeTimelines
  * demogRTF
  * hepExplorer
  * labdist
  * paneledOutlierExplorer
  * qTexplorer
  * QTpaneledOutlierExplorer
  * QTsafetyDeltaDelta
  * QTsafetyHistogram
  * QTsafetyOutlierExplorer
  * QTsafetyResultsOverTime
  * QTsafetyShiftPlot
  * safetyDeltaDelta
  * safetyHistogram
  * safetyOutlierExplorer
  * safetyOutlierExplorerModule
  * safetyOutlierExplorerStatic
  * safetyResultsOverTime
  * safetyResultsOverTimeStatic
  * safetyShiftPlot
  * tendril
This version of bslib is designed to work with rmarkdown version 2.7 or higher.

Listening on http://127.0.0.1:5082
Couldn't get a file descriptor referring to the console

Execution halted
xni7 commented 2 years ago

it looks like you are running this interactively in Workbench/RStudio? The error msg Couldn't get a file descriptor referring to the console seems to have to do with Linux OS settings . In RStudio console (installed on the Ubuntu server), you might try utils::browseURL("https://google.com") to see if there is a similar issue. browseURL mentioned about xdg-open. https://stat.ethz.ch/R-manual/R-devel/library/utils/html/browseURL.html

paulinebaur commented 2 years ago

Thanks for your reply.

I’ve entered the following in the R console:

utils::browseURL(https://google.com)

Nothing happened. But there is no error message, too.

The server is a pure terminal server without graphical interface. So it can’t open a browser (locally). Other shiny apps are running without problems on the shiny server. Does the safetyGraphics app require access to a local file or to the internet?

elimillera commented 2 years ago

Hey @paulinebaur Can you try to remove the launch.browser = TRUE argument when you call runApp? That's the first thing that comes to mind.

paulinebaur commented 2 years ago

Hello @elimillera, removing the runApp() function completely did it and the app runs now. Thanks for your advice.

elimillera commented 2 years ago

Thanks @paulinebaur, I've renamed the issue to make it a little more searchable to anyone who runs into this in the future.