Generating markdown reports from shiny?


#1

This tread is a continuation from the following: Generating markdown report in shiny?


I would like to bump this thread up again. The OP asked for a way to render the markdown document in the app itself, which I think is a very reasonable thing to do -- the user would be able to check that document addresses /matches their needs/inputs. I don't think any of the answers address that as the conversation steered towards producing hundreds of report files.

To the effect of showing the result in the app: My understanding is that includeHTML and includeMarkdown (http://shiny.rstudio.com/reference/shiny/1.1.0/include.html) are intended to act on static content of the app only (at least based on my attempt to pass output$whatever to them). A brute force solution might be to base::scan() the intermediate .html/.md file and pass it properly to htmlOutput(), but I hope somewhat more elegant solutions exist.


Generating markdown report in shiny?
#2

Hi @StasK ,

To make sure I understand the objective, the Rmd file will serve as the UI? If so, then the following example might help you get started.


tldr

ui.R

htmlOutput("renderedReport")

server.R

output$renderedReport <- renderUI({           
	includeMarkdown(knitr::knit('my_file.Rmd'))           
})

my_markdown_file.Rmd

## Hello, World!
some text here
..


Overview

I pulled the librarians data set from 538's data repo as an example (1). In this app, I wanted to view the areas with the most employed librarians of chosen US state.

My initial thoughts were to render the report using a shiny input(s) and to trigger the rendering of the report once a button was clicked. I did it this way as it would save on valuable computing time if the app were to be hosted on AWS or Digital Ocean.

The rendering of the rmarkdown will take place on the server side (knitr::knit('my_file.Rmd') and sent to the ui via renderUI and includeMarkdown. On the front end, the markdown output will be received using htmlOutput.

Using the shinyjs package, I smoothed the display of the content using hide and show.

ui.R

In this example, I'm using the sidebarLayout and a selectInput to filter the data by state (USA). After selection is made, the user clicks the View Report button.

sidebarPanel(

	# filter by state
	selectInput(inputId = "state",
				label = "Choose a state",
              choices = c("Choose a state", unique(librarians$prim_state)),
              multiple = FALSE),
                
 	# button
	actionButton(inputId = "report",label="View Report")
)

The mainPanel is fairly short. I added a loading... message and wrapped the Rmarkdown output to ease the rendering of the content. Additional styling can be applied to the the report using tags$style or in separate css file.

...
mainPanel(
	# show loading screen
	shinyjs::hidden(
		tags$div(id="loading",
			tags$style("#loading{position:absolute;}"),
			tags$h1("Loading..."))
	),
              
	# report
	tags$div(id="report-wrapper",
		tags$style("#report-wrapper p{font-size:14pt;}"),
		htmlOutput("renderedReport")
	)
)
...

server.R

On the server side, everything is triggered by observeEvent(input$report,{...}).

The first step is to hide the report div and show the loading message (For fun, the shinycustomloader package can be used to make cool loading screens (2)).

Next, the data is filtered based on the input. If nothing is selected, the full dataset is returned.

The markdown file is rendered and sent the UI using the following code.

output$renderedReport <- renderUI({
	includeMarkdown(knitr::knit("report_template.Rmd"))         
})

The final step is to hide the loading message and show the report.

rmarkdown file

I wrote the Rmd file as normal. In the Rmd file, I'm calling the object defined by the filtering step.

Limitations

The beauty of this method is that markdown is fairly straightforward to write and the UI can be modified fairly easy. This could be useful if you are collaborating with non-R folks that are familiar with markdown.

The downsides is that the rendering of the report can be slow. Additional YAML configurations and adjusting the shinyjs hide/show speed might help with the lag. The ggplot outputs were pretty poor. I imagine that manually styling plots via theme(..), setting the dpi, and/or css will help make the objects readable.

I didn't test interactive visualizations. I would imagine the same methods for interactive documents would apply here.

I hope this helps!


I posted the full code on github: https://github.com/davidruvolo51/shinyAppTutorials/tree/master/rmarkdown-app

Sources

  1. 538 Librarian data: https://github.com/fivethirtyeight/data/tree/master/librarians
  2. Shinycustomloader pkg: https://github.com/emitanaka/shinycustomloader

#3

Thanks, David. I am sure I'll be able to figure it out using your templates.