Comment faire ses templates RMarkdown et Shiny ?

What the Fuck - Stop the copy and paste bullshit already, it's played out
Auteur : Elena Salette
Tags : Ressources, Autour de R
Date :

Ça y est, ça t’a pris un temps fou, mais tu y es enfin arrivé ! Un superbe document RMarkdown, une magnifique application Shiny, un CSS parfait, le logo de ta société, le tout respectant à la lettre la charte graphique que tu devais suivre. C’était du boulot, tu en es fier, et tu veux que tout le monde dans ta boite en profite, et toi le premier, sans devoir faire tout un tas de copier/coller. Car recommencer from scratch à chaque fois, c’est une vraie perte de temps. Alors, comment ? En en faisant un template, paradis !

C’est quoi, un bon template ?

Qu’on se mette tout de suite d’accord : créer un template, ça prend du temps, surtout si on fait les choses bien. Alors est-ce-que ça vaut vraiment le coup ? La réponse est bien sûr : oui ! Si vous avez atterri sur cette page, c’est qu’à priori, vous envisagez de faire un template. Pourquoi ? Pour vous éviter un travail répétitif, de repartir de zéro à chaque fois que vous créez une nouvelle application Shiny ou un nouveau document, et que de copier à la main des fichiers ou des bouts de code ne figure pas le top des bonnes pratiques. Factoriser ses process de mise en forme, c’est au final un gain de temps énorme.

En plus de garantir une uniformité de vos documents, un template permet également d’assurer une certaine sécurité de ne rien oublier. Vous pouvez ainsi faire le choix d’inclure automatiquement un logo, des informations clés, ou par exemple une clause de confidentialité en fin de document, ou encore un onglet “Contact” à toutes vous applications.

Pour en revenir à notre question première : c’est quoi un bon template ?

  • c’est un template complet : toutes les règles de styles que vous aurez créées doivent être illustrées. Les différents niveaux de titres, les classes, les tableaux, les images, les styles particuliers… Il préférable de trop en mettre et de laisser à l’utilisateur de votre template la possibilité de supprimer les composants dont il n’aura pas besoin, plutôt que de le faire chercher comment inclure tel ou tel élément.
  • les zones à modifier/compléter ou à garder absolument doivent être mises en évidence dans votre code. Il conviendra donc de commenter et documenter au maximum votre template.
  • il devra avoir un nom parlant : email_template, cv_template, monthly_report_template, sales_dashboard_template, …

Astuce : Si la structure de votre document ou de votre application cible est bien définie et que le but est que l’utilisateur n’ait plut qu’à “remplir les cases” avec son propre contenu, je vous conseille fortement d’utiliser le package {shinipsum} qui vous permettra de générer du texte, des tableaux, ou encore des graphes, et ce faisant de créer un template ayant une architecture fixée.

La première étape : un package R

Si je souhaite partager mes templates R avec mes collègues, il va falloir dans un premier temps que je crée un package (vous ne savez pas comment faire ? Utilisez {fusen} pour créer vos packages depuis un Rmd). Une fois que ce dernier sera installé par l’utilisateur, il aura un accès direct aux templates que nous avons créés, simplement via clic-bouton.

Vous pouvez inclure des fonctions à votre package, mais ce n’est pas une nécessité.

Pour nos exemples à suivre, créons un package {myTemplates} : File > New Project… > New Directory > R Package using devtools

On se retrouve avec les fichiers et dossiers suivants :

Prenez soin de compléter le fichier DESCRIPTION.

Les templates R Markdown

Créer son template Rmd PDF grâce à {pagedown}

Je vais commencer par une petite digression ici et vous parler de {pagedown} rapidement (même s’il mériterait un article dédié). Ce package constitue la manière la plus simple de personnaliser ses documents Rmd PDF, surtout si comme moi, vous ne portez pas vraiment LaTeX dans votre cœur. Quoi ? on peut créer des PDFs sans faire de LaTeX ? Ouiii ! {pagedown} nous permet en effet de faire un Rmarkdown HTLM qui sera ensuite converti en PDF, via un convertisseur de navigateur comme Chrome, en utilisant la librairie JavaScript paged.js qui met en (vraies) page un HTML.

Libre à nous alors de jouer avec du CSS ! Et même avec un niveau basique en CSS, on arrive rapidement à un résultat personnalisé au rendu professionnel. On saluera donc le travail de Xihui Xie, responsable de tous les down, et Romain Lesur, qui signent ce package ô combien utile.

J’ai mon template, et maintenant ?

Voilà, j’ai fait, en dehors de mon package, un beau template RMarkdown :

