Une question sur stackoverflow a motivé ce billet : https://stackoverflow.com/q/54845375/3315962. Répondre à la question m’a permis plusieurs choses : confirmer qu’il est possible d’envoyer par email des tableaux HTML réalisés avec flextable et réviser mes connaissances du package sendmailR.

L’objet du post est de montrer comment envoyer des emails contenant des tableaux de reporting depuis R.

On utilise ici deux packages R:

library(flextable)
myft <- flextable(head(iris, n = 10))
myft <- autofit(myft)
myft

Solution 1 : Composer le HTML

On peut créer un contenu HTML assez simplement à condition de connaître les bases du HTML…

On va composer le contenu HTML et y insérer le résultat de la fonction flextable::format(myft, type = "html") qui retourne le code HTML d’un flextable sous forme d’une chaîne de caractères.

raw_html <- paste(
  "<!DOCTYPE html>",
  "<html>",
  "<head>",
  "<meta http-equiv='Content-Type' content='text/html; charset=utf-8' />",
  "<meta name='viewport' content='width=device-width, initial-scale=1.0'/>",
  "<title>your report title</title>",
  "</head>",
  "<body>",
  format(myft, type = "html"),
  "</body>",
  "</html>"
)

Le contenu à afficher étant défini, il faut maintenant l’intégrer dans un email et l’envoyer avec la fonction sendmailR::sendmail.

Attention, il faut bien sûr aussi connaître les paramètres de serveur de mail que vous devez utiliser, pour l’illustration, j’utilise le service de google.

Si vous êtes dans une entreprise et que vous n’avez aucune idée de quoi je parle (serveur SMTP, quel port utiliser, existe t-il un compte pour l’envoi d’email automatique?), je vous invite à prendre un café avec quelqu’un de votre service informatique et de lui demander de vous expliquer ce qu’est un serveur SMTP.

Dans un premier temps, on définit notre contenu HTML comme tel en précisant un Content-Type à la valeur "text/html". Ce contenu doit être dans une liste (mail_message dans notre exemple) comme indiqué dans la documentation de sendmail.

library(sendmailR)

# un contenu mime
body_message <- mime_part(raw_html)
body_message$headers$`Content-Type` <- "text/html"

# le message sera composé d'un seul contenu : body_message
mail_message <- list(body_message)

Il reste à appeler la fonction principale pour envoyer l’email :

sendmail(
  from = "<arthur.dent@gmail.com>",
  to = "<ford.prefect@gmail.com>",
  subject = "this is not a towel",
  msg = mail_message,
  control = list(smtpServer = "ASPMX.L.GOOGLE.COM")
)

Voici ce que peut lire Ford Prefect :

Solution 2 : Utiliser R Markdown

La technique est à peu près la même - en revanche, on ne créé pas le HTML directement, on utilise un document R Markdown. Cela offre de nombreux avantages mais ce n’est pas l’objet du billet - le sujet est trop vaste… Retenons ici que je n’ai plus à comprendre le HTML en travaillant avec R Markdown et que je peux facilement créer des contenus plus riches.

Pour l’illustration, on va utiliser un fichier R Markdown existant dans le package flextable.

an_rmd_example <- system.file(package = "flextable", "examples/rmd/demo.Rmd")

Le contenu du fichier est le suivant:

