Generating downloadable reports with shiny in LAN - r

I shared an Rshiny app in LAN with my coworkers PCs, so I used shiny.host and shiny.port
So when my coworkers in theirs PCs tried to download reports when clicking in a report_button button, they received the last values that I chose on my PC
example:
On my PC I choose input$slider = 46 and I download the report file in HTML, then my coworker on his PC opens the link 192.168.5.140:8888 in his browser, chooses input$slider=73 and downloads the file, and then (!!!) he gets the same report that I got, i.e he gets input$slider = 46
here is the code :
library(shiny)
options(shiny.host = '192.168.5.140')
options(shiny.port = 8888)
ui = fluidPage(
sliderInput("slider", "Slider", 1, 100, 50),
downloadButton("report_button", "Generate report")
)
server = function(input, output) {
output$report_button<- downloadHandler(
# For PDF output, change this to "report.pdf"
filename = "report.html",
content = function(file) {
# Copy the report file to a temporary directory before processing it, in
# case we don't have write permissions to the current working dir (which
# can happen when deployed).
tempReport <- file.path(tempdir(), "report.Rmd")
file.copy("report.Rmd", tempReport, overwrite = TRUE)
# Set up parameters to pass to Rmd document
params <- list(n = input$slider)
# Knit the document, passing in the `params` list, and eval it in a
# child of the global environment (this isolates the code in the document
# from the code in this app).
rmarkdown::render(tempReport, output_file = file,
params = params,
envir = new.env(parent = globalenv())
)
}
)
}
shinyApp(ui, server)
and the report.RMD file :
---
title: "Dynamic report"
output: html_document
params:
n: NA
---
```{r}
# The `params` object is available in the document.
params$n
```

You should be able to prevent this situation by using tempfile instead of tempdir to create the temporary Rmd file.
tempdir is constant as long as you are running the same R-session - which might end up in concurrent shiny sessions overwriting each others report results.
tempfile however, creates a unique file for each function call:
library(shiny)
options(shiny.host = '0.0.0.0')
options(shiny.port = 8888)
ui = fluidPage(
sliderInput("slider", "Slider", 1, 100, 50),
downloadButton("report_button", "Generate report")
)
server = function(input, output) {
output$report_button<- downloadHandler(
# For PDF output, change this to "report.pdf"
filename = "report.html",
content = function(file) {
# Copy the report file to a temporary directory before processing it, in
# case we don't have write permissions to the current working dir (which
# can happen when deployed).
tempReport <- tempfile(fileext = ".Rmd")
file.copy("report.Rmd", tempReport, overwrite = TRUE)
# Set up parameters to pass to Rmd document
params <- list(n = input$slider)
# Knit the document, passing in the `params` list, and eval it in a
# child of the global environment (this isolates the code in the document
# from the code in this app).
rmarkdown::render(tempReport, output_file = file,
params = params,
envir = new.env(parent = globalenv())
)
}
)
}
shinyApp(ui, server)

Related

Inline code not executed in a shiny app with Rmarkdown report

