shiny-server fails when trying to source an R file - r

I updated shiny server (open source) and now i cannot source an R file from within the app code. The .R file referenced is just a simple set of functions, all using base R. The file is also within the same dir as the app resides. I even chmod 777 everything to try to get it to run and it still returns the 'error has occurred' on port 3838. Yet it runs just fine locally (within RStudio). shiny-server.conf is unchanged, user runs as shiny, etc. I updated all packages and even uninstalled and reinstalled shiny and shiny-server with no luck. Its literally only failing on trying to source a file.
source('/srv/shiny-server/basicFls.R')
library(shiny)
ui <- fluidPage(
titlePanel("Hello Shiny!"),
sidebarLayout(
sidebarPanel(
sliderInput(inputId = "bins",
label = "Number of bins:",
min = 1,
max = 50,
value = 30)
),
mainPanel(
plotOutput(outputId = "distPlot")
)
)
)
server <- function(input, output) {
output$distPlot <- renderPlot({
x <- faithful$waiting
bins <- seq(min(x), max(x), length.out = input$bins + 1)
hist(x, breaks = bins, col = "#75AADB", border = "white",
xlab = "Waiting time to next eruption (in mins)",
main = "Histogram of waiting times")
})
}
shinyApp(ui = ui, server = server)

You need to have the path of the source() function set relative to the working directory of the app. In this case, the app directory should be referenced as ".". So a file called code.R located in the app director is referenced as:
source("./code.R")
If the file is located in a directory called "srv" in the same directory as the app, you reference the file as:
source("./srv/code.R")
So, in the following example, the contents of code.R is below, and the file is stored in a directory called "srv" off the root of the app directory.
# code.R
makePlot <- function(x_name) {
ggplot(mtcars, aes_string(x=x_name, y="disp")) + geom_point()
}
Here's the app code that works for that:
library(shiny)
library(ggplot2)
source('./srv/code.R')
ui <- fluidPage(
titlePanel("Basic App"),
sidebarLayout(
sidebarPanel(
selectInput("xaxis", "X axis:", choices=c("mpg", "qsec"))
),
mainPanel(
plotOutput("plot")
)
)
)
server <- function(input, output) {
output$plot <- renderPlot(
# ggplot(mtcars, aes_string(x=input$xaxis, y="disp")) + geom_point()
makePlot(input$xaxis)
)
}
shinyApp(ui, server=server)

Related

Upload Shiny Logfiles to WebDAV Server

We are running several shiny apps for educational purposes on shinyapps.io. In order to track the usage we tried the shinylogs package locally, which worked out fine. Now the goal is to upload the logfiles created by shinylogs on shinyapps.io to a WebDAV server.
The file upload itself can be achieved with the following code. Note, that I have not revealed my true credentials for security reasons. So this request won't actually work for you.
username <- "xxx"
password <- "yyy"
file <- upload_file("test.txt")
PUT("https://fernuni-hagen.sciebo.de/public.php/webdav/test.txt", authenticate(username, password), body = file)
As the next step I created a function out of it, which also works fine.
upload <- function(filename){
body <- upload_file(filename)
PUT(paste0("https://fernuni-hagen.sciebo.de/public.php/webdav/", filename), authenticate(username, password), body = body)
}
upload("test.txt")
Finally, I tried to combine this code with the track_usage command of shinylogs. According to the documentation the store_custom mode should be suitable. Not finding any working example on the web, I was unable to figure out the right syntax, though.
Instead of a single file specified upfront, the function should upload any new logfile to the WebDAV server. In order to clarify my requirements, I have created this simple demo app.
library(shiny)
library(shinylogs)
library(httr)
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("Old Faithful Geyser Data"),
fluidRow(
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
sliderInput("bins1",
"Number of bins:",
min = 1,
max = 50,
value = 30)),
mainPanel(
# Show a plot of the generated distribution
plotOutput("distPlot1")
))),
fluidRow(
# Sidebar with a slider input for number of bins (and action button)
sidebarLayout(
sidebarPanel(
sliderInput("bins2",
"Number of bins:",
min = 1,
max = 50,
value = 30),
actionButton("go", "Update Plot")),
mainPanel(
# Show a plot of the generated distribution
plotOutput("distPlot2")
)))
)
# Define server logic required to draw a histogram
server <- function(input, output) {
username <- "xxx"
password <- "yyy"
track_usage(storage_mode = store_custom(FUN = function(logs){
body <- upload_file(logs)
url <- paste0("https://fernuni-hagen.sciebo.de/public.php/webdav/", logs)
PUT(url, authenticate(username, password), body = body)
}))
output$distPlot1 <- renderPlot({
# generate bins based on input$bins from ui.R
x <- faithful[, 2]
bins <- seq(min(x), max(x), length.out = input$bins1 + 1)
# draw the histogram with the specified number of bins
hist(x, breaks = bins, col = 'darkgray', border = 'white')
})
output$distPlot2 <- renderPlot({
# generate bins based on input$bins from ui.R (only upon click)
input$go
x <- faithful[, 2]
bins <- seq(min(x), max(x), length.out = isolate(input$bins2) + 1)
# draw the histogram with the specified number of bins
hist(x, breaks = bins, col = 'darkgray', border = 'white')
})
}
# Run the application
shinyApp(ui = ui, server = server)
When I run this shiny app locally, there is no error message in the R console, but the files will never arrive on the WebDAV server. Any help to fix this issue would be much appreciated.
So this is my own solution.
# Configuration of logging directory
logsdir <- "~/logs"
# Create directory for log files, if not existent
if(!dir.exists(logsdir)) dir.create(logsdir)
# Usage tracking with JSON file being saved on Shiny and WebDAV Servers
track_usage(
storage_mode = store_custom(FUN = function(logs) {
jsondata <- toJSON(logs)
filename <- paste0("shinylogs_", session$token, ".json")
filepath <- paste0(logsdir, "/", filename)
write(jsondata, file = filepath)
body <- upload_file(filepath)
url <- paste0("https://fernuni-hagen.sciebo.de/public.php/webdav/", filename)
PUT(url, authenticate(username, password), body = body)
})
)

