Create static report from dynamic shiny markdown - r

I have a shiny markdown app in which I have several figures, say for different days of the week. Above these figures is a text area where I write some comments.
I want to be able to export this report to a static markdown format.
I'm presenting a (mainly) reproducible example below, the first part is the code that I would like to have edited so that it creates the code from the second part in a separate file.
---
title: "WEEKLY REPORT"
runtime: shiny
output: html_document
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = F)
```
```{r header, echo=FALSE}
selectInput("x", label = "x",choices = 1:10,width="100%")
actionButton("button", "Export report")
```
## Monday
```{r monday}
textAreaInput("mon", label = NULL)
renderPlot({
plot(log(1:input$x))
})
```
## Tuesday
```{r tuesday}
textAreaInput("tue", label = NULL)
renderPlot({
plot(sin(1:input$x))
})
```
How can I edit it so the action button creates a new a Rmd file containing the code below (or an Rmd file that would create a similar output)? (change png urls to any existing file to make it reproducible).
---
# title: "WEEKLY REPORT"
output: html_document
---
## Monday
The text I would have put on the first box
![](plot_monday.png)
## Tuesday
The text I would have put on the second box
![](plot_tuesday.png)
So basically the input selectors have to go, the text areas need to be changed to standard text (possibly containing markdown), and the plots have to be exported as picture files for the relevant inputs and then inserted back as pictures in the report.
Ideally I would also like to be able to export monday and tuesday into different Rmd files.

I think the easiest is to use a report template and pass in the inputs as parameters.
So, you create a new report with the filename "sampleRmdReport.Rmd" in the same directory as your Shiny report with the following contents:
---
title: "Weekly Report"
author: "Moody_Mudskipper"
date: '`r format(Sys.Date(),"%Y-%B-%d")`'
output: html_document
params:
mondayText: "holder"
tuesdayText: "holder"
x: "holder"
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = FALSE, results = "asis")
```
# Monday
```{r}
cat(params$mondayText)
```
```{r}
plot(log(1:params$x))
```
# Tuesday
```{r}
cat(params$tuesdayText)
```
```{r}
plot(sin(1:params$x))
```
Then, add the following to your Shiny report:
Download the weekly report:
```{r}
downloadHandler(
filename = function(){
paste0("weeklyReport_generated_"
, format(Sys.Date(), "%Y%b%d")
, ".html")
}
, content = function(file){
rmarkdown::render(input = "sampleRmdReport.Rmd"
, output_file = file
, params = list(mondayText = input$mon
, tuesdayText = input$tue
, x = input$x
))
}
, contentType = "text/html"
)
```
making the full file:
---
title: "WEEKLY REPORT"
runtime: shiny
output: html_document
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = F)
```
```{r header}
selectInput("x", label = "x",choices = 1:10,width="100%")
```
Download the weekly report:
```{r}
downloadHandler(
filename = function(){
paste0("weeklyReport_generated_"
, format(Sys.Date(), "%Y%b%d")
, ".html")
}
, content = function(file){
rmarkdown::render(input = "sampleRmdReport.Rmd"
, output_file = file
, params = list(mondayText = input$mon
, tuesdayText = input$tue
, x = input$x
))
}
, contentType = "text/html"
)
```
## Monday
```{r monday}
textAreaInput("mon", label = NULL)
renderPlot({
plot(log(1:input$x))
})
```
## Tuesday
```{r tuesday}
textAreaInput("tue", label = NULL)
renderPlot({
plot(sin(1:input$x))
})
```
Then, clicking on the "Download" button will generate the report and prompt the user to download it. Note that if you test in RStudio, the file name won't work. I suggest opening it in the browser to test that.
Then, if you want to be able to generate separate daily reports, just add a template for the report you want and a download button for each. Or, you can make your downloadHandler generate a report for each day (from templates) and put them in a zipped directory to download together.
(Note: I tend to find this is more flexible in a Shiny App than in a markdown document, particularly as that allows more control of the download button. Depending on your use case, it may be worth considering that as an approach.)
Based on the comments, here is a version that would upload to Imgur and insert the images that way. Replace the other template with this or add a second button. Note that I did not make the imgur upload function work because I do not have an API key (I assume you do, since you are planning to do it this way).
---
title: "Weekly Report"
author: "Moody_Mudskipper"
date: '`r format(Sys.Date(),"%Y-%B-%d")`'
output:
html_document:
self_contained: false
params:
mondayText: "holder"
tuesdayText: "holder"
x: 1
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = FALSE, results = "asis")
library(ggplot2)
tempDir <- tempdir()
uploadImgur <- function(fileName){
# This function would need to upload with the Imgur API
# I don't have a key, and don't want to set one up
# for this example.
# It would return the url that imgur assigns the image
# here, I am using a placeholder
outUrl <- "https://i.imgur.com/WsUV4DK.gif"
return(outUrl)
}
```
# Monday
```{r}
cat(params$mondayText)
```
```{r}
tempPlot <-
ggplot(mapping = aes(x = 1:params$x
, y = log(1:params$x))) +
geom_point() +
xlab("X") +
ylab("Y")
tempFile <- tempfile("plot_", tempDir, ".png")
ggsave(tempFile, tempPlot, width = 4, height = 4)
imgurURL <- uploadImgur(tempFile)
cat("![](", imgurURL,")", sep = "")
```
# Tuesday
```{r}
cat(params$tuesdayText)
```
```{r}
tempPlot <-
ggplot(mapping = aes(x = sin(1:params$x)
, y = log(1:params$x))) +
geom_point() +
xlab("X") +
ylab("Y")
tempFile <- tempfile("plot_", tempDir, ".png")
ggsave(tempFile, tempPlot, width = 4, height = 4)
imgurURL <- uploadImgur(tempFile)
cat("![](", imgurURL,")", sep = "")
```

