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?
Related
I am trying to run a GET request inside a shinyApp, but I don't want to wait for the response as it would take quite a long time to process and I dont need really need the response inside the shinyApp, although a status code would be nice, but it is not obligatory.
Or is there maybe a function, that sends an async request? Like wrapping the whole GET inside a future/promise?
Currently I have this observeEvent in my shinyApp:
observeEvent(input$import, {
httr::GET(url = "https://someurl/that/takes/a/long/time")
})
Is the curl_fetch_multi from the curl package suited for that task?
Here is a way to run GET asynchronously and in a intra-session non-blocking manner (observer returning nothing):
library(shiny)
library(future)
library(promises)
library(future.callr)
library(httr)
plan(callr)
queryGoogle <- function(queryString) {
myResponse <- httr::GET("http://google.com/", path = "search", query = list(q = queryString))
return(myResponse)
}
ui <- fluidPage(
br(),
textOutput("time_output"),
br(),
textInput(inputId="query_input", label = NULL, value = "", placeholder = "Search google..."),
actionButton("import", "Query"),
hr(),
textOutput("query_output")
)
server <- function(input, output, session) {
futureData <- reactiveValues(response = NULL)
observeEvent(input$import, {
myFuture <- future({
queryGoogle(isolate(input$query_input))
})
then(
myFuture,
onFulfilled = function(value) {
futureData$response <- value
},
onRejected = NULL
)
return(NULL)
})
output$query_output <- renderPrint({
req(futureData$response)
})
time <- reactive({
invalidateLater(500, session)
Sys.time()
})
output$time_output <- renderText({ paste("Something running in parallel:", time()) })
}
shinyApp(ui, server)
This is a slight modification of my answer here.
Please also read Joe Cheng's related answer here carefully.
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(" ", " ", 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
))
}
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)
I want to initialize the download of a file in R Shiny when a button is pressed and do some checks before generating the file.
I've fooled arround with the downloadHandler (https://shiny.rstudio.com/gallery/file-download.html). But I want to catch the event of another button, do some things and checks with the data and when everything went well generate the file and initialize the download without having to press the download button from downloadHandler.
I've implemented most checks for now in the downloadHandler, but it now generates a failed download when some checks aren't fulfilled. I don't like the behavior.
output$downloadData <- downloadHandler(
filename = function() { paste("DATA_EXPORT-", Sys.Date(), ".csv", sep="")
},
content = function(file) {
withProgress(message = 'Export data', value = 0, {
# Number of steps
n <- 3
incProgress(1/n, detail = "Pre checks and get data")
# checks if inputs for get_data are well defined
dataSet <- get_data(blabla)
incProgress(1/n, detail = "Post Proces and check")
incProgress(1/n, detail = "generate flatfile")
write.csv(dataSet, file, row.names = FALSE)
})
}
)
To elaborate my comment, a minimal example:
library(shiny)
library(shinyjs)
# function which checks the data; returns TRUE or FALSE
checkData <- function(dat){
TRUE
}
# function which transforms the data; returns NULL if check not TRUE
processData <- function(dat){
if(checkData(dat)){
# do something with dat
names(dat) <- toupper(names(dat)) # for our example
return(dat)
}else{
return(NULL)
}
}
ui <- fluidPage(
useShinyjs(),
conditionalPanel(
"false", # always hide the download button
downloadButton("downloadData")
),
actionButton("check", "Download")
)
server <- function(input, output, session){
dat <- mtcars
finalData <- reactiveVal() # to store the processed data
observeEvent(input$check, {
if(!is.null(df <- processData(dat))){
finalData(df)
runjs("$('#downloadData')[0].click();")
}else{
# something which throws an alert message "invalid data"
# (eg with shinyBS::createAlert or shinyWidgets::sendSweetAlert)
}
})
output$downloadData <- downloadHandler(
filename = function() {
paste("data-", Sys.Date(), ".csv", sep="")
},
content = function(file) {
write.csv(finalData(), file)
}
)
}
shinyApp(ui, server)
I have created a shiny app that uses session$clientData to get parameter values to the server. It works great, however, I would also like to be able to initiate a download through the url, e.g:
localhost:8100/?plot=a&title=mytitle&download=1
and then in server.R, something like:
if(session$clientData$download == "1"){
download()
}
Hence, is it possible to initiate the downloadHandler() in server.R?
Thanks!
I am not sure I have correctly understood what you are trying to do. What I have understood is that you would like a download to be initiated when the query string download=1 is present in the url. You could do this by injecting some javascript to open the link when the required query string is detected. There will be some problems however.
Your browser will most likely block the pop up. You will need to wait a sufficient length of time before you fire the code (I have chosen 5 seconds).
require(shiny)
runApp(list(
ui = bootstrapPage(
tags$head(tags$script(HTML('
Shiny.addCustomMessageHandler("jsCode",
function(message) {
eval(message.value);
}
);
'))),
downloadLink('downloadData', 'Download'),
verbatimTextOutput("summary")
),
server = function(input, output, session) {
data <- seq(100)
output$downloadData <- downloadHandler(
filename = function() {
paste('data-', Sys.Date(), '.csv', sep='')
},
content = function(file) {
write.csv(data, file)
}
)
output$summary <- renderText({
cnames <- names(session$clientData)
allvalues <- lapply(cnames, function(name) {
item <- session$clientData[[name]]
if (is.list(item)) {
list_to_string(item, name)
} else {
paste(name, item, sep=" = ")
}
})
paste(allvalues, collapse = "\n")
})
observe({
query <- parseQueryString(session$clientData$url_search)
if(!is.null(query$download)){
if(query$download == 1){
jsinject <- "setTimeout(function(){window.open($('#downloadData').attr('href'))}, 5000);"
session$sendCustomMessage(type = 'jsCode', list(value = jsinject))
}
}
})
}
))