This is a followup or more a simplification of this question Error: File header.tex not found in resource path in a rmarkdown generated pdf report from a shiny app
With this Rmarkdown code I can achieve what I want:
logo.png
report.Rmd
---
geometry: margin=20truemm
fontfamily: mathpazo
fontsize: 11pt
documentclass: article
classoption: a4paper
urlcolor: blue
output:
pdf_document:
header-includes:
- \usepackage{fancyhdr}
- \pagestyle{fancy}
- \rhead{\includegraphics[width = .05\textwidth]{logo.png}}
params:
scores: NA
---
<!-- ```{r, echo=FALSE} -->
<!-- hist(params$scores) -->
<!-- ``` -->
```{r}
hist(runif(100))
```
Getting desired output: The R logo is in the header:
Now I would like to do the same from a shiny app
For this I pass the plot as a parameter and uncomment the relevant part in the report.Rmd file
relevant part in report.Rmd file:
```{r, echo=FALSE}
hist(params$scores)
```
app.R
# Global variables can go here
n <- 200
# Define the UI
ui <- bootstrapPage(
numericInput('n', 'Number of obs', n),
plotOutput('plot'),
downloadButton('report', 'Generate Report')
)
# Define the server code
server <- function(input, output) {
output$plot <- renderPlot({
hist(runif(input$n))
})
# create markdown report ----------------------------------
output$report <- downloadHandler(
filename = "report.pdf",
content = function(file) {
tempReport <- file.path(tempdir(), "report.Rmd")
file.copy("report.Rmd", tempReport, overwrite = TRUE)
params <- list(scores = input$n)
rmarkdown::render(tempReport, output_file = file,
params = params,
envir = new.env(parent = globalenv())
)
}
)
}
# Return a Shiny app object
shinyApp(ui = ui, server = server)
Error:
! Package pdftex.def Error: File `logo.png' not found: using draft setting.
I suspect because it works locally logo.png is not found in the temporary file where shiny saves tempReport
But I don't know why this works when knitting from markdown and not when calling it from the shiny app.
I think I have been through of the relevant sites on the internet!
Many thanks!
Basically you already figured out what's the issue. Hence one approach to fix your issue would be to do copy both the report template and the logo to the same temporary directory.
# Define the server code
server <- function(input, output) {
output$plot <- renderPlot({
hist(runif(input$n))
})
# create markdown report ----------------------------------
output$report <- downloadHandler(
filename = "report.pdf",
content = function(file) {
td <- tempdir()
tempReport <- file.path(td, "report.Rmd")
tempLogo <- file.path(td, "logo.png")
file.copy("report.Rmd", tempReport, overwrite = TRUE)
file.copy("logo.png", tempLogo, overwrite = TRUE)
params <- list(scores = input$n)
rmarkdown::render(tempReport,
output_file = file,
params = params,
envir = new.env(parent = globalenv())
)
}
)
}
Related
I'm trying to knit a word document from a shiny app using a template.docx file. I get the following error message:
pandoc.exe: template.docx: openBinaryFile: does not exist (No such file or directory)
The following 3 files were all currently located in the same directory.
app.R:
library(shiny)
library(rmarkdown)
ui <- fluidPage(
titlePanel("Word template"),
sidebarLayout(
sidebarPanel(),
mainPanel(
downloadButton("download", "Download Report")
)
)
)
server <- function(input, output) {
output$download <- downloadHandler(
filename = function() {
"report.docx"
},
content = function(file) {
src <- normalizePath('report.Rmd')
owd <- setwd(tempdir())
on.exit(setwd(owd))
file.copy(src, 'report.Rmd', overwrite = TRUE)
out <- render('report.Rmd',
envir = new.env(parent = globalenv()))
file.rename(out, file)
}
)
}
shinyApp(ui = ui, server = server)
report.Rmd:
---
title: "Test"
output:
word_document:
reference_docx: template.docx
---
`r getwd()`
```{r}
mtcars
```
template.docx is an empty "new" Word 2016 document
In the downloadHandler() function you need to copy both .Rmd and .docx files to the temporary directory
content = function(file) {
src <- normalizePath(c('report.Rmd', 'template.docx')) # SEE HERE
owd <- setwd(tempdir())
on.exit(setwd(owd))
file.copy(src, c('report.Rmd', 'template.docx'), overwrite = TRUE) # SEE HERE
out <- render('report.Rmd',
envir = new.env(parent = globalenv()))
file.rename(out, file)
}
Thoughts
I wouldn't have worked this out without preparing a reproducible example and thinking about how to describe the problem. It's worth thinking about how to ask your question!
I am trying to put a progress bar around my shiny downloadHandler(). The progress bar should show the render status of the rmarkdown HTML
I found this infomation on GitHub (https://github.com/rstudio/shiny/issues/1660) but could not get it to work. If I define no environment the file can not be knitted.
app.R
library(shiny)
library(rmarkdown)
ui <- fluidPage(
sliderInput("slider", "Slider", 1, 100, 50),
downloadButton("report", "Generate report"),
textOutput("checkrender")
)
server <- function(input, output, session) {
output$checkrender <- renderText({
if (identical(rmarkdown::metadata$runtime, "shiny")) {
TRUE
} else {
FALSE
}
})
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 = input$slider)
rmarkdown::render(tempReport,
output_file = file,
params = params,
envir = new.env(parent = globalenv())
)
}
)
}
shinyApp(ui = ui, server = server)
report.Rmd
---
title: "Dynamic report"
output: html_document
params:
n: NA
---
```{r}
params$n
```
A plot of `params$n` random points.
```{r}
plot(rnorm(params$n), rnorm(params$n))
```
Your solution was quite close!
Two problems I see with your code:
You've left out the withProgress call in your downloadHandler code
The test for whether you're running in a shiny environment, if (identical(rmarkdown::metadata$runtime, "shiny")), needs to go in your .Rmd file. You enclose any calls to increment/set the progress bar in this test, otherwise the .Rmd code will produce errors like Error in shiny::setProgress(0.5) : 'session' is not a ShinySession object.
The below reworking of your code should work:
app.R
library(shiny)
library(rmarkdown)
ui <- fluidPage(
sliderInput("slider", "Slider", 1, 100, 50),
downloadButton("report", "Generate report"),
textOutput("checkrender")
)
server <- function(input, output, session) {
output$checkrender <- renderText({
if (identical(rmarkdown::metadata$runtime, "shiny")) {
TRUE
} else {
FALSE
}
})
output$report <- downloadHandler(
filename = "report.html",
content = function(file) {
withProgress(message = 'Rendering, please wait!', {
tempReport <- file.path(tempdir(), "report.Rmd")
file.copy("report.Rmd", tempReport, overwrite = TRUE)
params <- list(n = input$slider)
rmarkdown::render(
tempReport,
output_file = file,
params = params,
envir = new.env(parent = globalenv())
)
})
}
)
}
shinyApp(ui = ui, server = server)
report.Rmd
---
title: "Dynamic report"
output: html_document
params:
n: NA
---
```{r}
params$n
if (identical(rmarkdown::metadata$runtime, "shiny"))
shiny::setProgress(0.5) # set progress to 50%
```
A plot of `params$n` random points.
```{r}
plot(rnorm(params$n), rnorm(params$n))
if (identical(rmarkdown::metadata$runtime, "shiny"))
shiny::setProgress(1) # set progress to 100%
```
Another version of answer.
With rmarkdown version 1.14, answer by jsavn seems to not work. Because
rmarkdown::metadata does not have $runtime. (I tried to capture value of rmarkdown::metadata$runtime by saving it as .rds during rendering by rmarkdown::render but it only had value of YAML and metadata$runtime was NULL.
So, for allow setProgress to work with "non-shiny" rendering, pass parameter from shiny-app may be better solution, since this will not depend on values of metadata (which may change as rmarkdown version changes).
app.R
library(shiny)
library(rmarkdown)
ui <- fluidPage(
sliderInput("slider", "Slider", 1, 100, 50),
downloadButton("report", "Generate report")
)
server <- function(input, output, session) {
output$report <- downloadHandler(
filename = "report.html",
content = function(file) {
withProgress(message = 'Rendering, please wait!', {
tempReport <- file.path(tempdir(), "report.Rmd")
file.copy("report.Rmd", tempReport, overwrite = TRUE)
params <- list(n = input$slider,
rendered_by_shiny = TRUE)
rmarkdown::render(
tempReport,
output_file = file,
params = params,
envir = new.env(parent = globalenv())
)
})
}
)
}
shinyApp(ui = ui, server = server)
report.Rmd
---
title: "Dynamic report"
output: html_document
params:
n: 10
rendered_by_shiny: FALSE
---
```{r}
params$n
if (params$rendered_by_shiny)
shiny::setProgress(0.5) # set progress to 50%
```
A plot of `params$n` random points.
```{r}
plot(rnorm(params$n), rnorm(params$n))
if (params$rendered_by_shiny)
shiny::setProgress(1) # set progress to 100%
```
I've created ShinyApp where everything works fine. I'd like to add downloadHandler to generate Markdown report that contains the chosen plot. Firstly, I upload a file into ShinyApp. Nextly, I select variables to be plotted using checkBoxInput. Next step is using dropdown list to select between Lattice/ggplot2 plot and finally I'd like to click download it and get it.
Unfortunately, every time I do try to download it I receive a blank Markdown page. It doesn't really matter what format of report will be generated. I'd like to get an appropiate logic for this task. I tried both solutions I found in a network:
output$downloadReport <- downloadHandler(
filename = function() {
paste('my-report', sep = '.', switch(
input$format, PDF = 'pdf', HTML = 'html', Word = 'docx')
)
},
content = function(file) {
src <- normalizePath('report.Rmd')
owd <- setwd(getwd())
on.exit(setwd(owd))
file.copy(src, 'report.Rmd', overwrite = TRUE)
out <- render('report.Rmd', switch(
input$format,
PDF = pdf_document(), HTML = html_document(), Word = word_document()
))
file.rename(out, file)
})
and
output$report <- downloadHandler(
filename = "report.html",
content = function(file) {
tempReport <- file.path(getwd(), "report.Rmd")
file.copy("report.Rmd", tempReport, overwrite = TRUE)
params <- list(graph = input$graph, colsel = input$colsel)
rmarkdown::render(tempReport, output_file = file,
params = params,
envir = new.env(parent = globalenv())
So respectively I created report.rmd templates for my app to fullfill it. I tried to put a lot of things inside but none of these works. Do I miss the logic for the template?
---
title: "Untitled"
author: "user"
date: "date"
output: html_document
runtime: shiny
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```
```{r plot, echo=TRUE}
plotdata <- reactive({
d <- dataIn()[, c(req(input$colsel))]
d <- melt(d, id.vars="Numer")
})
plot1 <- reactive({
dotplot(value~Numer, data=plotdata(), auto.key = list(space="right", title="Types"), groups=variable)
})
plot2 <- reactive({
ggplot(plotdata(), aes(x=Numer, y=value, color=variable)) +
geom_point()
})
graphInput <- reactive({
switch(input$graph,
"Lattice" = plot1(),
"ggplot2" = plot2())
})
renderPlot({
graphInput()
})
})
```
Alright, I got it finally! Firstly, we need to run shinyApp using function "Run External". Secondly we don't need that mess I made in the template. Simple:
---
title: "Untitled"
author: "user"
date: "date"
output: html_document
runtime: shiny
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
library(shiny)
library(ggplot2)
library(lattice)
library(markdown)
```
```{r plot}
plot1()
plot2()
graphInput()
```
Where plot1(), plot2() and graphinput() represent my:
plot1 <- reactive({
dotplot(value~Numer,data=plotdata(), auto.key = list(space="right", title="WWW"), groups=variable)
})
plot2 <- reactive({
ggplot(plotdata(), aes(x=Numer, y=value, color=variable)) +
geom_point()
})
graphInput <- reactive({
switch(input$graph,
"Lattice" = plot1(),
"ggplot2" = plot2()
)
})
I want to use data from my shiny app as params in Rmarkdown. How can I get my data and use it. Here is my app:
library(shiny)
library(readxl)
dat2 <-read_xlsx("data/iepp_18.xlsx")
shinyApp(
ui = fluidPage(
selectInput("Ecole","Ecole", as.character(sort(unique(dat2$Nom_Ecole)))),
downloadButton("report", "Generate report")
),
server = function(input, output) {
output$report <- downloadHandler(
filename = "report.html",
content = function(file) {
tempReport <- file.path(tempdir(), "Reports.Rmd")
file.copy("Reports.Rmd", tempReport, overwrite = TRUE)
params <- list(base = input$Ecole)
rmarkdown::render(tempReport, output_file = file,
params = params,
envir = new.env(parent = globalenv())
)
}
)
}
)
And here is my Rmd yaml
title: "Dynamic report"
output: html_document
params:
base: NA
Is it not possible to subset the data in my Rmd with params$base?
``` r
data_bulletin %>%
filter(identification_nom_etablissement==params$base)
```
Is it not possible to subset with params?
Is it possible to use data from my shiny app in Rmarkdown?
I'm basing my code below on this page on R Studio on creating apps with downloadable reports. I've created the following app and .Rmd documents, which are both saved in the same directory:
app.R:
library(rmarkdown)
library(shiny)
shinyApp(
ui = fluidPage(
sliderInput("slider", "Slider", 1, 100, 50),
downloadButton("report", "Generate report")
),
server = function(input, output) {
output$report <- downloadHandler(
filename = "report.pdf",
content = function(file) {
tempReport <- file.path(tempdir(), "report.Rmd")
file.copy("report.Rmd", tempReport, overwrite = TRUE)
params <- list(n = input$slider)
render(tempReport, output_file = file,
params = params,
envir = new.env(parent = globalenv())
)
}
)
}
)
report.Rmd:
---
title: "Dynamic report"
output: pdf_document
params:
n: NA
---
```{r}
# The `params` object is available in the document.
params$n
```
A plot of `r params$n` random points.
```{r}
plot(rnorm(params$n), rnorm(params$n))
```
When I click on the "Generate report" button, the file that gets saved is called "report", when in fact I named it "report.pdf". I end up having to add ".pdf" manually into the filename in order for my computer to recognize it as a PDF document.
Is there a reason why the filename doesn't match exactly what I specified? What else am I supposed to do?
Try to set the the contentType argument of downloadHandler to the correct MIME type, i.e. to "application/pdf".