Au niveau des fichiers dont j’ai besoin pour que mon template tourne bien, j’ai :

  • un .Rmd
  • un dossier www/ contenant :

Je vais avoir besoin de copier ces éléments dans mon package (vous savez, celui que l’on a créé lors de la première étape). Oui, mais où ? En effet, un package R est un projet qui a une structure qui lui est propre. Et les templates R Markdown ont une place bien précise à respecter au sein de notre package : à la racine du projet de notre package myTemplates, on crée un dossier inst/rmarkdown/templates/. C’est ici que l’on va avoir les dossiers contenant nos templates. Pour celui que je viens de faire, je crée son propre dossier : raddict_html_template/skeleton/. C’est ici que je vais copier mon .Rmd ainsi que le dossier www/ qui contient mon css et mes images.

Lors de la création d’un nouveau Rmd via ce template, tous ces fichiers et dossiers inclus dans skeleton/ seront copiés dans le répertoire courant de l’utilisateur. Il faudra renommer votre Rmd en skeleton.Rmd.

Il ne manque plus qu’un élément, qui permettra cette copie : un fichier template.yaml, que l’on mettra dans le dossier raddict_html_template/. Son contenu indiquera le nom donné au template :

name: RAddict HTML Template
create_dir: true

On a donc, au final (oui, les chemins sont assez complexes) :

  • myTemplate/inst/rmarkdown/templates/raddict_html_template/template.yaml
  • myTemplate/inst/rmarkdown/templates/raddict_html_template/skeleton/skeleton.Rmd
  • myTemplate/inst/rmarkdown/templates/raddict_html_template/skeleton/www/

On build ensuite le package, et hop, la magie opère. Le template est maintenant utilisable : File > New File > R Markdown… > From Template et vos collègues n’auront plus qu’à installer votre package pour faire des super docs :

Les templates Shiny

Quand on souhaite créer son propre template d’applications Shiny, on parle de template de projet : un projet complet, avec une structure bien définie. Dans notre cas le projet doit contenir une application Shiny, mais en général, le template de projet peut avoir n’importe quelle structure.

Créer son template

La première étape, comme pour le cas des templates RMarkdown, c’est de créer son application template, dans un nouveau projet. Je crée par exemple une application Shiny avec {golem} (si vous n’êtes pas encore familiers avec ce package, je vous invite à regarder cette vidéo d’introduction à {golem} et à lire cette petite présentation).

J’ai créé la structure de mon UI, ajouté mon logo, mon CSS, …

Au niveau de l’organisation du code de mon projet, rien de nouveau pour le moment, on reste sur du classique :

Et j’ai simplement ajouté quelques modules :

Intégration du template Shiny dans le package

Bon, c’est là que les choses se compliquent un peu, mais on va y aller pas à pas. Comme pour les templates RMarkdown, les templates de projets doivent être dans un répertoire bien précis au sein de notre package. Reprenons notre package {myTemplates}.

Nous allons avoir besoin de plusieurs éléments :

  • les éléments qui constituent notre template : on va copier tout le contenu de notre projet golem, à l’exception du .Rproj et du .Rhistory dans myTemplates > inst > raddict_shiny_template :

  • une fonction R qui va créer le projet chez l’utilisateur : quand cette dernière sera appelée dans RStudio, elle recevra en paramètre path qui sera le chemin du nouveau projet, et copiera à cet endroit tous les éléments de notre template. Cette fonction est à sauvegarder dans le dossier R/ de notre package : myTemplates > R > create_raddict_app.R