fpeek::peek_head(an_rmd_example, n = 100)
## ---
## title: "flextable example"
## ---
## 
## ```{r setup, include=FALSE}
## library(knitr)
## library(flextable)
## library(officer)
## library(magrittr)
## library(data.table)
## opts_chunk$set(echo = FALSE, message = FALSE)
## ```
## 
## ```{r}
## col_palette <- c("#D73027", "#F46D43", "#FDAE61", "#FEE08B", 
##   "#D9EF8B", "#A6D96A", "#66BD63", "#1A9850")
## cor_matrix <- cor(mtcars)
## mycut <- cut( cor_matrix, 
##   breaks = c(-1, -0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75, 1), 
##   include.lowest = TRUE, label = FALSE)
## mycolors <- col_palette[mycut]
## data <- data.frame(rowname = row.names(cor_matrix), stringsAsFactors = FALSE) %>%
##   cbind(cor_matrix)
## ```
## 
## ## An example 
## 
## ```{r}
## flextable(data) %>%
##   bg(j = colnames(cor_matrix), bg = mycolors) %>%
##   align(align = "center", part = "all") %>%
##   compose(i = 1, j = 1, value = as_paragraph(""), part = "header") %>% 
##   colformat_num(j = colnames(cor_matrix), digits = 2) %>% 
##   autofit()
## ```
## 
## ## Another example 
## 
## ```{r}
## ft <- flextable(head(iris))
## 
## # measure column widths but only for the body part
## w_body <- dim_pretty(ft, part = "body")$widths
## # measure column widths only for the header part and get the max
## # as height value for rotated text
## h_header <- max( dim_pretty(ft, part = "header")$widths )
## 
## ft <- rotate(ft, j = 1:4, rotation="btlr",part="header")
## ft <- rotate(ft, j = 5, rotation="tbrl",part="header")
## 
## ft <- valign(ft, valign = "center", part = "header")
## ft <- align(ft, align = "center", part = "all")
## 
## # Manage header height
## ft <- height(ft, height = h_header * 1.1, part = "header")
## # ... mainly because Word don't handle auto height with rotated headers
## ft <- hrule(ft, i = 1, rule = "exact", part = "header")
## 
## ft <- set_caption(ft, caption = "iris dataset", autonum = run_autonum(bkm = "iris"))
## ft
## ```
## 
## 
## ## flextable - demo as_grouped_data
## 
## ```{r tab.cap='mean of carbon dioxide uptake in grass plants', tab.id='CO2-id', label='CO2-label'}
## data_CO2 <- dcast(as.data.table(CO2), 
##   Treatment + conc ~ Type, value.var = "uptake", fun.aggregate = mean)
## data_CO2 <- as_grouped_data(x = data_CO2, groups = c("Treatment"))
## 
## as_flextable( data_CO2 ) %>% 
##   bold(j = 1, i = ~ !is.na(Treatment), bold = TRUE, part = "body" ) %>% 
##   bold(part = "header", bold = TRUE ) %>% 
##   width(width = 1.5) %>% 
##   compose(i = ~ !is.na(conc), j = "conc", 
##           value = as_paragraph(
##             as_chunk(conc, formatter = function(x) sprintf("%.0f", x))
##           )
##   ) %>% add_footer_lines("dataset CO2 has been used for this flextable") %>% 
##   bg(bg = "#FFFFFF", part = "footer") %>% 
##   set_header_labels(conc = "Concentration") %>% 
##   width(width = c(1.5, 1, 1)) 
## ```

On génère le fichier HTML avec la fonction rmarkdown::render puis le contenu de ce fichier est stocké dans une chaîne de caractère :

out <- tempfile(fileext = ".html")
rmarkdown::render(an_rmd_example, output_file = out)
raw_html <- paste(readLines(out), collapse = "")

On peut désormais reprendre les mêmes opérations que précédemment :

body_message <- mime_part(raw_html)
body_message$headers$`Content-Type` <- "text/html"

mail_message <- list(body_message)

sendmail(
  from = "<arthur.dent@gmail.com>",
  to = "<ford.prefect@gmail.com>",
  subject = "this is not a towel",
  msg = mail_message,
  control = list(smtpServer = "ASPMX.L.GOOGLE.COM")
)

Complément : ajouter une pièce jointe

Pour l’illustration, on va envoyer aussi le fichier R Markdown comme pièce attachée. Dans la pratique, on a le plus souvent envie de joindre un fichier de données, la démarche est la même.

Il faut définir une nouvelle partie MIME et l’ajouter dans la liste mail_message :

rmd_attachment <- mime_part(x = an_rmd_example, name = "demo_html.Rmd")

mail_message <- list(body_message, rmd_attachment)

sendmail(
  from = "<arthur.dent@gmail.com>",
  to = "<ford.prefect@gmail.com>",
  subject = "this is not a towel",
  msg = mail_message,
  control = list(smtpServer = "ASPMX.L.GOOGLE.COM")
)

Commentaires

Attention à ne pas abuser de l’outil sendmailR - nous recevons déjà tous beaucoup TROP de mails - n’hésitez pas à regarder aussi du côté de slackr écrit par Bob Rudis et beaucoup d’autres. Egalement, faites attention à la taille du message que vous souhaitez envoyer (par exemple, n’envoyez pas des emails de 10 Mo).

Utilisez R Markdown pour aider à la composition de vos contenus depuis R. Si vous avez peu de contenu à envoyer, ce n’est pas vraiment critique ; mais si vous devez envoyer des tableaux, du texte, tout en prenant en compte des paramètres - R Markdown sera votre ami.

Ce post ne couvre qu’une petite partie d’un vaste sujet. Il faudrait idéalement couvrir aussi les problèmes engendrés par l’insertion de javascript, parler des pixels qui permettent de savoir si votre email est consulté, expliquer comment insérer des images… Peut-être une autre fois :)

Aller plus loin

Vous souhaitez aller plus loin dans la dissémination de vos reporting ; contactez nous et nous échangerons sur vos besoins.


Suivez nous:  -  Sites recommandés: R-bloggers R weekly Twitter #rstats Jobs for R-users