Publipostage avec données de plusieurs tables

Contexte : un grist qui répertorie les inscriptions d’agent·es à des formations
Objectif : afficher dans une vue dynamique et imprimable l’historique des formations auxquelles a participé chaque agent·e

Contrairement à ce que nous avons fait dans cet exemple Générer des PDF personnalisés / Publipostage / Widget Markdown, nous allons dans ce tuto utiliser une seule formule, qui va à la fois récupérer nos données et construire notre vue html.

Structure du document

Le document Grist est composé de plusieurs tables :

  • une table Agent·e avec le nom de l’agent·e et son e-mail
  • une table Formations avec le nom de la formation, la date et la structure organisatrice (qui est une référence à une table « Organisateurs »)
  • une table Inscriptions qui contient une colonne agent·e, référence vers la table agent·e, une colonne formation, qui est une référence vers la table formation, et deux colonnes « Retenu·e » et « Présent·e » pour savoir si la personne a été acceptée sur la formation et si elle s’y est présentée.

Ajouter la vue Markdown

Pour afficher les données dans une vue personnalisée, nous allons :

  • créer, dans notre table Agent·e, une colonne nommée « Formule » de nature formule, qui contiendra les éléments à afficher dans la vue
  • ajouter à notre page une vue personnalisée Markdown (on pourrait aussi utiliser une vue personnalisée Html), basée sur la table Agent·e et en la liant bien à la vue de la table Agent·e. Le fait de lier les vues entre elles permettra que, lorsqu’on parcourt la table Agent·e , la vue Markdown soit mise à jour selon la ligne sélectionnée

Nouveau > Ajouter une vue à la page > Personnalisée > Markdown, avec « Sélectionner par » sur la vue de la table Agent·e

Afficher le nom, prénom et mail de l’agent·e

Nous allons commencer par quelque chose de simple : afficher dans le code HTML le nom de l’agent·e et son e-mail. C’est facile car ce sont des données qui sont accessibles directement depuis la table courante. Pour cela, il nous faut générer une chaîne de caractères (un texte) contenant ces deux informations.

On peut utiliser la formule : $Nom_Prenom + " " + $Mail

Voici le rendu :

Une autre manière d’écrire cette formule en python est la suivante :
f"{$Nom_Prenom} {$Mail}"

Le f devant une chaîne de caractères en python signifie que c’est une chaîne formatée et ce qu’il y a entre accolades sera interprété comme des variables. Par exemple {$Nom_Prenom} sera remplacé par la valeur de la variable $Nom_Prenom.

C’est un format plus concis qu’avec les +.

Dans cette chaîne de caractères que nous générons, nous pouvons ajouter des balises html pour mettre en forme notre vue. Par exemple <br> pour sauter une ligne, <b></b> pour du gras, <h1></h1> ou <h2> ou <h3> pour un titre etc.

Par exemple, mettons le nom/prénom de l’agent·e comme titre avec la formule :
f"<h2>{$Nom_Prenom}</h2> {$Mail}"

Voici le résultat :

Afficher l’historique des formations

Nous souhaitons maintenant afficher la liste des formations auxquelles chaque agent·e s’est inscrit·e. Nous allons pour cela utiliser la fonction lookupRecords qui va nous permettre de récupérer la liste des inscriptions pour chaque agent·e. On aurait envie d’écrire

inscriptions = Inscriptions.lookupRecords(Nom_agent=$Nom_Prenom)
Mais cette formule ne fonctionnera pas car Nom_agent de la table inscription est une référence alors que Nom_Prenom de la table des agents est un texte. Nous allons donc ajouter une colonne d’assistance dans la table d’inscription pour récupérer le texte brut du nom de l’agent·e

et pouvoir finalement appliquer le bon filtre sur notre lookupRecords :
inscriptions = Inscriptions.lookupRecords(Nom_agent_txt_brut=$Nom_Prenom)

Nous récupérons ainsi la liste d’inscriptions, assignée à notre variable inscriptions.

Nous allons maintenant parcourir chaque élément de cette liste, grâce à une boucle for, afin de créer notre chaîne de caractères contenant toutes les formations.

Par exemple, si on se limite pour l’instant à récupérer uniquement le nom de la formation, la formule sera la suivante :

# variable à laquelle est assigné notre texte
liste_formations = ""

# parcourt les inscriptions pour construire la chaîne de caractères
for i in inscriptions:
  # pour cette inscription i, la on assigne le nom de la formation  
  # récupéré dans la table Formation à la variable "formation"
  formation = f"{i.Formation.Nom}"
  # notre variable liste_formations est mise à jour avec la nouvelle valeur 
  liste_formations = liste_formations + formation

Enfin, à la sortie de la boucle, la formule va concaténer les informations sur l’agent·e avec la liste des formations.
return html_agent + liste_formations

La formule finale :