ReferenceError: Can't find variable: Pickr when running shinytest::recordTest()

I am currently working on an RShiny App which became pretty big and hence I want to implement automated UI testing. I therefore tried to record my UI tests with recordTest() from the shinytest package. However, when I run shinytest::recordTest(), I get the error message "ReferenceError: Can't find variable: Pickr" (see attached image). I located the problem to be a colorPickr from the shinyWidgets package and it seems like it has something to do with a .js-File in the package, but I have no idea how to solve this problem.
Error Message
When running shinytest::recordTest(), I usually would expect that the app starts in a headless browser and I can record my tests. This works perfectly fine, when I disable the line of code where the colorPickr is defined. With the colorPickr, the above error occurs.
I tried to update my R version (unfortunately we are working with 3.6.0 currently) and updated all packages, which did not help. I also tried to install phantomJS and set my PATH variable to the phantomjs.exe. Did not help either (not sure if I did that correctly tbh).
The package versions I use are: shinytest_1.5.1, shinyWidgets_0.6.2, shiny_1.6.0
The error is reproducable with the following example app:
library(shiny)
library(shinyWidgets)
library(shinytest)
# Define UI for app that draws a histogram ----
ui <- fluidPage(
# App title ----
titlePanel("Hello Shiny!"),
# Sidebar layout with input and output definitions ----
sidebarLayout(
# Sidebar panel for inputs ----
sidebarPanel(
# Input: Slider for the number of bins ----
sliderInput(inputId = "bins",
label = "Number of bins:",
min = 1,
max = 50,
value = 30),
colorPickr(
inputId = "color",
label = "Pick a color",
selected = "blue" )
),
# Main panel for displaying outputs ----
mainPanel(
# Output: Histogram ----
plotOutput(outputId = "distPlot")
)
)
)
# Define server logic required to draw a histogram ----
server <- function(input, output) {
output$distPlot <- renderPlot({
x <- faithful$waiting
bins <- seq(min(x), max(x), length.out = input$bins + 1)
hist(x, breaks = bins, col = "#007bc2", border = "white",
xlab = "Waiting time to next eruption (in mins)",
main = "Histogram of waiting times")
})
}
shinyApp(ui, server)
To reproduce the error, run shinytest::recordTest().
Thanks in advance for any help!

Is it possible to have one function to download various ggplot plots?