#' Create a new package using RAddicts template
#'
#' @param path Name of the folder to create the package
#' @param check_name `logical`
#' @param open `logical`
#' @param package_name `character`
#'
#' @return path
#'
#' @importFrom cli col_green cat_rule
#' @importFrom utils menu getFromNamespace
#' @importFrom fs path_abs path_file path dir_copy path_expand dir_exists dir_create file_copy
#' @importFrom yaml read_yaml write_yaml
#' @importFrom rstudioapi isAvailable openProject
#' @export
#'
create_raddict_app <- function(
  # Chemin du nouveau projet
  path,
  # Le nom du projet doit-il repondre aux bonnes pratiques
  check_name = TRUE,
  # Ouvrir le projet a sa creation :
  open = TRUE,
  # Nom du package :
  package_name = basename(path)
) {
  path <- path_expand(path)
  if (path == "." & package_name == path_file(path)) {
    package_name <- path_file(getwd())
  }
  if (check_name) {
    cat_rule("Cheking package name")
    getFromNamespace("check_package_name", "usethis")(package_name)
    cat(col_green("Valid package name"))
  }
  if (dir.exists(path)) {
    cat("the file already exists, override ?")
    res <- menu(c("Yes", "No")) == 1
    if (!res) return(invisible(NULL))
  }
  cat_rule("Creating dir")
  dir_create(path, recurse = TRUE)
  cat(col_green("Created package directory"))
  cat_rule("Copying package skeleton")
  from <- system.file("raddict_shiny_template", package = "myTemplates")
  # copy whole directory :
  dir_copy(path = from, new_path = path, overwrite = TRUE)
  # copy logo & images :
  # to avoid problems with images copying the whole directory
  file_copy(system.file("raddict_shiny_template/inst/app/www/ricon.ico",
                        package = "myTemplates"),
            paste0(path, "/inst/app/www/ricon.ico"),
            overwrite = TRUE)
  file_copy(system.file("raddict_shiny_template/inst/app/www/LOGO-01.png",
                        package = "myTemplates"),
            paste0(path, "/inst/app/www/LOGO-01.png"),
            overwrite = TRUE)
  file_copy(system.file("raddict_shiny_template/inst/app/www/fond.jpg",
                        package = "myTemplates"),
            paste0(path, "/inst/app/www/fond.jpg"),
            overwrite = TRUE)
  # Listing copied files
  copied_files <- list.files(path = from, full.names = FALSE, all.files = TRUE,
                             recursive = TRUE)
  # replace package name in copied files
  for (f in copied_files) {
    copied_file <- file.path(path, f)
    try({
      replace_word(
        file = copied_file,
        pattern = "ShinyTemplate",
        replace = package_name)
    }, silent = TRUE)
  }
  cat(col_green("Copied app skeleton"))
  # golem configuration
  cat_rule("Setting default config")
  yml_path <- path(path, "inst/golem-config.yml")
  conf <- read_yaml(yml_path, eval.expr = TRUE)
  yaml_golem_wd <- "here::here()"
  attr(yaml_golem_wd, "tag") <- "!expr"
  conf$dev$golem_wd <- yaml_golem_wd
  conf$default$golem_name <- package_name
  conf$default$golem_version <- "0.0.0.9000"
  write_yaml(conf, yml_path)
  cat(col_green("Configured app"))
  if (open & rstudioapi::isAvailable()) {
    rstudioapi::openProject(path = path)
  }
  return(invisible(path_abs(path)))
}
replace_word <- function(file, pattern, replace) {
  suppressWarnings(tx <- readLines(file))
  tx2 <- gsub(pattern = pattern, replacement = replace, x = tx)
  writeLines(tx2, con = file)
}
  • Les métadonnées de notre template, qui doivent indiquer à RStudio comment utiliser la fonction ci-dessus pour créer un nouveau projet : il s’agit d’un .dcf que l’on sauvegardera dans le dossier inst/rstudio/templates/project/new_raddict_app.dcf :
Binding: create_raddict_app
Title: New RAddicts Shinyapp Package
OpenFiles: dev/01_start.R
Icon:
Parameter: check_name
Widget: CheckboxInput
Label: Perform a check on the name ?
Default: On
Position: right
  • le champ Binding indique à RStudio à quelle fonction R le modèle de projet est associé. Lorsqu’un utilisateur crée un nouveau projet en utilisant ce modèle, la fonction create_raddict_app() définie dans notre package sera utilisée pour générer le projet,
  • le champ Titre est le nom utilisé dans l’assistant RStudio de création de nouveau projet,
  • le champ OpenFiles indique à RStudio d’ouvrir un fichier appelé 01_start.R lorsque le projet est ouvert pour la première fois, après avoir créé le projet,
  • le champ Icon permet d’ajouter une icône à coté du nom du template projet dans l’assistant RStudio de création de nouveau projet. On ajoutera l’image associée dans le même dossier que new_raddict_app.dcf : inst/rstudio/templates/project/.

Les champs suivants permettent de définir les paramètres de la fonction, autres que path. Dans notre exemple : check_name.

On teste ?

Après avoir réalisé un check et un build de notre package myTemplates, on retrouve notre nouveau template d’application Shiny : File > New Project > New Directory

Un nouveau projet est créé et s’ouvre sur le fichier spécifié :

Conclusion

Ben… y’a plus qu’à ! Enjoy !


À propos de l'auteur


Commentaires


À lire également

Nos formations Certifiantes à R sont finançables à 100% via le CPF

Nos formations disponibles via moncompteformation.gouv.fr permettent de délivrer des Certificats reconnues par l’état, enregistrées au répertoire spécifique de France Compétences. 3 niveaux de certifications existent :

Contactez-nous pour en savoir plus.

Calendrier

08/11/2022

Formation : Analyse de données avec R

COMPLET

08/11/2022

Formation : Création de packages R

COMPLET

04/10/2022