R: Introducing Automation in Shiny App - r

I want to know the way to build a Shiny App which automatically runs functions in a scheduled time. I read this but it doesn't seem relevant to my case.
I created a function to update a google sheet, using the package googlesheet. Now the app ui looks like this.
Clicking "Manually update" button enables the app to run the functions and update the googlesheet.
But I want the app to run the same function everyday at 1pm, even without clicking "Manually update" button, while "Activate auto update" button is on.
So to summarise, I want to know
the way to automatically execute the functions at a scheduled time in shiny app.
the way to save the user input of "Activate auto update". That is to say, once you turn on this button, even if you close this app and open it again, it stays to be "on". Once you turn off this button, it stays to be "off" until the user turn it on again, even if the user shuts off the app.
I'd appreciate for any kinds of help!
ui.R
navbarPage("Automated Report Generator", id = "nav",
tabPanel("ITM",
sidebarLayout(
sidebarPanel(materialSwitch(inputId = "activateAutoITM",
label = "Activate auto update",
status = "primary", right = FALSE),
actionButton("ITM_update", "Manually update"),
width = 2),
mainPanel(div(a("Visit ITM Google Sheet",
href = "https://docs.google.com/spreadsheets/d/#########",
target="_blank")),
div(htmlOutput("gsheet_text_ITM"))
)
) # sidebarLayout
)) # navbarPage
server.R
shinyServer(function(input, output, session) {
observeEvent(input$ITM_update, {
####### Here are the functions to update google sheet########
####### But I'll just omit because of the security#########
output$gsheet_text_ITM <- renderUI({
str1 <- paste("Calculation finised.")
HTML(paste(str1))
})
}) # shinyServer

Related

Shiny observeEvent shows first event, but not second, third

TL;DR: observeEvent is only working on the first instance, but not subsequent.
Explanation:
In a small shiny app below, I dynamically build a URL based on user input, which points to the corresponding pre-rendered "timer" gif images I have hosted on GH. In this shiny app code below, observeEvent works great to pull the sliderInput value (e.g. 5sec), build a URL, and then clicking 'go' will show the timer using a clever shinyjs package. However, if I do not change the input (still 5sec), clicking go doesn't rebuild the image. If I change the value to a different number (4sec), it will show the correct gif. If I change back to the original number (5sec), no gif. If I go to a new number (3sec), correct gif. If I print the value of input$time or of rv$time in my observeEvent, then each of those values are updating correctly (both print the corresponding value).
Goal: to show the gif that corresponds to the input$time upon each update of input$go
Reprex:
library(shiny)
library(tidyverse)
library(glue)
library(shinyjs)
# Define UI
ui <- navbarPage(title = "Reprex",
## RHYME TIME -----
tabPanel("Time",
useShinyjs(),
sidebarLayout(
sidebarPanel(
sliderInput("time",
"Seconds",
min = 3,
max = 10,
value = 5
),
actionButton(inputId = "go",
label = "Go"),
),
mainPanel(
HTML("<center>"),
shinyjs::hidden(htmlOutput("simple_timer")),
HTML("</center>")
)
)
)
)
# Define server logic
server <- function(input, output, session) {
#a container to store my time var
rv <- reactiveValues(
time = 0
)
#the event that is triggered by input$go getting clicked
observeEvent(input$go, {
rv$time <- input$time #this should update rv$time after go is clicked
shinyjs::show("simple_timer") #this is the clever package with simple show/hide funs
})
#the reactive text that generates my HTML
output$simple_timer <- renderText({
glue::glue('<img src ="https://github.com/matthewhirschey/time_timer/raw/main/data/{rv$time}_sec.gif",
align = "center",
height="50%",
width="50%">')
})
}
# Run the application
shinyApp(ui = ui, server = server)
There are a couple of issues with your code:
renderText won't refire if you press input$go again (w/o changing the slider). Becasue the idea is that observer/render fires whenever their reactives change. As your renderText depends on rv$time which does not change when input$time does not change, the render function is not fired on subsequent button presses. This can be remedied by including input$go in the render function setting an additional dependency on the button.
This will not, however, solve your problem, because the browser uses caching. It sees that the <img> tag did not change (same src), thus it does not reload the picture. To circumvent that you can use the trick from Disable cache for some images by adding a timestamp to the src.
To make a long story short, this code does the trick:
output$simple_timer <- renderText({
input$go # make code dependent on the button
# add `?timestamp=<timestamp>`to the src URL to convince the browser to reload the pic
glue::glue('<img src ="https://github.com/matthewhirschey/time_timer/raw/main/data/{rv$time}_sec.gif?timestamp={Sys.time()}",
align = "center",
height="50%",
width="50%">')
})

Start R Shiny app as a background job programmatically

Dean Attali has provided a wonderful example on how to exit elegantly from a Shiny app using a close button which both closes the browser window and ends the Shiny session. Consider the following example (modification of the original code from Dean):
The ui.r:
library(shiny)
library(shinyjs)
jscode <- "shinyjs.closeWindow = function() { window.close(); }"
ui <- fluidPage(
useShinyjs(),
extendShinyjs(text = jscode, functions = c("closeWindow")),
htmlOutput(outputId = "exitHeading"),
actionButton(inputId = "closeGUI", label = "Exit")
)
The server.r:
library(shiny)
library(shinyjs)
server <- function(input, output, session) {
output$exitHeading <- renderText("Press the button below to exit the app")
observeEvent(input$closeGUI, {
js$closeWindow()
stopApp()
})
}
And running the app:
runApp(appDir = "/tmp")
My question is about how to start a Shiny app as a background job programmatically, so that the RStudio console is free for further use (or even start a second Shiny app in parallel) while the app is still running, and then end the job using the exit button from the app above. I am looking for a solution which can be added to a package which contains a Shiny app, like this one.
I have read this and have tried the provided sample app, but it still requires manual intervention by the user.
Can someone assist with this?
So as I mentioned in the comments you can achieve this by using the system which basically runs a terminal command, with the wait and show.output.on.console flags set to FALSE.
system('Rscript file.r', wait=F, show.output.on.console = F)
# if you want to access a file from in a package u need
# also in the source of the package you need to put the
# folder `directory` in `root.of.package/inst`
p <- system.file(file.path("directory", "myfile.r"), package = "my.package")
system(paste0('Rscript "', p, '"'), wait=F)

R Shiny Randomly Generated Radio Button Choices

I'm creating a survey in Shiny where I want the choices as represented by radio buttons to be randomly drawn. See sample code below:
library(shiny)
choices<-c("a","b","c","d","e")
x1<-sample(choices,2)
x2<-sample(choices,2)
ui<-fluidPage(
radioButtons("q1","Which do you prefer?", choices=c(x1,"No Preference"),selected=""),
radioButtons("q2","Which do you prefer?", choices=c(x2,"No Preference"),selected="")),
server <- function(input, output) {
# Not important
})
shinyApp(ui = ui, server = server)
When I run the app locally a different set of choices will be selected each time I run the app (as intended). But once I publish to shinyapps.io the choices are the same each time (as in x1 is the same each time I run the app). How can I make so that each time the app opens a new sample is taken from the choice set?
As you may know, Shiny server implements caching to improve system utilization. The problem you are having is the x1 variable is being sampled and cached between users.
The way around this problem is to sample choices into a reactiveValue and then dynamically update the radio buttons when the page loads.
Alternatively, you might try using this approach:
ui <- function(req) {
fluidPage(
x1<-sample(choices,2)
x2<-sample(choices,2)
radioButtons("q1","Which do you prefer?", choices=c(x1,"No Preference"),selected=""),
radioButtons("q2","Which do you prefer?", choices=c(x2,"No Preference"),selected=""))
...
)
}

R Shiny : Refreshing plot when entering input OR pressing an action button

I'm fairly new to R Shiny but am stuck with the following problem for some time now and hope you can provide some help:
I try to refresh a plot in R shiny that should be refreshed if either a new input argument is entered OR an action button is pressed. This should be straightforward, but unfortunately I can't solve it, despite googling/reading instructions for some time. Any advice would be recommended. Any solutions on the web seem to put the whole renderplot function inside the observeEvent function, but I also need the renderplot in addition outside of it to account for the possibility of just entering inputs without pressing the action button.
I have no trouble creating a (render)plot that either exclusively is refreshed when entering a new input or exclusively refreshed when pressing a button.
However when doing both at the same I fail: I first tried to copy the renderplot function including the resulting output twice one time within an observeEvent function (to account for clicking the action button) and one time outside of an observeEvent (to account for only refreshing the inputs to the plot) but this leads only to a greyed out graph that refreshes after ~10 seconds delay when pressing the action button. I imagine adding the reactive click input generated from clicking the action button directly to the renderplot outside of observe event , but so far I couldn't get it to run. Any recommendations would be much appreciated.
Thank you in advance.
Like this?:
Edit: No need to pass the selectInput to the reactive Vals.. this does the same:
library(shiny)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectInput(inputId="select", label="title", choices=LETTERS[1:3], selected = "A"),
actionButton(inputId="btn", label="refresh")
),
mainPanel(
plotOutput("scatterPlot")
)
)
)
server <- function(input, output) {
plotSettings <- reactiveValues()
observeEvent(c(input$btn, input$select), {
plotSettings$values <- runif(100,1,100)
# plotSettings$title <- input$select
}, ignoreNULL = FALSE)
output$scatterPlot <- renderPlot({
plot(plotSettings$values, main=input$select)
})
}
shinyApp(ui = ui, server = server)