My shiny app generates a number of useful graphs. I would like to allow the user to download the graphs in various formats.
I have done this before for a single graph using How to save plots that are made in a shiny app as a guide. However, I am ending up creating more repeated code for each additional plot. I am not a programmer, but it really seems like I should be able to write one function to do this since I am just passing parameters to downloadHandler and ggsave, but I can't figure it out.
The MRE below represents a page with, say, ten different graphs. Is there a way to write a single function that receives the plot ID from a button (like a tag or something?) and the format from the selectInput to pass those parameters to downloadHandler and ggsave to save each of those graphs in the selected format? The function at the bottom shows my thinking, but I don't know where to go from here or if that is even the right direction.
Thanks!
library(shiny)
library(ggplot2)
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("Old Faithful Geyser Data"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
sliderInput("bins",
"Number of bins:",
min = 1,
max = 50,
value = 30)
),
# Show plots and download buttons
mainPanel(
plotOutput("distPlot"),
fluidRow(
column(3,
downloadButton("dl_plot1")
),
column(3,
selectInput("plot1_format",label = "Format",choices = c("SVG","PDF","JPEG","PNG"),width = "75px")
)
),
plotOutput("scat_plot"),
column(3,
downloadButton("dl_plot2")
),
column(3,
selectInput("plot2_format",label = "Format",choices = c("SVG","PDF","JPEG","PNG"),width = "75px")
)
)
)
)
# Define server logic required to draw a histogram and scatterplot
server <- function(input, output) {
output$distPlot <- renderPlot({
x <- faithful$waiting
binwidth<-(max(x)-min(x))/input$bins
p<-ggplot(faithful,aes(waiting))+
geom_histogram(binwidth = binwidth)
p
})
output$scat_plot<-renderPlot({
p<-ggplot(faithful,aes(x=waiting,y=eruptions))+
geom_point()
p
})
downloadPlot <- function(plot_name,file_name,file_format){#concept code
downloadHandler(
filename=function() { paste0(file_name,".",file_format)},
content=function(file){
ggsave(file,plot=plot_name,device=file_format)
}
)
}
}
# Run the application
shinyApp(ui = ui, server = server)
To achieve your desired result without duplicating code you could (or have to) use a Shiny module. Basically a module is a pair of an UI function and a server function. For more on modules I would suggest to have a look at e.g. Mastering shiny, ch. 19.
In the code below I use a module to take care of the download part. The job of downloadButtonUI and downloadSelectUI is to add a download button and a selectInput for the file format. The downloadServer does the hard work and saves the plot in the desired format.
Note: Besides the download module I moved the code for the plots to reactives so that the plots could be passed to the downloadHandler or the download module.
EDIT: Added a fix. We have to pass the reactive (e.g. dist_plot without parentheses) to the download server and use plot() inside the downloadServer instead to export the updated plots.
library(shiny)
library(ggplot2)
# Download Module
downloaButtondUI <- function(id) {
downloadButton(NS(id, "dl_plot"))
}
downloadSelectUI <- function(id) {
selectInput(NS(id, "format"), label = "Format", choices = c("SVG", "PDF", "JPEG", "PNG"), width = "75px")
}
downloadServer <- function(id, plot) {
moduleServer(id, function(input, output, session) {
output$dl_plot <- downloadHandler(
filename = function() {
file_format <- tolower(input$format)
paste0(id, ".", file_format)
},
content = function(file) {
ggsave(file, plot = plot())
}
)
})
}
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("Old Faithful Geyser Data"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
sliderInput("bins",
"Number of bins:",
min = 1,
max = 50,
value = 30
)
),
# Show plots and download buttons
mainPanel(
plotOutput("distPlot"),
fluidRow(
column(3, downloaButtondUI("distPlot")),
column(3, downloadSelectUI("distPlot"))
),
plotOutput("scat_plot"),
fluidRow(
column(3, downloaButtondUI("scatPlot")),
column(3, downloadSelectUI("scatPlot"))
),
)
)
)
server <- function(input, output) {
dist_plot <- reactive({
p <- ggplot(faithful, aes(waiting)) +
geom_histogram(bins = input$bins)
p
})
scat_plot <- reactive({
p <- ggplot(faithful, aes(x = waiting, y = eruptions)) +
geom_point()
p
})
output$distPlot <- renderPlot({
dist_plot()
})
output$scat_plot <- renderPlot({
scat_plot()
})
downloadServer("distPlot", dist_plot)
downloadServer("scatPlot", scat_plot)
}
shinyApp(ui = ui, server = server)
#>
#> Listening on http://127.0.0.1:4092

Audio files not loading properly in Shiny App