# html pour les informations sur l'agent·e
html_agent =  f"<h2>{$Nom_Prenom}</h2> {$Mail}" 

# récupère les inscriptions de l'agent·e
inscriptions = Inscriptions.lookupRecords(Nom_agent_txt_brut=$Nom_Prenom)

# variable qui va contenir notre texte
liste_formations = ""

# parcourt les inscriptions pour construire la chaîne de caractères
for i in inscriptions:
  formation = f"{i.Formation.Nom}"
  liste_formations = liste_formations + formation
  
#retourne le html agent + la liste des formations   
return html_agent + liste_formations

Cela donne :

Pour améliorer la présentation, nous allons ajouter des sauts de ligne <br> après le mail, et une liste à puces pour chaque ligne de formation <li></li>.

Cela donne :

Ajoutons maintenant d’autres informations sur les formations : la date et l’organisateur.

formation = f"<li>{i.Formation.Nom} - {i.Formation.Date} - Organisé par : {i.Formation.Organisateur.Nom} </li><br>"

Enfin, ajoutons les informations « Retenu·e » et « Présent·e » issues de la table Inscriptions. Comme ces colonnes sont des booléens, si nous affichons directement leurs valeurs on verra « True » et « False ». Nous allons donc utiliser un bout de code pour afficher plutôt « oui » ou « non » :

 retenu = "oui" if (i and i.Retenu) else "non"
  present = "oui" if (i and i.Present_e) else "non"

puis on crée notre ligne :
html_inscription = f"<span>Retenu : {retenu} – Présent·e : {present}</span></li>"

Attention, il faudra bien placer la fin de l’élément liste à puce </li> après les données d’inscription, au lieu de la placer après les données de formation.

La formule finale est :

# html pour les informations sur l'agent·e
html_agent = f"{$Nom_Prenom} - {$Mail} <br>" 

# récupère les inscriptions de l'agent
inscriptions = Inscriptions.lookupRecords(Nom_agent_txt_brut=$Nom_Prenom)

html_formations_inscriptions = ""

# parcourt les inscriptions pour construire le html
for i in inscriptions:
  html_formation = f"<li>{i.Formation.Nom} - {i.Formation.Date} <br> Organisée par : {i.Formation.Organisateur.Nom} <br>"
  
  retenu = "oui" if (i and i.Retenu) else "non"
  present = "oui" if (i and i.Present_e) else "non"
  html_inscription =  f"Retenu : {retenu} - Présent : {present}  </li> <br>" 
  
  html_formations_inscriptions = html_formations_inscriptions + html_formation + html_inscription
  
#retourne le html agent + la liste des inscriptions/formations   
return html_agent + html_formations_inscriptions

Affiner le style

Pour avoir une jolie vue avec des marges et un style agréable, nous pouvons utiliser la propriété style dans les balises html. Un exemple de style peut être :

Obtenu avec le code suivant :

# HTML pour les infos de l'agent·e avec style
html_agent = f"""<div style='border: 1px solid #ccc; padding: 10px; border-radius: 8px; margin-bottom: 15px;'>
  <strong style='font-size: 1.2em;'>{$Nom_Prenom}</strong><br>
  <a href='mailto:$Mail' style='color: #1a73e8;'>{$Mail}</a>
</div>
"""

# Récupérer les inscriptions pour l'agent·e
inscriptions = Inscriptions.lookupRecords(Nom_agent_txt_brut=$Nom_Prenom)

html_formations_inscriptions = "<ul style='list-style-type: disc; padding-left: 20px;'>"
# Parcourir la liste des inscriptions
for i in inscriptions:
  html_formation = f"""<li style='margin-bottom: 10px;'>
      <strong>{i.Formation.Nom}</strong> – {i.Formation.Date}<br>
      <em>Organisée par :</em> {i.Formation.Organisateur.Nom}<br>
  """
  retenu = "oui" if (i and i.Retenu) else "non"
  present = "oui" if (i and i.Present_e) else "non"
  html_inscription = f"<span>Retenu : {retenu} – Présent·e : {present}</span></li>"
  html_formations_inscriptions += html_formation + html_inscription
# Fin de la liste
html_formations_inscriptions += "</ul>"
# Retourne le HTML complet
return html_agent + html_formations_inscriptions

Imprimer la vue

Pour imprimer, cliquer sur les points en haut à droite de la vue, puis « Imprimer la vue ».

Document de démo

1 « J'aime »

Bonjour, très intéressant merci!
J’ai pu grandement m’en inspirer. Par contre, la date ressort au format Y-m-d au lieu de d/m/Y, comment peut on faire?
Merci pour votre aide

1 « J'aime »

Bonjour,
Désolée du délai de réponse ! Pour la date au format fr

i.Formation.Date.strftime(format = '%d-%m-%Y')

J’ai modifié les formules dans le modèle, merci !