Créer des fichiers PDF à partir de modèles avec des scripts Python et Google

Il est souvent utile de créer des fichiers PDF à partir de vos scripts Python. Que vous créiez des factures, des lettres, des rapports ou tout autre document contenant beaucoup de répétitions de formatage mais seulement un peu de contenu dynamique, l’ajout d’une certaine automatisation peut vous faire économiser de nombreuses heures.

Vous avez quelques options pour cela. Les habituels sont:

  1. Utilisez une bibliothèque PDF comme reportlab pour générer directement des fichiers PDF (par ex. https://www.blog.pythonlibrary.org/2010/03/08/a-simple-step-by-step-reportlab-tutorial/)
  2. Utilisez une bibliothèque de modèles HTML comme Jinja2 et convertissez-la de HTML en PDF (par exemple, voir http://pbpython.com/pdf-reports.html)
  3. Utilisez une API tierce comme https://pdfgeneratorapi.com/.

Pour l’option 1, la génération de PDF directement à partir de Python peut rendre le formatage très difficile. Vous devez dessiner tout ce dont vous avez besoin élément par élément, en utilisant du code, et même une fois que vous avez un modèle comme vous le souhaitez, il est difficile à maintenir.

L’option 2 peut souvent fonctionner mieux, mais vous devez toujours créer le standard HTML Jinja, et parfois la conversion HTML en PDF ne se déroule pas tout à fait comme prévu.

L’option 3 nécessite que vous créiez d’abord le modèle à l’aide de l’interface Web d’un service en ligne. Bien que vous obteniez une interface de glisser-déposer, il est assez maladroit et difficile de faire paraître votre modèle comme vous le souhaitez. Habituellement, vous devez également payer pour utiliser le service.

Bien que l’une des options ci-dessus puisse vous convenir, si vous n’en aimez aucune, vous pouvez également pirater une API de génération de documents basée sur Google Drive. Vous obtiendrez une API gratuite et vous pourrez utiliser Google Docs comme outil de création de modèles, qui est assez puissant et contient de nombreux modèles préexistants pour des éléments tels que les factures, les lettres et les CV.

J’ai commencé avec un modèle de facture que j’ai trouvé en ligne. Cela ressemble à ceci:

 Modèle de facture Google Docs

Dans ce tutoriel, nous allons créer une API qui génère ces factures et vous permet d’insérer par programme le numéro de facture à partir d’un script Python externe. En réalité, vous devrez faire la même chose pour beaucoup d’autres domaines, mais nous commencerons par un exemple simple pour des raisons de démonstration.

Nous allons écrire quelques lignes de code de script d’application Google et quelques lignes de code Python.

Création d’un modèle de document

Utilisez l’un des modèles de document Google intégrés, recherchez en ligne celui qui correspond à vos besoins ou créez le vôtre sur docs.google.com . (Vous aurez besoin d’un compte Google).

Ajoutez des espaces réservés où vous avez besoin d’informations dynamiques. Dans l’exemple ci-dessous, j’ai ajouté la FACTURE NO {invoice_id} à la place de l’identifiant « 456 » que j’avais sur le document d’origine. Il n’y a rien de spécial dans cette syntaxe – nous utiliserons une fonction de recherche et de remplacement de base plus tard pour échanger cela contre les informations réelles, alors utilisez quelque chose qui a peu de chances d’apparaître dans le document final.

Prenez note de l’ID de votre document, qui est la partie en surbrillance dans la barre d’URL.

 Modèle de facture avec espace réservé

Configuration d’un script Google personnalisé

Accédez à Google Drive, appuyez sur « Nouveau » dans le coin supérieur gauche. Sous « Plus », sélectionnez « Script Google Apps » s’il est disponible ou « Connecter plus d’applications » si vous ne le voyez pas.

 Connexion d'autres applications

Recherchez « script d’applications » et choisissez de le connecter. Vous pourriez voir des messages d’avertissement vous demandant si vous vous faites confiance. Dis que oui.

 Ajout d'un script d'applications

Une fois que vous pouvez créer un nouveau script d’application, vous verrez un script vide par défaut qui se présente comme suit.

 Script Google Apps vide

Supprimez le code que vous y voyez et remplacez-le par une fonction createDocument qui se présente comme suit.

function createDocument(invoice_id) { var TEMPLATE_ID = '1Ybq8r_SiWu4Z4-_Z6S0IW1L8FJywfpjPAATPCvvkKk8'; var documentId = DriveApp.getFileById(TEMPLATE_ID).makeCopy().getId(); drivedoc = DriveApp.getFileById(documentId); drivedoc.setName("Invoice " + invoice_id); doc = DocumentApp.openById(documentId); var body = doc.getBody(); body.replaceText('{invoice_id}', invoice_id); drivedoc.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.EDIT); return "https://docs.google.com/document/d/" + documentId + "/export?format=pdf";}

À la ligne 2, désactivez le TEMPLATE_ID avec l’ID de document que vous avez copié à partir de la barre d’URL de votre document Google template.

Ce code trouve le document modélisé, en crée une copie et définit le nom du fichier sur « Facture » plus tout ce que invoice_id nous transmettons. Il ouvre ensuite le nouveau fichier via DocumentApp (au lieu de l’application Drive, afin que nous puissions réellement obtenir le contenu du fichier et les modifier). Il recherche dans le document l’espace réservé que nous avons ajouté ({invoice_id}) et le remplace par le invoice_id réel que la fonction prend en entrée. Il définit ensuite le document pour qu’il soit accessible au public et renvoie une URL qui ira directement à une exportation PDF pour ce document.

Sous cette fonction, ajoutez une autre appelée doGet. Alors que la fonction précédente peut être nommée n’importe quoi, doGet est une fonction spéciale dans les scripts Google Apps, vous devrez donc la nommer exactement doGet. Cette fonction traitera les demandes Web entrantes une fois que nous aurons déployé notre application.

Le code de la fonction doGet est le suivant. Collez ceci sous la fonction createDocument() précédente.

function doGet(e) { var invoice_id = e.parameter.invoice_id; var url = createDocument(invoice_id); return ContentService.createTextOutput(url);}

Cela prend l’invoice_id comme paramètre URL, le transmet à notre fonction createDocument que nous venons d’écrire et renvoie l’URL du document créé en texte brut.

Publication de notre API

Dans le menu « Publier », sélectionnez « Déployer en tant qu’application web »

 Déployez en tant qu'application Web

Il vous sera demandé de nommer le projet. Donnez-lui un nom comme « API PDF » ou tout ce que vous voulez.

 Nommer le projet

Vous verrez un nouveau menu pour définir les options de déploiement de votre application Web.

 Options de déploiement

Ajoutez un message du type « déploiement initial » sous lequel il est dit « Nouveau » et choisissez « N’importe qui, même anonyme » dans les paramètres d’accès. Laissez les paramètres d’exécution comme « Moi ».

Avertissement: Si vous partagez le lien dans un lieu public, des personnes peuvent abuser du service et le spammer avec des demandes automatiques. Google peut verrouiller votre compte en cas d’abus si cela se produit, alors gardez le lien en sécurité.

Appuyez sur le bouton Déployer et notez l’URL que vous voyez dans la fenêtre contextuelle suivante.

 URL de votre application

Ajouter « ?invoice_id=1 » à la fin de l’URL et visitez-la dans votre navigateur. Cela devrait ressembler à quelque chose comme

https://script.google.com/macros/s/AKfycbxDiKpTGqMijZmU8-0cPj06DBFjDOPYZJ9IFvhcO111GCh8jqxC/exec?invoice_id=1

Si tout s’est bien passé, vous devriez voir un lien Google Docs s’afficher.

 Réponse de notre application web

Si vous visitez l’URL, un PDF de la facture avec l’espace réservé désactivé par 1 doit être téléchargé.

Mise à jour de l’application

Si vous voyez une erreur à la place ou si vous n’obtenez pas de réponse, vous avez probablement fait une erreur dans le code. Vous pouvez le modifier et mettre à jour le déploiement de la même manière que vous l’avez initialement déployé. L’écran de mise à jour n’est que légèrement différent de l’écran de déploiement.

 Options de déploiement de mise à jour

La seule chose délicate est que vous devez sélectionner « Nouveau » comme version pour chaque modification que vous apportez. Si vous apportez des modifications au code et mettez à jour une version précédente, les modifications ne prendront pas effet, ce qui n’est pas évident dans l’interface utilisateur. (Vous pouvez voir qu’il m’a fallu quelques essais pour bien faire les choses.).

Création de nos factures à partir de Python

Nous pouvons maintenant créer des factures et les enregistrer localement à partir d’un script Python. Le code suivant montre comment générer trois factures dans une boucle for.

import requestsurl = "https://script.google.com/macros/s/AKfycbyYL5jhEstkuzZAmZjo0dUIyAmzUc1XL5B-01fHRHx8h63cieXc/exec?invoice_id={}"invoice_ids = for invoice_id in invoice_ids: print("processing ", invoice_id) response = requests.get(url.format(invoice_id)) print("file generated") response = requests.get(response.content) print("file downloaded") with open("invoice{}.pdf".format(invoice_id), "wb") as f: f.write(response.content)

Notez que le processus de création et de téléchargement est assez lent, il faudra donc quelques secondes pour chaque facture que vous créez.

Vous avez probablement remarqué qu’il s’agit d’une solution assez « hacky » pour générer des fichiers PDF à partir de Python. La fonctionnalité « remplacer » est assez limitée par rapport à un langage de modèle approprié, et le passage de données via une requête get a également des limites. Si vous passez par quelque chose de plus compliqué qu’un identifiant de facture, vous devrez d’abord encoder les données par URL. Vous pouvez le faire en Python en utilisant le module urllib.parse. Un exemple de modification du script Python pour traiter des données plus compliquées est le suivant.

import requestsimport urllib.parseurl = "https://script.google.com/macros/s/AKfycbyYL5jhEstkuzZAmZjo0dUIyAmzUc1XL5B-01fHRHx8h63cieXc/exec?"invoice_ids = for invoice_id in invoice_ids: print("processing ", invoice_id) payload = {"invoice_id": invoice_id} u = url + urllib.parse.urlencode(payload) response = requests.get(u) print("file generated") response = requests.get(response.content) print(response.content) print("file downloaded") with open("invoice{}.pdf".format(invoice_id), "wb") as f: f.write(response.content)

Mais il y a encore des limites quant au type de données et à la quantité que vous pouvez transmettre simplement en utilisant des URL, vous devrez donc modifier le script pour utiliser les requêtes POST à la place si vous envoyez beaucoup de données dynamiques.

C’est également assez lent par rapport à certaines des autres méthodes que nous avons discutées au début, et Google a certaines limites sur le nombre de fichiers que vous pouvez créer automatiquement de cette manière.

Cela dit, pouvoir générer des modèles à l’aide de Google Docs peut être rapide et puissant, vous devrez donc évaluer les compromis par vous-même.

Notez également qu’il s’agit d’un exemple assez artificiel, où nous aurions pu exécuter le script Python à partir de l’écosystème Google et éviter d’avoir à configurer une API publique qui pourrait potentiellement être abusée si d’autres personnes découvraient l’URL. Cependant, vous pouvez avoir une application Python existante, non hébergée sur Google, que vous devez connecter avec des fichiers PDF générés automatiquement, et cette méthode vous permet toujours de configurer un « microservice » autonome au sein de l’écosystème Google qui permet de générer facilement des PDF.

Conclusion