0

I would like a shiny app to offer the user the ability to download multiple versions of the same report, depending on user-defined parameters. Using the iris dataset as an example, the idea would be that a report would be generated for each user-defined subset of the dataset (by Species).

My code for the report (test.Rmd) is below:

---
title: "Parametrised report"
output: pdf_document
params:
   species: 
      label: Species
      input: select
      choices: [setosa, versicolor, virginica]
---

```{r}
df <- dplyr::filter(iris, Species %in% params$species)

library(ggplot2)
ggplot(df, aes(Sepal.Length, Petal.Length)) + geom_point()
```

My code for the app.R file is below:

# Define the ui code
ui <- bootstrapPage(

selectInput('species', 'Select species:',
          paste(unique(iris$Species)),
          multiple = TRUE),

downloadButton('DownloadReport')

)


# Define the server code
server <- function(input, output) {

flower_species <- reactive({input$species})

observe({
lapply(flower_species(), function(i) {
  output$DownloadReport <- downloadHandler(

    filename = paste0('test_', i, '.pdf'),

    content = function(file) {

      out <- rmarkdown::render('test.Rmd', rmarkdown::pdf_document(), params = list(species = i))
      file.rename(out, file)

    }

  )
})
})

}

# Return a Shiny app object
shinyApp(ui = ui, server = server)

The user would define the species (one or more of setosa, versicolor, virginica), and if several were selected, not only would the report subset the data to include just that species per report (df <- dplyr::filter(iris, Species %in% params$species)), but a new report would be generated for each of the selected species.

I found this link: Shiny: dynamic UI - a loop around downloadHandler? which builds on this discussion: https://groups.google.com/forum/#!msg/shiny-discuss/qGN3jeCbFRY/xOW5qoVrr94J, but this isn't quite what I need - I don't want multiple download buttons, but one download button that creates independent reports based on the inputs to selectInput. I was hoping the lapply approach shown above would work, but only one report is created. Additionally, I tried a looping approach, but that didn't work (error message was that I tried to do something reactive outside of a reactive environment).

I'm surprised not to find this anywhere - it seems like something that would be useful in many shiny apps. Any help is greatly appreciated.

Community
  • 1
  • 1
Jonny
  • 2,703
  • 2
  • 27
  • 35
  • How about creating the reports in the `lapply` loop and then zipping them and passing this zip file to the `downloadHandler` ? – Tutuchan Mar 15 '16 at 10:31
  • @Tutuchan I'm not sure I follow exactly. Would you be able to write that up as usable code? Many thanks – Jonny Mar 15 '16 at 10:49

1 Answers1

2

I managed to get a working version but it does not work in the RStudio previewer, you have to click on the Show in browser button.

I think you need to remove some parts of the YAML in test.Rmd that are not needed here:

---
title: "Parametrised report"
output: pdf_document
params:
   species: setosa
---

```{r}
df <- dplyr::filter(iris, Species %in% params$species)

library(ggplot2)
ggplot(df, aes(Sepal.Length, Petal.Length)) + geom_point()
```

app.R looks like this :

library(magrittr)

# Define the ui code
ui <- bootstrapPage(

  selectInput('species', 'Select species:',
              paste(unique(iris$Species)),
              multiple = TRUE),

  downloadButton('DownloadReport')

)


# Define the server code
server <- function(input, output) {

  flower_species <- reactive({input$species})

  output$DownloadReport <- downloadHandler(
    filename = "test.zip",
    content = function(file){
      files <- lapply(flower_species(), function(i) {
        rmarkdown::render('test.Rmd', rmarkdown::pdf_document(), output_file = paste0(i, ".pdf"), params = list(species = i))
      }) %>% unlist %>% basename
      zip(file, files)
      file

    }
  )
}

# Return a Shiny app object
shinyApp(ui = ui, server = server)

It is basically the same principle that you wrote earlier but the lapply loop is inside the downloadHandler content element, and you use the zip function to zip all the generated reports into a single zip file that is returned. Maybe throw an unlink after the zip in order to remove all the pdfs.

Tutuchan
  • 1,527
  • 10
  • 19
  • That worked perfectly - many thanks for the quick solution. I wonder if it's absolutely necessary to zip the files, or if a similar method would avoid that step. Nevertheless, this works very elegantly, thanks again! – Jonny Mar 15 '16 at 12:23
  • The thing is I don't know if downloadHandler can handle more than one file, hence the zip. – Tutuchan Mar 15 '16 at 13:16