skapa PDF-filer från mallar med Python-och Google-skript

ofta är det användbart att skapa PDF-filer från dina Python-skript. Oavsett om du skapar fakturor, brev, rapporter eller andra dokument som innehåller mycket formateringsrepetition men bara Lite dynamiskt innehåll, kan du lägga till lite automatisering spara många timmar.

du har några alternativ för detta. De vanliga är:

  1. använd ett PDF-bibliotek som reportlab för att generera PDF-filer direkt (t. ex. https://www.blog.pythonlibrary.org/2010/03/08/a-simple-step-by-step-reportlab-tutorial/)
  2. Använd ett HTML-mallbibliotek som Jinja2 och konvertera från HTML till PDF (t. ex. se http://pbpython.com/pdf-reports.html)
  3. Använd ett API från tredje part som https://pdfgeneratorapi.com/.

för alternativ 1 kan generering av PDF-filer direkt från Python göra formateringen mycket svår. Du måste rita allt du behöver element för element, med kod, och även när du har en mall som ser ut som du vill ha det är det svårt att underhålla.

alternativ 2 kan ofta fungera bättre, men du måste fortfarande bygga Jinja HTML-boilerplate, och ibland kommer HTML till PDF-konverteringen inte ut som du förväntade dig.

alternativ 3 kräver att du bygger mallen först med hjälp av en onlinetjänsts webbgränssnitt. Även om du får ett dra-och-släpp-gränssnitt är det ganska klumpigt och svårt att få din mall att se ut som du vill. Vanligtvis måste du också betala för att använda tjänsten.

medan ett av alternativen ovan kan fungera för dig, om du inte gillar någon av dem, kan du också hacka ihop ett dokumentgenererings-API baserat på Google Drive. Du får ett gratis API och du kan använda Google Docs som ditt mallverktyg, vilket är ganska kraftfullt och har många befintliga mallar för saker som fakturor, brev och CV.

jag började med en fakturamall som jag hittade online. Det ser ut så här:

Google Docs fakturamall

i den här handledningen går vi igenom att skapa ett API som genererar dessa fakturor och låter dig programmatiskt infoga fakturanumret från ett externt Python-skript. I verkligheten skulle du behöva göra detsamma för många andra fält, men vi börjar med ett enkelt exempel av demonstrationsskäl.

vi skriver några rader med Google App Script-kod och några rader med Python-kod.

skapa ett malldokument

använd en av de inbyggda Google-dokumentmallarna, sök online efter en som matchar dina behov eller bygg din egen Över på docs.google.com. (du behöver ett Google-konto).

Lägg till platshållare där du behöver dynamisk information. I exemplet nedan har jag lagt till faktura nr {invoice_id} i stället för” 456 ” id som jag hade på originaldokumentet. Det finns inget speciellt med den här syntaxen-vi använder en grundläggande Sök-och ersättningsfunktion senare för att byta ut det här för den verkliga informationen, så använd något som sannolikt inte kommer att visas i slutdokumentet.

notera ditt dokument-id, vilket är den markerade delen i URL-fältet.

fakturamall med platshållare

ställa in ett anpassat Google-skript

gå till Google Drive, tryck på ”ny” i det övre vänstra hörnet. Under” mer ”väljer du” Google Apps Script ”om det är tillgängligt eller” anslut fler appar ” om du inte ser det.

 Ansluta fler appar

Sök efter ”apps script” och välj att ansluta det. Du kan se några varningsmeddelanden som frågar om du litar på dig själv. Säg att du gör det.

lägga till Apps Script

när du kan skapa en ny App Script, ser du en standard tomt skript som ser ut som följer.

tomt Google Apps-skript

ta bort koden som du ser där och ersätt den med en createDocument – funktion som ser ut som följer.

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";}

på rad 2, Byt ut TEMPLATE_ID med det dokument-ID som du kopierade från URL-fältet på din mallade Google Doc.

den här koden hittar det mallade dokumentet, skapar en kopia av det och ställer in filnamnet till ”Faktura” plus vad invoice_id vi skickar in. Den öppnar sedan den nya filen via DocumentApp (istället för Drive-appen, så att vi faktiskt kan få innehållet i filen och redigera dem). Den söker i dokumentet efter platshållaren vi lagt till ({invoice_id}) och ersätter den med den faktiska invoice_id som funktionen tar som inmatning. Det ställer sedan in att dokumentet ska vara offentligt tillgängligt och returnerar en URL som går direkt till en PDF-export för det dokumentet.

under denna funktion lägger du till en annan som heter doGet. Medan den tidigare funktionen kan namnges vad som helst, är doGet en speciell funktion i Google Apps-skript, så du måste namnge den exakt doGet. Den här funktionen hanterar inkommande webbförfrågningar efter att vi har distribuerat vår app.

koden för funktionen doGet är som följer. Klistra in detta under föregående createDocument() – funktion.

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

detta tar in invoice_id som en URL-parameter, skickar detta vidare till vår createDocument – funktion som vi just skrev och returnerar webbadressen till det skapade dokumentet som vanlig text.

publicera vårt API

från menyn ”publicera” väljer du ”distribuera som webbapp”

distribuera som webbapp

du blir ombedd att namnge projektet. Ge det ett namn som ”PDF API” eller något annat du vill ha.

 namnge projektet

du ser en ny meny för att ställa in alternativen för att distribuera din webbapp.

 Deploy options

Lägg till ett meddelande som ”initial deploy” under där det står ”New” och välj ”någon, även anonym” från åtkomstinställningarna. Lämna Exekveringsinställningarna som”jag”.

varning: om du delar länken på en offentlig plats kan människor missbruka tjänsten och skräpposta den med automatiska förfrågningar. Google kan låsa ditt konto för missbruk om detta händer, så håll länken säker.

tryck på Deploy-knappen och anteckna webbadressen som du ser på nästa popup.

 din app URL

Lägg till”?invoice_id=1 ” till slutet av webbadressen och besöka den i din webbläsare. Det ska se ut som

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

om allt gick bra bör du se en Google Docs-länk visas.

 svar från vår webbapplikation

om du besöker webbadressen ska en PDF av fakturan med platshållaren avstängd med 1 laddas ner.

uppdatera applikationen

om du ser ett fel istället eller inte får svar gjorde du förmodligen ett misstag i koden. Du kan ändra den och uppdatera distributionen på samma sätt som du ursprungligen distribuerade den. Uppdateringsskärmen skiljer sig bara något från deploy-skärmen.

 uppdatera distributionsalternativ

det enda knepiga är att du måste välja ”ny” som version för varje ändring du gör. Om du gör ändringar i koden och uppdaterar en tidigare version kommer ändringarna inte att träda i kraft, vilket inte är uppenbart från användargränssnittet. (Du kan se att det tog mig några försök att få det rätt.).

skapa våra fakturor från Python

vi kan nu skapa fakturor och spara dem lokalt från ett Python-skript. Följande kod visar hur man genererar tre fakturor i en for – slinga.

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)

Observera att skapandet och nedladdningsprocessen är ganska långsam, så det tar några sekunder för varje faktura du skapar.

du har säkert märkt att det här är en ganska ”hacky” lösning för att generera PDF-filer inifrån Python. Funktionen ”ersätt” är ganska begränsad jämfört med ett korrekt mallspråk, och att skicka data genom en get-begäran har också begränsningar. Om du passerar något mer komplicerat än en faktura-ID, kommer du att URL koda data först. Du kan göra detta i Python med modulen urllib.parse. Ett exempel på modifiering av Python-skriptet för att hantera mer komplicerade data är följande.

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)

men det finns fortfarande begränsningar för vilken typ av data och hur mycket du kan skicka bara med hjälp av webbadresser, så du skulle behöva ändra skriptet för att använda postförfrågningar istället om du skickade mycket dynamisk data.

det är också ganska långsamt jämfört med några av de andra metoderna vi diskuterade i början, och Google har vissa begränsningar för hur många filer du kan skapa automatiskt på detta sätt.

som sagt, att kunna generera mallar med Google Dokument kan vara snabbt och kraftfullt, så du måste bedöma avvägningarna själv.

Observera också att detta är ett ganska konstruerat exempel, där vi kunde ha kört Python-skriptet från Googles ekosystem och undvikit att behöva skapa ett offentligt API som potentiellt kan missbrukas om andra upptäckte webbadressen. Du kan dock ha en befintlig Python-applikation, inte värd på Google, som du behöver ansluta till automatiskt genererade PDF-filer, och den här metoden låter dig fortfarande skapa en fristående ”mikroservice” i Googles ekosystem som möjliggör enkel PDF-generering.

slutsats