Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Attach a knitted tempfile to email in R shiny

I have a working shiny app that uses Mailgun to send an email when a button is clicked and also produces a rmarkdown report when another button is clicked.

Here is working code, obviously without the working email authentication:

ui.R

library(shiny)

# Define UI for application that draws a histogram
shinyUI(fluidPage(
  sliderInput("slider", "Slider", 1, 100, 50),
  downloadButton("report", "Generate report"),
  actionButton("mail", "send email"),
  textOutput('mailo')
)
)

server.R

library(shiny)

sendEmail <- function(email = "[email protected]",
                      mail_message = "Hello"){

  url <- "https://api.mailgun.net/v3/sandboxxxxxxxxx.mailgun.org/messages"
  ## username:password so api_key is all after the api:
  api_key <- "key-0xxxxxxxxxxxx"
  the_body <-
    list(
      from="Mailgun Sandbox <[email protected]>",
      to=email,
      subject="Mailgun from R test",
      text=mail_message
    )

  req <- httr::POST(url,
                    httr::authenticate("api", api_key),
                    encode = "form",
                    body = the_body)

  httr::stop_for_status(req)

  TRUE

}


# Define server logic required to draw a histogram
shinyServer(function(input, output) {

  event <- observeEvent(input$mail,{     
                                  sendEmail()
                                  }, ignoreInit = TRUE)
  output$mailo <- renderText({print("EMAIL SENT!")})

  output$report <- downloadHandler(
    # For PDF output, change this to "report.pdf"
    filename = "report.html",
    content = function(file) {
      # Copy the report file to a temporary directory before processing it, in
      # case we don't have write permissions to the current working dir (which
      # can happen when deployed).
      tempReport <- file.path(tempdir(), "report.Rmd")
      file.copy("report.Rmd", tempReport, overwrite = TRUE)

      # Set up parameters to pass to Rmd document
      params <- list(n = input$slider)

      # Knit the document, passing in the `params` list, and eval it in a
      # child of the global environment (this isolates the code in the document
      # from the code in this app).

      rmarkdown::render(tempReport, 
                        output_file = file,
                        params = params,
                        envir = new.env(parent = globalenv())
      )
    }
  )

})

I want to do both things in one step. That is, generate the report, attach it to the email and send it to the given address. I am just not sure how to treat a tempfile() when referencing the file.

I also currently have the app deployed on Shinyapps.io, so saving to file and then retrieving won't work.

Any ideas?

like image 376
eflores89 Avatar asked Apr 04 '18 19:04

eflores89


1 Answers

This is the code you need. I tested this and it worked, although my gmail did give me huge bright yellow warnings that the email contains an unverified file that may be dangerous. I also simplified the app a bit and removed some unnecessary code.

library(shiny)

ui <- fluidPage(
  sliderInput("slider", "Slider", 1, 100, 50),
  actionButton("mail", "send email")
)

sendEmail <- function(email = "[email protected]",
                      mail_message = "Hello",
                      file = NULL) {

  url <- "https://api.mailgun.net/v3/sandboxxxxxxxxxxxxxxxxxxxxxxxx.mailgun.org/messages"
  ## username:password so api_key is all after the api:
  api_key <- "XXXXXXXXXXXXXXXXXX-XXXXXXXXX-XXXXX"
  the_body <-
    list(
      from = "Mailgun Sandbox <[email protected]>",
      to = email,
      subject = "Mailgun from R test",
      text = mail_message
    )
  if (!is.null(file)) {
    the_body$attachment = httr::upload_file(file)
  }

  req <- httr::POST(url,
                    httr::authenticate("api", api_key),
                    encode = "multipart",
                    body = the_body)

  httr::stop_for_status(req)

  TRUE

}

server <- function(input, output, session) {
  observeEvent(input$mail, {
    # Copy the report file to a temporary directory before processing it, in
    # case we don't have write permissions to the current working dir (which
    # can happen when deployed).
    tempReport <- file.path(tempdir(), "report.Rmd")
    file.copy("report.Rmd", tempReport, overwrite = TRUE)

    # Set up parameters to pass to Rmd document
    params <- list(n = input$slider)

    # Knit the document, passing in the `params` list, and eval it in a
    # child of the global environment (this isolates the code in the document
    # from the code in this app).

    file <- rmarkdown::render(tempReport,
                      output_file = file.path(tempdir(), "report.html"),
                      params = params,
                      envir = new.env(parent = globalenv())
    )

    sendEmail(file = file)
  })
}

shinyApp(ui, server)

By the way, there's also an IMmailgun package in case you're interested, but it achieves essentially what you do with this code.

like image 136
DeanAttali Avatar answered Sep 20 '22 02:09

DeanAttali