Dear all shiny developers!
I have a question about a shiny app with a Rmarkdown report downloadable.
The app is based on this https://shiny.rstudio.com/articles/generating-reports.html, taken here as an exemple.
shinyApp(
ui = fluidPage(
sliderInput("slider", "Slider", 1, 100, 50),
downloadButton("report", "Generate report")
),
server = function(input, output) {
output$report <- downloadHandler(
# For PDF output, change this to "report.pdf"
filename = "report.pdf",
content = function(filename) {
# Copy the report file to a temporary directory before processing it, in
# case we don't have write permissions to the current working dir (which
# can happen when deployed).
tempReport <- file.path(tempdir(), "Template.Rmd")
print(tempReport)
file.copy("Template.Rmd", tempReport, overwrite = TRUE)
# Set up parameters to pass to Rmd document
params <- list(n = input$slider)
# Knit the document, passing in the `params` list, and eval it in a
# child of the global environment (this isolates the code in the document
# from the code in this app).
rmarkdown::render(tempReport, output_file = filename,
params = params,
envir = new.env(parent = globalenv())
)
}
)
}
)
with my .Rmd document
---
title: ''
output: pdf_document
geometry: "left=2cm,right=2cm,top=1cm,bottom=2cm"
params:
n: NA
---
```{r}
# The `params` object is available in the document.
params$n
```
A plot of `r params$n` random points.
The code is running ok, the document is created but when I open it, I can see that the code within the chunk is ok but not in the inline code, see below:
However, in this exemple https://shiny.rstudio.com/gallery/download-knitr-reports.html we see that the inline code is good (I don't have the code of the Rmarkdown unfortunatelly).
I saw this subject (https://community.rstudio.com/t/embedding-shiny-with-inline-not-rendering-with-html-output/41175) that could be similar but it is html and the answer does not seem to fit.
Do you have an idea why it does not function ? Or any track to explore ?
Many thanks !!
Be careful to the file.copy function !
Thanks to the remark of Limey, I notice that the Rmarkdown that had a typo few moments ago was not updated! Careful that the file.copy is correct!
Here is the script working:
shinyApp(
ui = fluidPage(
sliderInput("slider", "Slider", 1, 100, 50),
downloadButton("report", "Generate report")
),
server = function(input, output) {
output$report <- downloadHandler(
# For PDF output, change this to "report.pdf"
filename = "report.pdf",
content = function(filename) {
# Copy the report file to a temporary directory before processing it, in
# case we don't have write permissions to the current working dir (which
# can happen when deployed).
tempReport <- file.path(tempdir(), "rules.Rmd")
print(tempReport)
file.copy("Data/rules.Rmd", tempReport, overwrite = TRUE)
# Set up parameters to pass to Rmd document
params <- list(n = input$slider)
# Knit the document, passing in the `params` list, and eval it in a
# child of the global environment (this isolates the code in the document
# from the code in this app).
rmarkdown::render(tempReport, output_file = filename,
params = params,
envir = new.env(parent = globalenv())
)
}
)
}
)
and the Rmarkown
---
title: ''
output: pdf_document
geometry: "left=2cm,right=2cm,top=1cm,bottom=2cm"
params:
n: NA
---
```{r}
# The `params` object is available in the document.
params$n
```
A plot of `r params$n` random points.
Thanks to all of you that gave time to this subject!

Rendering word document through shiny app, odd behaviour

I can't make the production of my word report use my template. The report gets generated but not with my styling.
I saw similar threads here and here but nothing i saw there resolved my issues.
The second problem i have (see example below) is that no matter which country is selected, the report always takes the name of the first country in the list (while it shouldn't be the case, the name should respond to the country input).
library(shiny)
ui <- fluidPage(
'Please select a country from the list below: ',
selectInput(
'Country',
'Country',
c('Albania','Belgium','Romania'),
selected = NULL,
multiple = FALSE,
selectize = TRUE,
width = NULL,
size = NULL
),
downloadButton("report", "Generate report")
)
server <- function(input, output,session) {
##### Report generation #####
output$report <- downloadHandler(
# For PDF output, change this to "report.pdf"
filename = paste0(input$Country,'_',Sys.Date(),".docx"),
content = function(file) {
# Copy the report file to a temporary directory before processing it, in
# case we don't have write permissions to the current working dir (which
# can happen when deployed).
tempReport <- file.path(tempdir(), "report.Rmd")
file.copy("report.Rmd", tempReport, overwrite = TRUE)
# Set up parameters to pass to Rmd document
params <- list(Country = input$Country)
# Knit the document, passing in the `params` list, and eval it in a
# child of the global environment (this isolates the code in the document
# from the code in this app).
rmarkdown::render(tempReport, output_file = file,
params = params,
envir = new.env(parent = globalenv())
)
}
)
}
# Run the application
shinyApp(ui = ui, server = server)
And the report.Rmd
---
title: "Title"
output: word_document
reference_docx: Template.docx
params:
Country: NA,
---
# `r params$Country`
`r Sys.Date()`

How do I prevent downloadHandler from prompting user for save location? I want it to automatically save to a specified location without prompting

I have written a shiny application that allows users to produce and download html rmarkdown reports. This works.
However, when the report is generated, the user is prompted for a save location. I can certainly include a location in the filename, but the user is still prompted.
Ideally, I would like the user to click the download button and have the file automatically sent to a specific location.
Here is a sample app that works:
library(shiny)
shinyApp(
ui = fluidPage(
sliderInput("slider", "Slider", 1, 100, 50),
downloadButton("report", "Generate report"),
),
server = function(input, output) {
output$report <- downloadHandler(
# For PDF output, change this to "report.pdf"
filename = paste("My name", ".html", sep=""),
content = function(file) {
# Copy the report file to a temporary directory before processing it, in
# case we don't have write permissions to the current working dir
tempReport <- file.path(tempdir(), "report.Rmd")
file.copy("report.Rmd", tempReport, overwrite = TRUE)
# Set up parameters to pass to Rmd document
params <- list(n = input$slider)
# Knit the document, passing in the `params` list, and eval it in a
# child of the global environment (this isolates the code in the document
# from the code in this app).
rmarkdown::render(tempReport, output_file = file,
params = params,
envir = new.env(parent = globalenv())
)
}
)
}
)
and an rmarkdown script:
---
title: "Dynamic report"
output: html_document
params:
n: NA
---
```{r}
# The `params` object is available in the document.
params$n
```
A plot of `params$n` random points.
```{r}
plot(rnorm(params$n), rnorm(params$n))
```

Saving rmarkdown output locally from Shiny

I have a shiny app that generates a downloadable report using rmarkdown, which works fine. I would like a copy of the report to be saved on the shiny host each time a user uploads data to generate a report. Ideally, I'd like to do this from within the downloadHandler call so that I do not have to generate the report twice.
A minimal example (adapted from this shiny article):
library(shiny)
shinyApp(
ui = fluidPage(
sliderInput("slider", "Slider", 1, 100, 50),
downloadButton("report", "Generate report")
),
server = function(input, output) {
output$report <- downloadHandler(
# For PDF output, change this to "report.pdf"
filename = "report.html",
content = function(file) {
# Copy the report file to a temporary directory before processing it, in
# case we don't have write permissions to the current working dir (which
# can happen when deployed).
tempReport <- file.path(tempdir(), "report.Rmd")
file.copy("report.Rmd", tempReport, overwrite = TRUE)
# Set up parameters to pass to Rmd document
params <- list(n = input$slider)
# Knit the document, passing in the `params` list, and eval it in a
# child of the global environment (this isolates the code in the document
# from the code in this app).
rmarkdown::render(tempReport, output_file = file,
params = params,
envir = new.env(parent = globalenv())
)
}
)
}
)
The rmarkdown file
---
title: "Dynamic report"
output: html_document
params:
n: NA
---
```{r}
# The `params` object is available in the document.
params$n
```
A plot of `params$n` random points.
```{r}
plot(rnorm(params$n), rnorm(params$n))
```
You basically just need to copy the generated report into another directory. Here is an example based on the code you gave.
library(shiny)
if(!dir.exists("reportDir"))
dir.create("reportDir")
shinyApp(
ui = fluidPage(
sliderInput("slider", "Slider", 1, 100, 50),
downloadButton("report", "Generate report")
),
server = function(input, output) {
output$report <- downloadHandler(
# For PDF output, change this to "report.pdf"
filename = "report.html",
content = function(file) {
# Copy the report file to a temporary directory before processing it, in
# case we don't have write permissions to the current working dir (which
# can happen when deployed).
tempReport <- file.path(tempdir(), "report.Rmd")
file.copy("report.Rmd", tempReport, overwrite = TRUE)
# Set up parameters to pass to Rmd document
params <- list(n = input$slider)
# Knit the document, passing in the `params` list, and eval it in a
# child of the global environment (this isolates the code in the document
# from the code in this app).
rmarkdown::render(tempReport, output_file = file,
params = params,
envir = new.env(parent = globalenv())
)
# copy generated report
file.copy(file, paste("reportDir/", Sys.time(), ".html"))
}
)
}
)

Attach a knitted tempfile to email in R shiny

I have a working shiny app that uses Mailgun to send an email when a button is clicked and also produces a rmarkdown report when another button is clicked.
Here is working code, obviously without the working email authentication:
ui.R
library(shiny)
# Define UI for application that draws a histogram
shinyUI(fluidPage(
sliderInput("slider", "Slider", 1, 100, 50),
downloadButton("report", "Generate report"),
actionButton("mail", "send email"),
textOutput('mailo')
)
)
server.R
library(shiny)
sendEmail <- function(email = "xxx#you.org",
mail_message = "Hello"){
url <- "https://api.mailgun.net/v3/sandboxxxxxxxxx.mailgun.org/messages"
## username:password so api_key is all after the api:
api_key <- "key-0xxxxxxxxxxxx"
the_body <-
list(
from="Mailgun Sandbox <postmaster#sandboxxxxxxxxxxxxxxxx.mailgun.org>",
to=email,
subject="Mailgun from R test",
text=mail_message
)
req <- httr::POST(url,
httr::authenticate("api", api_key),
encode = "form",
body = the_body)
httr::stop_for_status(req)
TRUE
}
# Define server logic required to draw a histogram
shinyServer(function(input, output) {
event <- observeEvent(input$mail,{
sendEmail()
}, ignoreInit = TRUE)
output$mailo <- renderText({print("EMAIL SENT!")})
output$report <- downloadHandler(
# For PDF output, change this to "report.pdf"
filename = "report.html",
content = function(file) {
# Copy the report file to a temporary directory before processing it, in
# case we don't have write permissions to the current working dir (which
# can happen when deployed).
tempReport <- file.path(tempdir(), "report.Rmd")
file.copy("report.Rmd", tempReport, overwrite = TRUE)
# Set up parameters to pass to Rmd document
params <- list(n = input$slider)
# Knit the document, passing in the `params` list, and eval it in a
# child of the global environment (this isolates the code in the document
# from the code in this app).
rmarkdown::render(tempReport,
output_file = file,
params = params,
envir = new.env(parent = globalenv())
)
}
)
})
I want to do both things in one step. That is, generate the report, attach it to the email and send it to the given address. I am just not sure how to treat a tempfile() when referencing the file.
I also currently have the app deployed on Shinyapps.io, so saving to file and then retrieving won't work.
Any ideas?
This is the code you need. I tested this and it worked, although my gmail did give me huge bright yellow warnings that the email contains an unverified file that may be dangerous. I also simplified the app a bit and removed some unnecessary code.
library(shiny)
ui <- fluidPage(
sliderInput("slider", "Slider", 1, 100, 50),
actionButton("mail", "send email")
)
sendEmail <- function(email = "RECIPIENT#gmail.com",
mail_message = "Hello",
file = NULL) {
url <- "https://api.mailgun.net/v3/sandboxxxxxxxxxxxxxxxxxxxxxxxx.mailgun.org/messages"
## username:password so api_key is all after the api:
api_key <- "XXXXXXXXXXXXXXXXXX-XXXXXXXXX-XXXXX"
the_body <-
list(
from = "Mailgun Sandbox <postmaster#sandboxXXXXXXXXXXXXXXXXXXX.mailgun.org>",
to = email,
subject = "Mailgun from R test",
text = mail_message
)
if (!is.null(file)) {
the_body$attachment = httr::upload_file(file)
}
req <- httr::POST(url,
httr::authenticate("api", api_key),
encode = "multipart",
body = the_body)
httr::stop_for_status(req)
TRUE
}
server <- function(input, output, session) {
observeEvent(input$mail, {
# Copy the report file to a temporary directory before processing it, in
# case we don't have write permissions to the current working dir (which
# can happen when deployed).
tempReport <- file.path(tempdir(), "report.Rmd")
file.copy("report.Rmd", tempReport, overwrite = TRUE)
# Set up parameters to pass to Rmd document
params <- list(n = input$slider)
# Knit the document, passing in the `params` list, and eval it in a
# child of the global environment (this isolates the code in the document
# from the code in this app).
file <- rmarkdown::render(tempReport,
output_file = file.path(tempdir(), "report.html"),
params = params,
envir = new.env(parent = globalenv())
)
sendEmail(file = file)
})
}
shinyApp(ui, server)
By the way, there's also an IMmailgun package in case you're interested, but it achieves essentially what you do with this code.

Resources