How to generate several reports with one button using Shiny - r

I have a Shiny app that generates reports about people. I would like to have a single button that generates all the reports as at the moment I am generating them one by one.
The names of the people is generated from RV4$data[,input$Map_EndoscopistIn] and the report takes a bunch of parameters as shown. How can I generate the reports in a loop for each element of the list RV4$data[,input$Map_EndoscopistIn] (preferably with element name as part of the generated filename?
output$Allreports <- downloadHandler(
k<-RV4$data[,input$Map_EndoscopistIn]
for ( i in k){
filename = "report.docx",
content = function(file) {
tempReport <- file.path(tempdir(), "report.Rmd")
file.copy("report.Rmd", tempReport, overwrite = TRUE)
# Set up parameters to pass to Rmd document
params <- list(EndoscopistChooserIn = RV4$data[,input$Map_EndoscopistIn][i],
Map_EndoscopistIn=input$Map_EndoscopistIn,
BarrEQPerformFinalTable=BarrEQPerformFinalTable(),
EndoscopyTypesDonePre=EndoscopyTypesDonePre(),
performanceTable=data(),
IndicsVsBiopsiesPre=IndicsVsBiopsiesPre(),
GRS_perEndoscopist_TablePrep=GRS_perEndoscopist_TablePrep()
)
rmarkdown::render(tempReport, output_file = file,
params = params,
envir = new.env(parent = globalenv())
)
}
}
)
The attempt above doesn't work.

Related

Generating downloadable reports with shiny in LAN

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)

I'm failing to create a pagedown::chrome_print pdf output from a shiny app which takes parameter inputs and renders the Rmarkdown

