4

My shiny app will reads content from a local file in my desktop every 5 mins because the file's content get updated every 5 mins too. And my shiny app basically read in the new content and append the data to the existing dataframe and plot the new content out every 5 mins.

Question: Ultimately, I would like to host this online. If I host this on shinyapps.io, would I still be able to read the local file in my desktop that is updated every 5 minutes? If not, what can I do?

M--
  • 25,431
  • 8
  • 61
  • 93
mynameisJEFF
  • 4,073
  • 9
  • 50
  • 96
  • Shinyapps.io is a rather static setup (not too suited for continuous data updates, afaik). Considering the wealth of R packages interfacing with storage services (Dropbox, Google Drive, GCP storage buckets, etc.) I would lean towards grabbing the data from an external resource. Happy to write a more comprehensive answer if that's a path you might consider. – anddt Mar 28 '20 at 11:32
  • Shinyapp is the first thing that came out when I googled. I am all ears to all opinions. In fact, it would be superb if you do not mind writing a comprehensive answer for these types of tasks. This will help the people who have similar questions. – mynameisJEFF Mar 28 '20 at 11:40
  • 1
    plumber might help. – Chris Mar 28 '20 at 11:56
  • @Chris, that's a good point, especially when data is needed outside of R or Shiny dashboards. Added a `plumber` snippet to my answer. – anddt Mar 30 '20 at 13:49

1 Answers1

8

I haven't extensive experience with shiny deployments on shinyapps.io but I'll try to keep this as general as possible.The main limitation lies on being unable to schedule a CRON job to grab data from your machine on a schedule. Hence I would consider the following:

  • Push your data on a storage provider (Dropbox will be used as an example ) each 5 minutes using a CRON job
  • Grab the data in your Shiny dashboard.

Below you can find couple example around Dropbox and Google Drive but you can easily it apply pretty much the same concepts for AWS and GCP (although you'll have to fiddle with passing secrets or encrypting your auth tokens).

Dropbox example

rdrop2 offer an easy to use wrapper around Dropbox API. Below you can find a simple example on how to push and retrieve a text file from an account (from rdrop2 readme file).

library(rdrop2)

# Authenticate and save token for later use2
token <- drop_auth()
saveRDS(token, "~/dropbox_token.rds")

# Create a folder
drop_create('upload_test')
# You can also create a public folder if data is not sensitive
# drop_create('public/upload_test')

# Upload the file in the freshly created folder
drop_upload("~/mtcars.csv", path = "upload_test")

## Retrieveing your file is as simple as
drop_download("upload_test/mtcars.csv", local_path = "~/new_file.csv")

Implementing it in Shiny

The cleanest way to apply the example above in Shiny would be to place data acquisition in a global.R file that will be imported into your Shiny application before running:
global.R:

library(rdrop2)

# Authenticate and save token for later use2
token <- drop_auth(rdstoken = "dropbox_token.rds")

# Retrieveing your file is as simple as
drop_download("upload_test/mtcars.csv", local_path = "data.csv",
              overwrite = TRUE)
drop_df <- read.csv("data.csv", sep = ",")

print("Downloaded and imported data!")

Your app.R file will look something like this:

library(shiny)
source("global.R")

ui <- fluidPage(

    # Application title
    titlePanel("Pulling data from Dropbox"),

    mainPanel(
       tableOutput("df_output")
    )
)

server <- function(input, output) {

    output$df_output <- renderTable({
        drop_df
    })
}

shinyApp(ui = ui, server = server)

Deploy to shinyapps

You can then deploy your app as usual (including the auth token). enter image description here

Scheduling data upload

Since your data gets refreshed every 5 mintues on your local machine, it'll be needed to have an upload schedule with that cadence. Here I'll be using the cronR package but using crontab on Linux will work just fine.

library(cronR)

cron_add(source("data_upload.R"), frequency = "*/5 * * * *", 
         description = "Push data to Dropbox")

plumber api

As @Chris mentioned, calling an API might be an option, especially if data will be needed outside of R scripts and Shiny dashboards. Below you can find a short endpoint one could call to retrieve data in csv format. Shinyapps.io doesn't support hosting plumber api, hence you'd have to host it on your favorite cloud provider.

library(plumber)
library(rdrop2)

#* @apiTitle Plumber Example API

#* Echo dropbox .csv file
#* @get /get-data
function(req, res) {
  auth_token <- drop_auth(rdstoken = "token.rds")

  drop_download('upload_test/mtcars.csv', dtoken = auth_token,
                local_path = "mtcars.csv", overwrite = TRUE)

  include_file("mtcars.csv", res, 'text/csv')
}

Building and starting the service with:

r <- plumb("plumber.R")  
r$run()
anddt
  • 1,589
  • 1
  • 9
  • 26
  • Thanks for the great answer above . I have one question though : when the data is updated every 5 mins and being uploaded to Dropbox, how does shiny know the Dropbox file content has been modified and it needs to re- download the file again, since the global.R is only executed once in the shinyapp. Please correct me if I am wrong . – mynameisJEFF Apr 05 '20 at 15:36
  • No worries, happy to help. Good question, I am afraid the dashboard simply won't know about that (as you correctly spotted, there's no webhook or ping to let the dashboard know that new data is arrived) and will therefore be available on the next page refresh. I naively assumed that data didn't need to be updated **while** users are actually using the dashboard (my bad). Something could be done with [`invalidateLater`](https://shiny.rstudio.com/reference/shiny/0.14/invalidateLater.html) forcing the dashboard to poll the data every 5 minutes. – anddt Apr 05 '20 at 18:09