R Shiny: Is there a way to check if a button is disabled using shinyjs package?

Is there a way to check if a download button is disabled using the shinyjs R package? I want to use shinyjs or something similar because they have very easy syntax. This is my package:
server.R:
library(shiny)
library(shinyjs)
library(shinyBS)
shinyServer(function(input, output) {
observe({
shinyjs::disable("download1")
if(shinyjs::is.disabled("download1")){ ## This is what I am trying to do
# Do something
}
})
})
ui.R
shinyUI(fluidPage(
downloadButton("download1")
))
Not directly (well, not easily*).
Buttons can only be disabled when you decide to disable them, so you can have some sort of a reactive variable that holds whether or not the button should be disabled, and whenever you disable the button, you also change the value of that variable. In order to make sure they stay in sync, every time you want to disable the button you can set the variable to mirror that, and then you can use shinyjs::toggleState(condition = variable) so that the disabled state will mirror what the variable says.
Example code to illustrate what I mean:
library(shiny)
ui <- fluidPage(
shinyjs::useShinyjs(),
numericInput("num", "When divisible by 3, disable the button", 1),
actionButton("btn", "button")
)
server <- function(input, output, session) {
values <- reactiveValues(disable = FALSE)
observe({
values$disable <- (input$num %% 3 == 0)
})
observe({
shinyjs::toggleState("btn", condition = !values$disable)
})
}
shinyApp(ui = ui, server = server)
In this app, whenever you want to disable the button, simply set values$disable to FALSE and to enable the button set it to TRUE. To check if the button is currently on or off at any point in time, you can look at the value of values$disable.
*I'm guessing that you wanted a more direct approach to ask the app a question in real time "hey shiny, is button X currently disabled?". You can do that, but it would involve writing custom javascript code to check for the button state, and for custom code to ask javascript that question and to listen for its response. This would work and be guaranteed to be correct, but it's likely overkill for most cases.

Resources