I have a rmarkdown file which is built around the {pagedreport} package (an add-on to pagedown). This creates a html and them uses pagedown::chrome_print to convert the html to pdf.
The rmarkdown file i have works as planned when I have the parameters manually entered on the rmarkdown file, but I want to be able to pick my parameters from a shiny app.
I currently have tried this approach
output$report <- downloadHandler(
# For PDF output, change this to "report.pdf"
filename = "new_report.pdf",
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("../temp/", "myRmarkdown.Rmd")
file.copy("myRmarkdown.Rmd", tempReport, overwrite = TRUE)
# Set up parameters to pass to Rmd document
params <- list(A = input$A, B = input$B)
# 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).
library(rmarkdown)
render(tempReport, params = params,
envir = new.env(parent = globalenv()))
}
and I get the result:
Output created: MyRmarkdown.html
but there is no pdf created. Pagedown uses chrome_print to convert - and this is in the yaml of the Rmarkdown.
This is the YAML of my Rmarkdown file
output:
pagedreport::paged_windmill:
img_to_dark: TRUE
logo_to_white: TRUE
knit: pagedown::chrome_print
#toc: TRUE
toc-title: "Table of Contents"
#toc_depth: 1
main-color: "#264653"
secondary-color: "#2a9d8f"
google-font: TRUE
main-font: "Raleway"
header-font: "Roboto"
params:
A: NA
B: NA
editor_options:
markdown:
wrap: 72
I know there is an issue between shiny and chrome_print() where you need to add chrome_print(..., async = TRUE), but I don't know where to put this argument in my shiny app?
I am pretty sure you have to make sure that your render function creates a file at the location given by the argument file. In your current appraoch render uses defaults, which won't work. Thus, it should be as simple as adding the output_file argument to render. The following code snippets works for me:
test.Rmd
---
title: "Untitled"
output: html_document
params:
title: "Test"
heading: "Header"
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = FALSE)
```
```{r results="asis"}
cat("## ", params$title)
```
You passed in argument `r cat(params$heading)`
app.R
library(shiny)
library(rmarkdown)
ui <- fluidPage(textInput("heading", "Heading"),
textInput("title", "Title"),
downloadButton("download_ok", "Works"),
downloadButton("download_nok", "Does not Work"))
server <- function(input, output) {
output$download_ok <- downloadHandler(
filename = function() {
paste("data-", Sys.Date(), ".html", sep="")
},
content = function(file) {
p <- list(title = input$title,
heading = input$heading)
render("test.Rmd", output_file = file, params = p)
}
)
output$download_nok <- downloadHandler(
filename = function() {
paste("data-", Sys.Date(), ".html", sep="")
},
content = function(file) {
p <- list(title = input$title,
heading = input$heading)
render("test.Rmd", params = p)
}
)
}
shinyApp(ui, server)
This snippet shows how this works now in principle, I do not know pagedreport. If there is an option in the YAML to produce pdfs right away (without the manual call to whatever function) this should work in the same way.
If you absolutely need to call the function, you can do that in the downloadHandler after the render assuming that the function can deal with input file names and create output files at the given location (again you need to create the file at the given location [as per argument file])
After reading the docs of pagedown::chrome_print you can adapt your downloadHandler as follows:
output$report <- downloadHandler(
filename = "new_report.pdf",
content = function(file) {
tempReport <- file.path("../temp/", "myRmarkdown.Rmd")
file.copy("myRmarkdown.Rmd", tempReport, overwrite = TRUE)
params <- list(A = input$A, B = input$B)
html_fn <- rmarkdown::render(tempReport, params = params,
envir = new.env(parent = globalenv()))
pagedown::chrome_print(html_fn, file)
}

Add multiple outputs from shiny to html markdown

I created a shiny app in which I have multiple outputs (tables, graphs and text) based on user selected parameters. I would like to download the outputs in a HTML document. I am able to do this using the small example on https://shiny.rstudio.com/articles/generating-reports.html but I cannot seem to figure out how to use multiple outputs in the markdown file.
I looked around quite a bit and although there are some examples out there I still cannot seem to figure it out. Probably it's just me being inexperienced. The code I modified (poorly) bellow gives outputs test.text, test.text2 then test.text again.
I would like to be able to add multiple output values that will subsequently be used in the markdown. I am using all my outputs in reactive functions as I noticed that I cannot use output$ in downloadHandler
This is the code I am trying to use in downloadHandler
test.text <- reactive({input$gendertext}) #input text written in boxes
test.text2 <- reactive({input$agetext})
output$report <- downloadHandler(
filename = "report.html",
content = function(file) {
tempReport <- file.path(tempdir(), "report.Rmd")
file.copy("report.Rmd", tempReport, overwrite = TRUE)
params <- list(n = test.text())
params2 <- list(n = test.text2())
rmarkdown::render(tempReport, output_file = file,
params = c(params,params2),
envir = new.env(parent = globalenv()))
}
)
As I have multiple outputs (ggplots, tables and text) I would like to be able to use test.text, test.text2, plot1...plotn etc. in markdonw. separately.
e.g.
---
title: "Dynamic report"
output: html_document
params:
n: NA
---
```{r}
test.text
plot1
``
```{r}
test.text2
plot2
``
If there is an easier way to download an Html/pdf file from a shinyUI that would be amazing!
Thank you!
I figured it out! Thanks #Ruben Kazumov for the help.
In order to be able to add plots to a markdown, or any other thing that you might have inside an output$ you first need to wrap your plot in a reactive function.
e.g.
plot <- reactive({your plot(can be ggplot2 or whatever)}
You can render this in your application by using
output$plottorender <- renderPlot({plot()})
And lastly you can use the plot() you just created in a markdown!
output$report <- downloadHandler(
filename = "report.html",
content = function(file) {
tempReport <- file.path(tempdir(), "report.Rmd")
file.copy("report.Rmd", tempReport, overwrite = TRUE)`
# one list() container object for all the parameters
# all the objects have unique names (keys)
`params <- list(text1 = input$test.text1,
text2 = input$test.text2,
plot1 = plot(),
plot2 = plot())) # ... whatever you have to send to the report
rmarkdown::render(tempReport, output_file = file,
params = params,
envir = new.env(parent = globalenv()))
})
output$report <- downloadHandler(
filename = "report.html",
content = function(file) {
tempReport <- file.path(tempdir(), "report.Rmd")
file.copy("report.Rmd", tempReport, overwrite = TRUE)
# one list() container object for all the parameters
# all the objects have unique names (keys)
params <- list(text1 = input$test.text1,
text2 = input$test.text2,
plot1 = output$plot1,
plot2 = output$plot2)) # ... whatever you have to send to the report
rmarkdown::render(tempReport, output_file = file,
params = params,
envir = new.env(parent = globalenv()))
}
)
Then in report:
```{r}
params$text1 # inside the report, you should call the object by its key
params$plot1
``
```{r}
params$text2
params$plot2
``

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.

Shiny inputs for Rmd files : Reading objects from shinyoutput object not allowed

I have seen in several examples that the input for .Rmd files are coming from input objects.
I have checked the similar questions (also this one)
I have the following problem:
ui <- fluidPage(
fileInput(inputId = "File",label = "Upload file...",accept=c("zip","text")),
radioButtons('format', 'Document format', c('PDF', 'HTML', 'Word'),
inline = TRUE),
downloadButton('downloadReport'),
tableOutput("AEP")
)
)
The input object is a zip file. contains different folders. I extract the suitable folders and picking up my files in server function.
server <- function(input, output){
options(shiny.maxRequestSize=50*1024^2)
output$AEP <- renderTable({
infile=input$File
if (is.null(infile))
return(NULL)
report_list <- c("Park result.txt",
"Park result minus",
"Park result plus")
temp_files <- unzip(infile$datapath)
temp_files <- temp_files[grepl(paste(report_list, collapse = "|"), temp_files)]
T=length(temp_files)
t1=3*c(1:(T/3))
t2=c(1:T)
t2=t2[-t1]
p=c();for(i in 1:T){p[[i]]=c()}
for(i in 1:(length(t1))){p[[t1[i]]]=read.table(temp_files[t1[i]],skip=1,sep=";")}
for(i in 1:(length(t2))){p[[t2[i]]]=read.table(temp_files[t2[i]],skip=2,sep=";")}
...
And finally I have my files as p[[i]]. I do calculations on those p[[i]], at the end my output called AEP (which is a table). I wanna use that table in markdown report. now for having proper input for my Rmd file I need to add this function to server :
output$downloadReport <- downloadHandler(
filename = function() {
paste('my-report', sep = '.', switch(
input$format, PDF = 'pdf', HTML = 'html', Word = '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(n = output$AEP)
# 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())
)
}
)
Which I could not compile it because :
params <- list(n = output$AEP)
and Error : Reading objects from shinyoutput object not allowed.
Any Idea of how I should define params would be very much appreciated. (I have tried n=input$File and it did not worked)
I do not know also how should I proceed with reactive() function in this case.

Resources