If you are ok with using ggplot you can use ggsave to save the plots locally as png.
You could try this for your main file:
---
title: "WEEKLY REPORT"
runtime: shiny
output: html_document
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = F)
library(ggplot2)
```
```{r header, echo=FALSE}
selectInput("x", label = "x",choices = 1:10,width="100%")
actionButton("button", "Export report")
observeEvent(input$button,{
params <- list(text = input$mon)
render('report.Rmd',params = params)
})
```
## Monday
```{r monday}
textAreaInput("mon", label = NULL)
renderPlot({
p <- ggplot(data.frame(x=1:input$x,y=log(1:input$x)),aes(x=x,y=y))+
geom_point()
ggsave("plot.png",plot=p)
p
})
```
And for your static report (needs to be in the same folder as the other .Rmd):
---
# title: "WEEKLY REPORT"
output: html_document
---
## Monday
```{r}
params$text
```
![plot](plot.png)
I'm not sure how to make a proper download button with a download handler in a shiny Rmd so this only works when run from RStudio and not in a browser.

Related

Rmarkdown arrange plot in params side by side

I have a shiny application where I return two chart in a reactive element. Then I pass the two charts as params to my rmarkdown file. I can't resize them nor print them side by side with download button.
Here is my rmarkdown code
---
# always_allow_html: true
output:
html_document
params:
plot_1: 'NULL'
plot_2: 'NULL'
---
{r setup, include=FALSE}
options(tinytex.verbose = TRUE)
knitr::opts_chunk$set(echo = FALSE, fig.retina = 1)
library(webshot)
{r, figures-side, fig.show="hold", out.width="50%", echo=FALSE}
# plot_1()
params$plot_1
params$plot_2
this is my download script:
output$download <- downloadHandler(
filename = "new_report.pdf",
content = function(file) {
tempReport <- normalizePath('report.Rmd')
file.copy("myRmarkdown.Rmd", tempReport, overwrite = TRUE)
params <- list(plot_1=plot_1()
,plot_2=plot_2()
)
html_fn <- rmarkdown::render(tempReport, params = params,
envir = new.env(parent = globalenv()))
pagedown::chrome_print(html_fn, file)
}
)
Is there any way to arrange plots in params side by side?
Here is the actual output:

RMarkdown - Is there a way to replace a plot view with datatable view and vice-versa?

UPDATE: Edited code to depict tabs already exist
I have a Quarto qmd file with an R plotly figure rendered as html. I want to provide an option (button/link) for the user to change the view from plot to the data table, and vice versa. What is the best way to go about this?
The code below shows the plot and table side by side. I would like to show either the plot or the table with the option to switch view to the other.
---
title: "MTCars"
format:
html:
code-fold: true
---
::: panel-tabset
## Tab 1
```{r, echo=FALSE, out.height="30%"}
#| warning: false
#| layout-ncol: 2
library(DT)
library(plotly)
plot_ly( mtcars,
x = ~disp,
y = ~wt,
type = 'scatter',
mode = 'lines',
height = 400,
width = 700
)
datatable(mtcars)
```
## Tab 2
:::
You could use a (nested) tabset panel? I.e.
---
title: "MTCars"
format: html
---
::: {.panel-tabset}
## Tab 1
::: {.panel-tabset}
## Plot
```{r, error=FALSE, message=FALSE, echo=FALSE}
library(plotly)
plot_ly(mtcars,
x = ~disp,
y = ~wt,
type = 'scatter',
mode = 'lines',
height = 400,
width = 700
)
```
## Table
```{r, echo=FALSE}
library(DT)
datatable(mtcars)
```
:::
## Tab 2
:::
Output:
See: https://quarto.org/docs/interactive/layout.html#tabset-panel
**Update: Nested version according to comment.

Creating and showing vector in a reactive environtment in Shiny

I having a problem using the shiny reactive environment. My problem is that the vector is not being completed. Everytime I presss the buttom the output goes to the row bellow, but erase the previous elemnt.
This is my code:
---
title: "teste"
author: "Teste1"
date: "03/01/2022"
output: html_document
runtime: shiny
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```
```{r}
actionButton("action1", "Press Button")
```
```{r, echo= FALSE, message = FALSE}
reactive({
u <- input$action1
w<- u + 2651641684
x <- c()
x[input$action1] <- paste("Texto",format(Sys.time(), "%S"))
print(x)
})
```
Why when I print(x) the vector doesnt store the previous elements when I press the button?
Any help?
Does this give you what you want?
---
title: "teste"
author: "Teste1"
date: "03/01/2022"
output: html_document
runtime: shiny
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```
```{r}
actionButton("action1", "Press Button")
```
```{r, echo= FALSE, message = FALSE}
v <- reactiveValues(x=c(), u=0)
observeEvent(input$action1, {
v$u <- v$u + 1
w <- v$u + 2651641684
v$x[v$u] <- paste("Texto",format(Sys.time(), "%S"))
print(v$x)
})
```

render images in a loop inside Rmarkdown chunk

I have a bunch of pre-saved plots inside a folder.
I have a Rmarkdown(Flexdashboard) looping this folder and showing a picture per tab this way (an example taking just one)
```{r ,results="asis"}
list_plots <- list.files(plots_folder,pattern = ".png", full.names = TRUE)
cat(" \n###", "Tab Name \n")
knitr::include_graphics(list_plots[1])
cat(" \n")
```
this worked flawlessly. My issue is when using a loop. The tabs are rendered but no plot inside. This way:
```{r ,results="asis"}
list_plots <- list.files(plots_folder,pattern = ".png", full.names = TRUE)
for(i in plots) {
cat(" \n###", "tab name \n")
knitr::include_graphics(i)
cat(" \n")
}
```
Similarly to this, I'll drop here a fully reproducible code for flexdashboard:
---
title: "Test"
output:
flexdashboard::flex_dashboard:
orientation: columns
vertical_layout: fill
---
```{r setup, include=FALSE}
library(flexdashboard)
```
```{r create plots}
# reproducible example
tmp <- tempdir()
x <- lapply(5:10, function(x){
png(file.path(tmp, paste0(x,".png")))
plot(seq(x))
dev.off()
})
```
```{r, results='asis'}
list_plots <- list.files(tmp, pattern = ".png", full.names = TRUE)
for(i in list_plots) {
cat(sprintf("\n### tab name\n![](%s)\n", i))
}
```

How to print short caption on each page before header of long flextable (rmarkdown, word output)

I have a long flextable, and I knit it to word output. My flextable has a caption, and I want to print a 'additional caption' (short version of my caption) on each page before the header of my flextable. I am trying to find the solution, but I can't find any information about it.
Is there any solution for it?
---
output:
word_document
---
```{r}
library(flextable)
ft1 <- flextable(iris)
library(officer)
ft1 <- set_caption(ft1, caption = "My caption", autonum = run_autonum() )
autofit(ft1)
```
The 'additional caption' that I need is marked red on the image attached below.
Something like that?
---
title: "Untitled"
output: word_document
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```
```{r}
library(flextable)
ft1 <- flextable(iris)
library(officer)
ft1 <- add_header_lines(ft1, "a short caption")
ft1 <- align(ft1, i = 1, part = "header", align = "right")
ft1 <- color(ft1, i = 1, part = "header", color = "orange")
ft1 <- set_caption(ft1, caption = "My caption", autonum = run_autonum() )
autofit(ft1)
```

Resources