HR-OPS: Automatiser la création de documents administratifs avec zio-notion

Introduction

Quand l’informatique est perçue comme une source de coûts, on peut être tenté d’utiliser des outils de no-code pour répondre à ses problèmes.

Et le no-code, ça marche bien jusqu’à ce que ça marche plus. La question de savoir ce que l’on veut faire est progressivement remplacée par “qu’est-ce que l’on peut faire” ?

Certaines tâches administratives et de gestion coûtent du temps, sont répétitives, sont difficilement automatisable par des solutions de no-code, en particulier quand il s’agit de faire des transformations avec un contexte entier qui ne se gère pas avec du 1 pour 1 (ex. N notes de frais, M personnes ⇒ M piéces pour la comptabilité, avec une synthèse et une concatenation des éléments des notes frais)

Dans cet article, nous allons voir comment faire un générateur de PDF qui source ces données depuis Notion.

Étude de cas

Imaginons plusieurs cas d’usage :

  • Notes de frais : Vous devez rassembler les notes de frais de vos équipiers dans un simple document, par équipier, pour l’envoyer à la comptabilité tous les mois.
  • Dossiers locatifs : si vous cherchez un endroit où habiter/travailler, il est possible que vous ayez à envoyer plusieurs dossiers. Certaines pièces changent régulièrement (3 derniers bulletins de salaire, justificatif de domicile récent, etc.) le reste de pièces est stable dans le temps.
  • Proposition commerciale pour enseigner dans une école : le programme change, le CV des formateurs évoluent moins vite.

Toutes ces pièces jointes sont soigneusement rangées dans votre base documentaire

Figure 1 - Base de données Notion regroupant des fichiers PDF ainsi que leurs informations contextuelles

Vous aimeriez pouvoir fusionner les PDF pour constituer un document à partir de votre page Notion, tout en gardant des informations sur le contenu de votre page, et en annotant les pièces avec des éléments supplémentaires (en tête, pagination, etc.).

Prérequis

  • Vous faites du Scala ;
  • Vous utilisez les effets ZIO ;

Zio-notion

zio-notion est une bibliothèque logicielle qui permet d’interagir avec Notion.

zio-notion est livré avec en ensemble de features pratiques telles que :

Un DSL pour écrire des filtres ou manipuler des colonnes :

Figure 2 - Exemple de filtre sur l’interface Notion

Et son équivalent avec zio-notion :

val filters: Filter =
    $"Tag".asSelect equals "filter-1" and
      $"Author".asPeople.contains("Bastien Guihard")

Un mécanisme de déstructuration :

Vous pouvez choisir de déstructurer une page et la représenter par une case class directement.

Soit une case class DatabaseEntry :

final case class DatabaseEntry(
      @NotionColumn("Name")   name: String,
      @NotionColumn("Author") author: String
)

Déstructuration de la Page en DatabaseEntry :

for {
      database   <- Notion.queryAllDatabase("ID-DB-FIGURE-1", filter)
      maybeEntry <- database.results.headOption.map(_.propertiesAs[DatabaseEntry].toZIO)
			_          <- maybeEntry match {
		     case Some(entry) => Console.printLine(entry.name)
         case None.       => Console.printLine("There is no database entry.") 
	    } 
} yield()

Vous pouvez retrouver la documentation ici : https://univalence.github.io/zio-notion/

Fusionneur de PDF “maison”

Revenons à notre projet initial

Notre projet doit être capable

  • D’interagir avec Notion ;
  • De télécharger les PDF à fusionner ;
  • De créer un PDF contenant une page récapitulative et les PDF fusionnés ;
Figure 3 - Architecture de notre fusionneur de PDF fait maison

Le projet de cet article est disponible ici : https://github.com/univalence/hr-ops

On va se pencher sur la logique de l’application :


/**
 * DocumentContext représente un document PDF une fois telechargé augmenté de 
 * son nom, son nombre de pages et les informations de la base de donnée
 */
final case class DocumentContext(name: String, bytes: Array[Byte], numberOfPage: Int, databaseRow: DatabaseEntry)

...

/**
 * Tout d'abord on souhaite fusionner les PDF dont la valeur de Tag
 * est égale à "filter-1"
 */
val filter: Filter = $"Tag".asSelect.equals("filter-1")

...

def program: ZIO[Downloader with Notion, Throwable, Unit] =
    for {
			/**
			 * `queryAllDatabase` nous permet de passer en revue l'intégralité de la base de données
			 * 
			 * En effet, zio-notion est livré avec une gestion de la pagination des bases de données, 
			 * vous pouvez aisément parcourir votre base de donnée de manière séquentielle
			 * https://univalence.github.io/zio-notion/pagination
			 */
      database <- Notion.queryAllDatabase(Constants.dbId, filter)
			// on destructure toutes les entrées qui nous interesse
      destructureEffects = database.results.map(_.propertiesAs[DatabaseEntry].toZIO)
      entries <- ZIO.collectAllPar(destructureEffects)
      // On construit les effets qui représentent le téléchargement des documents
			contextualizeEffects = entries.map(_.contextualize)
      documentContexts <- ZIO.collectAllPar(contextualizeEffects)
      flattenDocumentContexts = documentContexts.flatten
			// La création d'un encodeur déclaratif rend la construction d'un PDF évidente 
      documentDescription =
        CreateNewPDF >>
          AddTable(flattenDocumentContexts) >> AddPDFs(flattenDocumentContexts) >> Paginate >> SavePDF("concat")
			/**
			 * On interprete cette description, en l'occurence via PDF BOX
			 * 
			 * Pour changer de technologie il nous suffit de changer d'interpreteur, 
			 * la description reste la même
			 */
      _ <- documentDescription.interpret.orDie
    } yield ()

Conclusion

En deux coups de cuillère à pot, vous pouvez vous affranchir des limites du no code.

La stack de ce projet (Scala + Notion + zio-notion) nous donne un ensemble de garanties :

  • Vous conservez la modularité de Notion pour organiser votre contenu ;
  • Zio-notion vous assure d’interagir avec notion d’une manière pratique, idiomatique de Scala ;

Les solutions no-code restent des solutions viables dans pas mal de cas, mais il est intéressant de prendre en compte dans votre choix d’outil que vous ne serez pas coupé dans votre élan.

La garantie de pouvoir interagir avec un outil no-code/low-code par le biais d’un SDK ou d’une API publique de qualité est un élément essentiel à prendre en compte dans son choix d’outillage.