Re-submit MySQL Calls, then refresh data when app is refreshed - r

I have written an app that calls a stored procedure to update a table in MySQL, then queries the new table (both using RMySQL), does some data manipulation and plots a bunch of graphs.
I designed the app to include a refresh button, with the assumption that when the page is refreshed shiny would re-query the database and re-calculate all the values for the outputs. After testing this, however, it is not doing so (and keeps the old values from pre- refresh). I've tried closing the connection to the database when the refresh button is pressed, then re-opening it, but this just causes the app to disconnect.
My question is why does it remember the old values rather than update them, and is there some way i can tell it to re-do all of it after a refresh? Apologies if this is a really basic question, anything that could point me in the right direction would be great.
I've included an example of the relevant chunks below, although not sure how useful that will be!
library(RMySQL)
library(shiny)
library(shinyjs)
con = dbConnect(RMySQL::MySQL(), dbname="abfd",
username="abc",password="abc123", host='blah, port=3306))
con2 = dbConnect(RMySQL::MySQL(), dbname="abfd",
username="abc",password="abc123", host='blah, port=3306))
frfr<-dbGetQuery(con2, 'CALL Updatedata();' )
Data<-as.data.frame(dbGetQuery(con,'SELECT Date, Age,Name FROM Results
WHERE Date >= DATE(NOW()) - INTERVAL 7 DAY ORDER BY Date Asc;'))
jsResetCode <- "shinyjs.reset = function() {history.go(0)}"
ui <- fluidPage(
shinyjs::useShinyjs(),
extendShinyjs(text = jsResetCode),
actionButton('Refresh','Refresh Data')
)
server <- function(input, output,session) {
observeEvent(input$Refresh,{
{js$reset()}
})
}
shinyApp(ui=ui, server=server)

Related

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)

reactiveFileReader in Shiny for .RData

My current workflow in a shiny application is to run a R script as a cron job periodically to pull various tables from multiple databases as well as download data from some APIs. These are then saved as a .Rdata file in a folder called data.
In my global.R file I load the data by using load("data/workingdata.Rdata"). This results in all the dataframes (about 30) loading into the environment. I know I can use the reactiveFileReader() function to refresh the data, but obviously it would have to be used in the server.R file because of an associated session with the function. Also, I am not sure if load is accepted as a readFunc in reactiveFileReader(). What should be the best strategy for the scenario here?
This example uses a reactiveVal object with observe and invalidateLater. The data is loaded into a new environment and assigned to the reactiveVal every 2 seconds.
library(shiny)
ui <- fluidPage(
actionButton("generate", "Click to generate an Rdata file"),
tableOutput("table")
)
server <- shinyServer(function(input, output, session) {
## Use reactiveVal with observe/invalidateLater to load Rdata
data <- reactiveVal(value = NULL)
observe({
invalidateLater(2000, session)
n <- new.env()
print("load data")
env <- load("workingdata.Rdata", envir = n)
data(n[[names(n)]])
})
## Click the button to generate a new random data frame and write to file
observeEvent(input$generate, {
sample_dataframe <- iris[sample(1:nrow(iris), 10, F),]
save(sample_dataframe, file="workingdata.Rdata")
rm(sample_dataframe)
})
## Table output
output$table <- renderTable({
req(data())
data()
})
})
shinyApp(ui = ui, server = server)
A few thoughts on your workflow:
In the end with your RData-approach you are setting up another data source in parallel to your databases / APIs.
When working with files there always is some housekeeping-overhead (e.g. is your .RData file completed when reading it?). In my eyes this (partly) is what DBMS are made for – taking care about the housekeeping. Most of them have sophisticated solutions to ensure that you get what you query very fast; so why reinvent the wheel?
Instead of continuously creating your .RData files and polling data with the reactiveFileReader() function you could directly query the DB for changes using reactivePoll (see this
for an example using sqlite). If your queries are long running (which I guess is the cause for your workflow) you can wrap them in a future and run them asynchronously (see this post
to get some inspiration).
Alternatively many DBMS provide something like materialized views to avoid long waiting times (according user privileges presumed).
Of course, all of this is based on assumptions, due to the fact, that your eco-system isn’t known to me, but in my experience reducing interfaces means reducing sources of error.
You could use load("data/workingdata.Rdata") at the top of server.R. Then, anytime anyone starts a new Shiny session, the data would be the most recent. The possible downsides are that:
there could be a hiccup if the data is being written at the same time a new Shiny session is loading data.
data will be stale if a session is open just before and then after new data is available.
I imagine the first possible problem wouldn't arise enough to be a problem. The second possible problem is more likely to occur, but unless you are in a super critical situation, I can't see it being a substantial enough problem to worry about.
Does that work for you?

force Shiny's updateDateRangeInput to send a message to the client immediately (or how to initialize dateRangeInput with data values from the server)

I have a little shiny app that uses a dateRangeInput to plot only a subset of the data.
I was trying to initialize the values of the dateRangeInput (max, min, start, end) to match those of the data set (plot the whole range by default).
One way to do that is to use updateDateRangeInput. It works, but only after an error that gets briefly displayed on the UI. (I also tried the solution discussed here, here and here, which is using uiOutput in the UI and renderUI on the server side, but I found basically the same problem).
Here's my (hopefully minimal) reproducible example (if you have shiny installed, you should be able to just copy and paste the code to see what I mean):
library(shiny)
library(dplyr)
get_data <- function(){
set.seed(1234567)
data.frame(
date = sort(sample(seq(as.Date('2014/01/01'), as.Date('2017/01/01'), by = "day"), 200)),
value = runif(200)
)
}
# Here's my UI
ui <- fluidPage(
dateRangeInput("dateRange", label = "Date range"),
plotOutput(outputId = "thePlot", height = "520px")
)
# And here's my server
server <- function(input, output, clientData, session) {
the_data <- reactive({
# Get the data
my_data <- get_data()
# And update the date range values to match those of the dataset
updateDateRangeInput(
session = session,
inputId = "dateRange",
start = min(my_data$date),
end = max(my_data$date)
)
my_data
})
output$thePlot <- renderPlot({
# I need to subset the data, using the user input (dateRangeInput)
data_subset <- dplyr::filter(the_data(),
date >= input$dateRange[[1]],
date <= input$dateRange[[2]]
)
str(data_subset)
# And plot the subset of data
plot(x = data_subset$date,
y = data_subset$value)
})
}
shinyApp(ui, server)
So this app basically loads a dataset, which I do not know in advance, therefore, I do not know the date range to hard code it on the UI.
The UI only has the dateRangeInput and a plotOutput. The server side defines a reactive expression to load the data and a renderPlot that only subsets the data using the range given by the dateRangeInput on the UI.
Note that the dateRangeInput on UI does not have max, min, start or end dates defined (therefore, the default NULL values are used). That's is because at this point I have no clue on the values, only at run time when the data is loaded I know what would be the appropriate values (the whole range of the data). Of course I can set some arbitrary values on UI, but they will be just that, arbitrary values that may or may not work.
So what I wanted to do is for my app to load the data and update the dateRangeInput with appropriate values. I am doing this just after loading the data. It works, but after stumbling on an error on the renderPlot.
What happens is that renderPlot gets executed before the dateRangeInput values are updated, therefore, renderPlot is using NULL values to subset the data and this leads to an error. This error is briefly displayed to the user. Then, the dateRangeInput values do get updated and the plot rendered properly.
I checked the flow and updateDateRangeInput is indeed executed before renderPlot. However, the values are actually updated lated on.
This behaviour is actually described on the help page of updateDateRangeInput:
The input updater functions send a message to the client, telling it
to change the settings of an input object. The messages are collected
and sent after all the observers (including outputs) have finished
running.
So, even though updateDateRangeInput get executed before renderPlot, it only collects the message which is only sent to the client after renderPlot finished running.
Is there any way to force updateDateRangeInput to send the messages to the client immediately?
Or how can I initialize the values of a dateRangeInput from the server side before other outputs are executed?
Thanks in advance, and sorry for such a long message (as the saying goes, I did not have time to write a short message, but I hope the reproducible example is as short as it gets to illustrate my problem).

Strange behavior of R Shiny reactiveValues() and invalidateLater()

I have a use case where am visualizing operational data for a dashboard. I would like it to be such that the visualization is updated periodically as data is added to the database. The logic in my mind is to first check if the number of rows in the live database table is equal to the number of rows in the corresponding dataframe within R. If yes, then no need to pull data, if no, then pull data from database. What I want to avoid is to just pull data (actual database table has over 5 million rows) periodically regardless of whether there is new data or not.
I have created a subset of the data here. The code below I wrote as a proof of concept to first wrap my head around how invalidateLater() and reactiveValues() work in R and how I could possibly use them. It simply reads the number of rows in the database table and displays it to the user. If the number of rows changes, the user interface is updated with the new number of rows. Note that to reproduce you may want to put data into a database so you can simulate adding and deleting rows to see reaction of the "app". I used postgres, and an ODBC connection. If you run the code as-is, you will notice that when rows are added to the db, when the app is doing the checking, the user interface (textOutput() widget) grays out for a few seconds and appears to be in a state of meditation before eventually correctly displaying the new number of rows. This is using the code which first checks if there are differences in row numbers between database and value held in R.
However if I comment out that part of the code which check for differences (comment out the block below)
sharedValues$data <- if(!is.null(sharedValues$data)){
if(nrow(sqlFetch(conn2,"test2")) == sharedValues$data){
return(sharedValues$data)
}
}
else{
sharedValues$data <- nrow(sqlFetch(conn2,"test2"))
return(sharedValues$data)
}
and instead just pull data periodically regardless if there is a change or not (uncomment this line)
#sharedValues$data <- nrow(sqlFetch(conn2,"test2"))
the interface reacts superbly, there is no lag (graying out of the widget text) and the new row value is displayed on the user interface.
My question is what causes the "lag-like" behavior when running the first alternative (which is the desired alternative) of first checking for database changes before making an expensive database select query), yet when the code is amended to pull data regardless of database changes (which seems to me inefficient) this lag-like behavior rears its ugly head? The entire code is below:
library(shiny)
library(shinydashboard)
library(rCharts)
library(curl)
library(RODBC)
conn2 <- odbcConnect("postgres") # database connection object
ui <- dashboardPage(
dashboardHeader(),
dashboardSidebar(),
dashboardBody(
fluidRow(
box(textOutput("text1"),width = 6)
)
)
)
server <- function(input, output, session) {
sharedValues <<- reactiveValues()
observe({
invalidateLater(30000,session)
cat("updating data...\n")
sharedValues$data <- if(!is.null(sharedValues$data)){
if(nrow(sqlFetch(conn2,"test2")) == sharedValues$data){
return(sharedValues$data)
}
}
else{
sharedValues$data <- nrow(sqlFetch(conn2,"test2"))
return(sharedValues$data)
}
#sharedValues$data <- nrow(sqlFetch(conn2,"test2"))
})
output$text1 <- renderText({
y <- sharedValues$data
return(y)
})
}
shinyApp(ui, server)
Any help greatly appreciated.
According to this answer, this can be fixed by manipulating the CSS in ui.R
tags$style(type="text/css",
".recalculating { opacity: 1.0; }"
)

Can I save the old value of a reactive object when it changes?

Note: After coming up with the answer I reworded the question to make if clearer.
Sometimes in a shiny app. I want to make use of a value selected by the user for a widget, as well as the previous value selected for that same widget. This could apply to reactive values derived from user input, where I want the old and the new value.
The problem is that if I try to save the value of a widget, then the variable containing that value has to be reactive or it will not update every time the widget changes. But, if I save the the value in a reactive context it will always give me the current value, not the previous one.
How can I save the previous value of a widget, but still have it update every time the user changes the widget?
Is there a way that does not require the use of an actionButton every time the user changes things? Avoiding an actionButton can be desirable with adding one is otherwise unnecessary and creates excess clicking for the user.
Seeing as the session flush event method seems to be broken for this purpose, here is an alternative way to do it using an observeEvent construct and a reactive variable.
library(shiny)
ui <- fluidPage(
h1("Memory"),
sidebarLayout(
sidebarPanel(
numericInput("val", "Next Value", 10)
),
mainPanel(
verbatimTextOutput("curval"),
verbatimTextOutput("lstval")
)
)
)
server <- function(input,output,session) {
rv <- reactiveValues(lstval=0,curval=0)
observeEvent(input$val, {rv$lstval <- rv$curval; rv$curval <- input$val})
curre <- reactive({req(input$val); input$val; rv$curval})
lstre <- reactive({req(input$val); input$val; rv$lstval})
output$curval <- renderPrint({sprintf("cur:%d",curre())})
output$lstval <- renderPrint({sprintf("lst:%d",lstre())})
}
options(shiny.reactlog = TRUE)
shinyApp(ui, server)
Yielding:
Update This answer was posted before the advent of the reactiveValues/observeEvent model in shiny. I think that #MikeWise 's answer is the better way to do this.
After some playing around this is what I came up with. The ui.r is nothing special
ui.r
library(shiny)
ui <- shinyUI(fluidPage(
sidebarLayout(
sidebarPanel(
selectizeInput(inputId="XX", label="Choose a letter",choices=letters[1:5])
),
mainPanel(
textOutput("Current"),
textOutput("old")
)
)
))
"Current" will display the current selection and "old" displays the previous selection.
In the server.r I made use of three key functions: reactiveValues, isolate and session$onFlush.
server.r
library(shiny)
server <- function(input, output,session) {
Values<-reactiveValues(old="Start")
session$onFlush(once=FALSE, function(){
isolate({ Values$old<-input$XX })
})
output$Current <- renderText({paste("Current:",input$XX)})
output$old <- renderText({ paste("Old:",Values$old) })
}
The server.r works like this.
First, Values$old is created using the reactiveValues function. I gave it the value "Start" to make it clear what was happening on load up.
Then I added a session$onFlush function. Note that I have session as an argument in my server function. This will run every time that shiny flushes the reactive system - such as when the selectizeInput is changed by the user. What is important is that it will run before input$XX gets a new value - so the value has changed at the selectizeInput but not at XX.
Inside the session$onFlush I then assign the outgoing value of XX to Values$old. This is done inside an isolate() as this will prevent any problems with input$XX gets updated with the new values. I can then use input$XX and Values$old in the renderText() functions.

Resources