tryCatch error handling doesn't work in shiny app? - r

I'm doing some touch to my shiny app , the problem i'm encountering is that i can't handle the errors using tryCatch like :
tryCatch({
# expr
},
error = function(e) {
# handle the error
}
I'm using the Apriori algorithm within my application ,when the user choose a dataset ,he can also adjust the values of min-support and min-confidence, but sometimes with some values of these, apriori algorithm returns 0 rules , and the error occurs when trying to plot the graph of the association rules .
Here's a small spinet of my code so far :
Getting the file
...
...
...
Find the association rules :
rules <- reactive({
validate(
need(input$file, "Please choose a data set")
)
transactions = read.transactions(
file = file(input$file$datapath),
format = "basket",
sep = ","
)
minValue <- min(length(transactions),input$visualization)
rules <-
apriori(transactions[0:minValue],
parameter = list(
support = input$min_supp,
confidence = input$min_conf
))
print(length(transactions[0:minValue]))
return(rules)
})
Plot the obtained association rules :
output$graphChart <- renderPlot({
Sys.sleep(1)
validate(
need(input$file, "Please choose a data set")
)
set.seed(42)
# validate(
# need(length(rules()) == 0, "zero rules")
# )
tryCatch({
plot(rules(), method = "graph")
})
error = function(condition){
print('there was an error')
}
})
But nothing changed i still get the error and no message printed in the R studio's console
I tried this but it doesn't help me get rid of the error,
By the way i also get errors on other tabs when no rules found .
EDITED
As Pork mentioned in his comment ,i tried :
output$graphChart <- renderPlot({
Sys.sleep(1)
validate(
need(input$file, "Please choose a data set")
)
set.seed(42)
# validate(
# need(length(rules()) == 0, "zero rules")
# )
tryCatch({
plot(rules(), method = "graph",)
})
error=function(cond) {
message(cond)
return(NA)
}
warning=function(cond) {
message(cond)
# Choose a return value in case of warning
return(NULL)
}
})
And the error persist again,
Can someone help me please ?
Any suggestions or advice would be appreciated!
Thanks.

Here is a small example of how you may use tryCatch block. We shall use showNotification to notify the user of the error
library(shiny)
ui <- fluidPage(
sidebarPanel(width = 2,
selectInput("data","data",choices = c(1,2),selected = 1)
),
mainPanel(
plotOutput("graphChart")
)
)
server <- function(input, output, session) {
rules <- reactive({
if(input$data == 1){
return(mtcars$mpg)
}else{
"some error"
}
})
output$graphChart <- renderPlot({
tryCatch({
plot(rules())
}, warning = function(w) {
showNotification('there was a warning','',type = "error")
return()
}, error = function(e) {
showNotification('there was an error','',type = "error")
return()
}, silent=TRUE)
})
}
shinyApp(ui, server)

Related

check existence of reactive in shiny

I have a shiny app where you can upload some raw data and build a summarizedExperiment (let's call it RAW) or you can upload an already processed summarizedExperiment (let's call it FINAL). Now I need to save in a reactive object RAW or FINAL. I tried something like this:
sumexp_all = reactive({
if(!is.null(RAW())){
list(sumexp_data = RAW()$sumexp_data, sumexp_data_mean = RAW()$sumexp_data_mean, replicates = RAW()$replicates)
}else if(!is.null(input$finalsumexpinput)){
readRDS(file = input$finalsumexpinput$datapath)
}else{
NULL
}
})
But in this case it works only if I have RAW (if I don't have RAW (so should be NULL since there are many req() in the pipeline) and I want to upload FINAL, nothing happens). If reverse the if conditions (before FINAL and then RAW) I can upload the FINAL. What's wrong? Is it correct to evaluate a reactive with is.null()?
I tried something else like this:
sumexp_all = reactive({
checkerror <- tryCatch(RAW(), error = function(e) "empty")
if(checkerror != "empty"){
list(sumexp_data = RAW()$sumexp_data, sumexp_data_mean = RAW()$sumexp_data_mean, replicates = RAW()$replicates)
}else if(!is.null(input$finalsumexpinput)){
readRDS(file = input$finalsumexpinput$datapath)
}else{
NULL
}
})
But the shiny app crashes and returns this error:
Warning: Error in if: argument is of length zero
And that's weird because if I print checkerror it correctly returns "empty" if's empty.
UPDATE:
I made some testing in order to find where is the problem, and the problem is with the RAW() reactive. Basically I tried this code to find the problem:
checkraw = reactive({
if(is.null(RAW())){
"raw is null"
}else{
"raw is not null"
}
})
checkinp = reactive({
if(is.null(input$finalsumexpinput$datapath)){
"input non loaded"
}else{
"input loaded"
}
})
output$printcheck= renderPrint({
paste(checkraw(), ",", checkinp())
})
So what I expect from this code is that if RAW() doesn't exist and I uplaod the rds file, it' printed "raw is null, input loaded", but what happens is that nothing is displayed.
If RAW() exists then it's correctly printed "raw is not null, input not loaded"
Then I tried to print only checkinp() (removing the checkraw() reactive) and then checkinp() is correctly printed.
So it seems to me that the problem is in the evaluation of the RAW() reactive. Do you think that the problem could be that RAW() depends on others reactive variables (so there is a req() inside it).
EDIT:
Here is a reproducible example.
ui <- shinyUI(pageWithSidebar(
headerPanel("check"),
sidebarPanel(
actionButton("Button", "enable raw"),
textInput("text", "write something", "something..."),
br(),
),
mainPanel(
verbatimTextOutput("printcheck"),
verbatimTextOutput("checkfin")
)
))
server <- shinyServer(function(input, output) {
stepa = eventReactive(input$Button, {
rnorm(100)
})
stepb = reactive({
req(stepa())
stepa() * 100
})
output$printcheck = renderPrint({
is.null(stepb())
})
fin = reactive({
if(!is.null(stepb())){
stepb()
}else{input$text}
})
output$checkfin = renderPrint({
fin()
})
})
shinyApp(ui=ui,server=server)
As you can see output$checkfin is printed ONLY if stepb() is not null even if in this case should be printed input$text
I posted the problem on the shiny github page. For those interested, here's the answer.
https://github.com/rstudio/shiny/issues/3533

Error message in Shiny from sourced R script

Is there a way to show happenings/errors/warnings from R script which is sourced inside server part of Shiny in Shiny panel?
Following is the sample code which works fine, but I need to see in Shiny if R throws an error while executing sourced GUI_trials2.R and if possible, a window to stream the happenings, like which line of GUI_trials2.R is running currently.
Sample code -
sidebar <- dashboardSidebar(
sidebarMenu(
menuItem("Required Calcs", tabName = "Requirements")
)
)
uibody <- dashboardBody(
tabItems(
tabItem(tabName = "Requirements", h2("Required Calcs")
,dateInput("ME_DATE_output",label=h2("Execution Date"), value="2020-05-29")
,hr()
,actionButton("calculate", "Calculate this" ))
))
ui = dashboardPage(dashboardHeader(title = "Results"), sidebar, uibody)
server = function(input, output) {
ME_DATE_GUI <- reactive({input$ME_DATE_output})
Code_loc <- "K:/Codes/"
observeEvent(input$calculate, {
ME_DATE <- ME_DATE_GUI()
source(paste0(Code_loc,"GUI_trials2.r"), local = TRUE)
})
}
shinyApp(ui, server)
GUI_trials looks like -
# Use ME_DATE from Shiny
ME_DATE <- as.Date(ME_DATE, format="%Y-%m-%d")
year_N_ME_DATE <- format(ME_DATE,"%Y")
month_N_ME_DATE <- format(ME_DATE,"%m")
month_T_ME_DATE <- months(ME_DATE)
# Location for Outputs
Output_DIR <- "K:/Outputs/"
Output_loc <- paste(Output_DIR,month_N_ME_DATE,". ",month_T_ME_DATE, " ",year_N_ME_DATE,"/",sep="")
success <- "Success"
write.csv(success, paste0(Output_loc,"Success.csv"))
Any help is deeply appreciated!
Use withCallingHandlers()
You can wrap your call to source() as follows and use arbitrary code to handle warnings and messages that arise when the code is run. To handle errors you will need to wrap this again in tryCatch() so your app doesn't crash. For example, you could choose to simply send notifications as follows:
tryCatch(
withCallingHandlers(
source(paste0(Code_loc,"GUI_trials2.r"), local = TRUE),
message = function(m) showNotification(m$message, type = "message"),
warning = function(w) showNotification(w$message, type = "warning")
),
error = function(e) showNotification(e$message, type = "error")
)
You can test this by using something like the following code in your GUI_trials2.R script:
for (i in 1:3) {
warning("This is warning ", i)
Sys.sleep(0.5)
message("This is message", i)
Sys.sleep(0.5)
}
stop("This is a fake error!")
Streaming Output in New Window
The easiest way to do this is to pepper your GUI_trials2.R script with informative calls to message() and then use withCallingHandlers() to output these as above. If you want to be more sophisticated and show these messages in a new window, you could do this by updating a modalDialog(), though this would require the shinyjs package. Here is a basic example:
server = function(input, output) {
ME_DATE_GUI <- reactive({input$ME_DATE_output})
# Show a modal that will be updated as your script is run
observeEvent(input$calculate, {
showModal(modalDialog(
shinyjs::useShinyjs(),
title = "Running my R script",
div("You can put an initial message here", br(), id = "modal_status")
))
Code_loc <- "K:/Codes/"
ME_DATE <- ME_DATE_GUI()
# Run the script and use `withCallingHandlers()` to update the modal.
# add = TRUE means each message will be added to all the previous ones
# instead of replacing them.
tryCatch(
withCallingHandlers(
source(paste0(Code_loc,"GUI_trials2.r"), local = TRUE),
message = function(m) {
shinyjs::html("modal_status", paste(m$message, br()), add = TRUE)
},
warning = function(w) {
shinyjs::html("modal_status", paste(w$message, br()), add = TRUE)
}
),
error = function(e) {
shinyjs::html("modal_status", paste(e$message, br()), add = TRUE)
}
)
})
}
Display Code From source()
The echo = TRUE argument to source() will mean that each expression in the file gets printed in the console. Unfortunately, applying handlers to text as it appears in the console isn't possible in R unless it's a message/warning/error, so echo = TRUE won't be of any use here. However, you could define a custom function, similar to source() which will allow you to handle the code as text before it gets evaluated. Here is an example:
# Default handler just prints the code to console, similar
# to `source(echo = TRUE)`
source2 <- function(file, handler = cli::cat_line, local = FALSE) {
# Copy `source()` method of handling the `local` argument
envir <- if (isTRUE(local))
parent.frame()
else if (isFALSE(local))
.GlobalEnv
else if (is.environment(local))
local
else stop("'local' must be TRUE, FALSE or an environment")
# Read each 'expression' in the source file
exprs <- parse(n = -1, file = file, srcfile = NULL, keep.source = FALSE)
# Apply `handler()` to each expression as text, then
# evaluate the expression as code
for (expr in exprs) {
handler(deparse(expr))
eval(expr, envir)
}
# Return nothing
invisible()
}
This will allow you to do anything you like with the code text before
it gets evaluated. E.g. you could apply some pretty HTML formatting and
then output it as a message, which would allow you to use something very similar to the code above, since withCallingHandlers() would handle
these messages for you:
# Define a function to show a message as code-formatted HTML
html_message <- function(msg) {
with_linebreaks <- paste(msg, collapse = "<br/>")
as_code <- sprintf("<code>%s</code>", with_linebreaks)
spaces_preserved <- gsub(" ", "&nbsp", as_code)
message(spaces_preserved)
}
# Then use very similar code to the above for `server`, so
# something like -
tryCatch(
withCallingHandlers(
source2(file = paste0(Code_loc,"GUI_trials2.r"),
handler = html_message,
local = TRUE),
# ... Same code as in the above example using normal source()
Bonus: Getting Fancy with HTML
If you want to get really fancy you could add some custom HTML formatting to each of your message/warning/error functions, e.g. you could show errors in red like so:
error = function(e) {
shinyjs::html("modal_status", add = TRUE, sprintf(
'<span style = "color: red;">%s</span><br/>', e$message
))
}

why seq() function in shinyApps does not work?

I tried to create a shinyApp with seq() function within the Apps.
header <- dashboardHeader(title = 'Testing' ,titleWidth = 300)
sidebar <- dashboardSidebar(uiOutput("sidebarpanel"), width = 300)
body <- dashboardBody(uiOutput("body"))
uix <- dashboardPage(header, sidebar, body)
serverx <- function(input, output, session){
output$sidebarpanel <- renderUI({
div(
sidebarMenu(id="tabs",
menuItem("Tes 1", tabName = "tes1", icon = icon("dashboard"), selected = TRUE)
)
)
})
output$body <- renderUI({
tabItems(tabItem(tabName = "tes1",
fluidRow(column(2, textInput("s1", "From :", value = 1))
,column(2, textInput("s2", "To", value = 7))
),
textOutput("result")
)
)
})
segment_low <- reactiveValues(ba=NULL)
segment_high <- reactiveValues(ba=NULL)
results <- reactiveValues(ba=NULL)
toListen <- reactive({
list(input$s1, input$s2)
})
observeEvent(toListen(),{
segment_low$ba <- input$s1 %>% as.numeric()
segment_high$ba <- input$s2 %>% as.numeric()
})
observe({
results$ba <- seq(segment_low$ba,segment_high$ba, 1)
})
output$result <- renderText({
results$ba
})
}
shinyApp(uix, serverx)
In that syntax, I would create a variable called results$ba because I want to escalate that value in the next time. But it turns out an error :
Warning: Error in seq.default: 'from' must be of length 1
[No stack trace available]
Could someone help me how to solve this problem? Since this error just happened if I put the reactiveValues to the seq() function, while I input a static input, for example seq(2,5,1) it will not return a error. And I already put the initial value for each input in textInput() function also.
Kindle need your help, developers!
Many Thanks.
The issue is that you're rendering the s1 and s2 inputs server-side. Because of this, the server at the beginning renders them as NULL, and your seq function errors when it gets the NULL value.
The simplest thing to do is to add a req function to prevent your code from evaluating unless it's getting some non-NULL values.
observe({
req(segment_low$ba, segment_high$ba)
results$ba <- seq(segment_low$ba,segment_high$ba, 1)
})
Basically, since you're using observe, which is very eager, you are telling the seq function to evaluate right away. By using the req function, you're stopping the chain of evaluation from happening unless the segment_low$ba and segment_high$ba have non-NULL values.

R shiny error with renderTable

I am building a shiny app for doing some network analyses. I want to calculate properties for the network using a function that is stored in global.R. However, I am not able to get the table output.
Here is the the part of my ui.R where I set the output for the table
# More ui.R above
mainPanel(
tabsetPanel(id = "conditionedPanels",
tabPanel("Network Properties",br(),value = 1,
actionButton('netproperty', label='Calculate Properties',class="btn btn-primary"),
h3(textOutput("Data Summary", container = span)),
tableOutput('prop_table')),
##... More ui.R code below
and here is my server.R:
shinyServer( function(input, output, session) {
## Get the properties
props <- reactive({
if (input$netproperty <= 0){
return(NULL)
}
result <- isolate({
input$netproperty
tryCatch ({
if(input$data_input_type=="example"){
if(input$datasets == "Network1"){
load("data/Network1.rda")
props <- graph_topology(g)
props
} else if (input$datasets == "Network2"){
load("data/Network1.rda")
props <- graph_topology(g)
props
} else if (input$datasets == "Network3"){
el <- read.delim("data/Network3.txt")
g <- graph.data.frame(el, directed = FALSE)
props <- graph_topology(g)
props
} else if (input$datasets == "Network4") {
el <- read.delim("data/Network4.txt")
g <- graph.data.frame(el, directed = FALSE)
props <- graph_topology(g)
props
}
} else if (input$data_input_type=="custom"){
if (is.null(input$dt_file))
return(NULL)
inFile <- input$dt_file
dataDT <- as.matrix(read.delim(inFile$datapah, sep="\t", header = FALSE, fill = TRUE))
g <- graph.data.frame(dataDT, directed = FALSE)
props <- graph_topology(g)
props
}
},
error = function(err) {
print(paste("Select Example or custom data set"))
})
})
result
})
## Output properties table
output$prop_table <- renderTable({
props()
})
}
When I press the button calculate properties, I get the error message always, telling me that I need to select example or custom data. I have tried with custom and example datasets, and the error remains. If I remove the tryCatch command in server.R I get the error of length equal zero. It seems that the function graph_topology in my global.R files is not working properly, but if I run it outside of the shiny app I get a matrix, that I thought it could be easily visualize with renderTable. I have also tried instead of using uiOutput in the ui.R using tableOutput but I have the same problem.

In R what is the difference between message and sink to stderr

This question is inspired by this post where printouts from a called functions are displayed inside a shiny app when the code is running.
My question is basically, what is the difference between:
message('hello')
#and
sink(file=stderr())
cat('hello')
In the documentation for message it says that:
The default handler sends the message to the stderr() connection.
I haven't found a way to illustrate the difference in just R without shiny , but in this example the 2 functions behave differently
library(shiny)
library(shinyjs)
myPeriodicFunction1 <- function(){
for(i in 1:5){
msg <- paste(sprintf("[1] Step %d done.... \n",i))
message(msg)
Sys.sleep(1)
}
}
myPeriodicFunction2 <- function(){
for(i in 1:5){
msg <- paste(sprintf("[2] Step %d done.... \n",i))
cat(msg)
Sys.sleep(1)
}
}
runApp(shinyApp(
ui = fluidPage(
shinyjs::useShinyjs(),
actionButton("btn1","Message"),
actionButton("btn2","Sink to stderr"),
textOutput("text")
),
server = function(input,output, session) {
observeEvent(input$btn1, {
withCallingHandlers({
shinyjs::text("text", "")
myPeriodicFunction1()
},
message = function(m) {
shinyjs::text(id = "text", text = m$message, add = FALSE)
})
})
observeEvent(input$btn2, {
withCallingHandlers({
shinyjs::text("text", "")
sink(file=stderr())
myPeriodicFunction2()
sink()
},
message = function(m) {
shinyjs::text(id = "text", text = m$message, add = FALSE)
})
})
}
))
Can anyone help me straighten this out?

Resources