I am trying to load an mp3 file held in the www folder in my shiny app. After having lots of issues with this, I have just reproduced the problem in a very simple shiny App (code below).
The audio file I am using, "brand.mp3" is a 19MB mp3 file
When I run this, it returns a 500 error. What is really weird is, if I close RStudio, and then restart, the first time I run the app, the file loads. Then if I reload, there is nothing at all. The first image shows first load of app:
And on the the reload I get this:
The Chrome console then gives an error: Failed to load resource: the server responded with a status of 500 (Internal Server Error)
Code for this is below.
Would be grateful for any ideas on this one. The only thing I can think of is if the app has some kind of memory issue. I have the same problem if I running RStudio in Windows or if I am running the app through a Docker Shiny Server Image found here
library(shiny)
ui <- fluidPage(
# App title ----
titlePanel("Test app"),
# Sidebar layout with input and output definitions ----
sidebarLayout(
# Sidebar panel for inputs ----
sidebarPanel(
# Input: Slider for the number of bins ----
sliderInput(inputId = "bins",
label = "Number of bins:",
min = 1,
max = 50,
value = 30),
tags$audio(src = "brand.mp3", type = "audio/mp3", autoplay = NA, controls = NA)
),
mainPanel(
plotOutput(outputId = "distPlot")
)
)
)
server <- function(input, output) {
output$distPlot <- renderPlot({
x <- faithful$waiting
bins <- seq(min(x), max(x), length.out = input$bins + 1)
hist(x, breaks = bins, col = "#75AADB", border = "white",
xlab = "Waiting time to next eruption (in mins)",
main = "Histogram of waiting times")
})
}
shinyApp(ui = ui, server = server)

How to delete a file created by a Shiny app when the ssession closes

I generate and display a flextable in a Shiny app and want to place it in a PDF. The only available method is to convert the flextable object to a PNG then place the PNG in the PDF. For each PNG file I assign a filename including a date-time stamp to make it unique between sessions. This file name is saved in a reactiveValue.
When the user is finished and the session is closed, how can I delete the file? If I do not I will pile up extraneous files. I cannot use onSessionEnded() because the reactive values are all gone when the browser is closed. I cannot generalize using a pattern because other users have files with a similar name. I have to delete these PNG files specifically.
Any ideas?
onSessionEnded code that does not work:
observe({
session$onSessionEnded(function() {
unlink(c(values$fnameSummary))
unlink(c(values$fnameLike))
unlink(c(values$fnameRisk1))
})
})
This produces the following error:
Warning: Error in .getReactiveEnvironment()$currentContext: Operation not
allowed without an active reactive context. (You tried to do something
that can only be done from inside a reactive expression or observer.)
Stack trace (innermost first):
33: .getReactiveEnvironment()$currentContext
32: .subset2(x, "impl")$get
31: $.reactivevalues
30: $
29: unlink
28: callback [C:\Users\jch1748\Documents\Projects\W2017010 - Combined Risk Tool\testing/server.R#2790]
1: runApp
maybe a working example will help?
#
# This is a Shiny web application. You can run the application by clicking
# the 'Run App' button above.
#
# Find out more about building applications with Shiny here:
#
# http://shiny.rstudio.com/
#
library(shiny)
tsts <- reactiveValues()
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("Old Faithful Geyser Data"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
sliderInput("bins",
"Number of bins:",
min = 1,
max = 50,
value = 30)
),
# Show a plot of the generated distribution
mainPanel(
plotOutput("distPlot")
)
)
)
# Define server logic required to draw a histogram
server <- function(input, output) {
output$distPlot <- renderPlot({
# generate bins based on input$bins from ui.R
x <- faithful[, 2]
bins <- seq(min(x), max(x), length.out = input$bins + 1)
# draw the histogram with the specified number of bins
hist(x, breaks = bins, col = 'darkgray', border = 'white')
})
observe({
tsts$fname <- "AAA.txt"
write(input$bins, file=tsts$fname)
})
onSessionEnded(function() {
cat("Session Ended\n")
unlink(tsts$fname)
})
}
# Run the application
shinyApp(ui = ui, server = server)
I had a similar problem where I wanted to dynamically serve images and pdf files for download in a shiny app. Therefore the files need to be placed within the www-directory which makes the use of tempdir impossible. Additionally, the created files needed to be deleted after the app stops. I solved the problem with the following code:
session$onSessionEnded(function() {
system(paste("rm -f", PathToFile))
})
No need to use reactiveValues. Please see:
library(shiny)
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("Old Faithful Geyser Data"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
sliderInput("bins",
"Number of bins:",
min = 1,
max = 50,
value = 30)
),
# Show a plot of the generated distribution
mainPanel(
plotOutput("distPlot")
)
)
)
# Define server logic required to draw a histogram
server <- function(input, output, session) {
fname <- "AAA.txt"
output$distPlot <- renderPlot({
# generate bins based on input$bins from ui.R
x <- faithful[, 2]
bins <- seq(min(x), max(x), length.out = input$bins + 1)
# draw the histogram with the specified number of bins
hist(x, breaks = bins, col = 'darkgray', border = 'white')
})
observe({
write(input$bins, file=fname)
})
session$onSessionEnded(function() {
cat("Session Ended\n")
unlink(fname)
})
}
# Run the application
shinyApp(ui = ui, server = server)

Resources