Jonny
Jonny

Reputation: 2783

Generating independent dynamic reports passing user-defined parameters in r + shiny

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.

Upvotes: 0

Views: 725

Answers (1)

Tutuchan
Tutuchan

Reputation: 1567

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.

Upvotes: 2

Related Questions