HTTP API Python su SAP BTP Cloud Foundry - decodifica barcode su PDF

Stasera voglio raccontarvi come è possibile creare una applicazione Python che offre delle API HTTP, e come eseguire questa applicazione all'interno dell'ambiente Cloud Foundry (CF) contenuto nella Business Technology Platform (BTP) di SAP.

Per i non iniziati, la BTP è un prodotto commerciale di SAP che offre una miriade di servizi cloud based per il mondo business, e la Cloud Foundry è un prodotto open source in grado di gestire in modo completo il ciclo di vita di una applicazione. In questo caso ci concentreremo sulla CF offerta all'interno della BTP.

Come applicazione utilizzeremo una variante di quella che ho descritto in un precedente post, in grado di analizzare un PDF e restituire il contenuto di tutti i barcode individuati nella prima pagina dello stesso.


Fase 1: procurarsi un account BTP trial

Per prima cosa occorre un account SAP, che può essere creato gratuitamente. Una volta ottenuto l'account è possibile creare una istanza trial della BTP, gratuita, che fornisce una miriade di servizi seppure con limitazioni. Per creare un trial gratuito è necessario registrarsi su sap.com,  e poi andare all'indirizzo https://account.hanatrial.ondemand.com/trial/#/home/trial. Durante la creazione della trial è necessario registrare la propria email e il proprio numero di cellulare, per ricevere un codice di attivazione. A questo punto si preme il pulsante "Go to my trial" e siamo nella nostra istanza della BTP.

Nella BTP trial è preconfigurato un subaccount "trial", cui possiamo accedere cliccandovi sopra.

Nel subaccount, tra le altre cose, è già attivo un ambiente Cloud Foundry. Prendiamo nota dell'api endpoint, ci servirà tra poco.

Come ultimo passo dobbiamo installare sul nostro PC i tool da riga di comando per lavorare con Cloud Foudry. Li possiamo scaricare da qui.


Fase 2: scrivere il programma Python

Il programma, comprensivo di file di appoggio e PDF di test con i barcode, è disponibile sul mio GitHub.

Il programma (barcode.py) è simile a quello descritto nello scorso post, con la sola differenza che abbiamo aggiunto Flask. Flask è una libreria Python che permette di creare endpoint HTTP. Nel nostro programma abbiamo un endpoint root (/), che restituisce un simpatico "Hello world!" utile per fare test, e un endpoint "barcode" di tipo POST che accetta un file PDF e restituisce un oggetto JSON contenente il valore dei barcode interpretati.

import os
from flask import Flask, request
from pyzbar import pyzbar
import pdf2image
import json

app = Flask(__name__)

# Se in CF, il numero porta è fornito dall'ambiente
# Per i test in locale usiamo la 3000
port = int(os.environ.get('PORT', 3000))

@app.route('/')
def hello():
    return "Hello World!"

@app.route("/barcode", methods=['POST'])
def barcode():
	images = pdf2image.convert_from_bytes(
		request.get_data(),
		dpi=400,
		first_page=1,
		last_page=1,
		grayscale=True
	)
    
	decoded = pyzbar.decode(images[0])
	
	ret = []
	for item in decoded:
		ret.append({
			"value": item.data.decode("utf-8")
		})

	return json.dumps({"values": ret})

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=port)

Prepariamo un altro file e lo chiamiamo environment.yaml. Questo file contiene le dipendenze python che verranno installate nel nostro deployment.

Nota: anzichè usare il package manager di default di python abbiamo usato "conda-forge", poichè solo quest'ultimo è in grado di fornirci "poppler", la libreria binaria utilizzata da pdf2image per la conversione del PDF in immagini. Nella scorsa puntata avevamo scaricato il binario e l'avevamo messo in una cartella sul nostro pc, questa strada non è ovviamente percorribile qui.

name: barcode

channels:
- conda-forge

dependencies:
- python=3.10
- pip
- flask
- poppler
- pdf2image
- pyzbar

Prepariamo infine un terzo file, denominato manifest.yaml. In questo file andiamo a definire le caratteristiche della nostra applicazione, tra cui il comando di avvio (python barcode.py) e le quote di ram e disco da mettere a disposizione della nostra istanza.

---
applications:
- name: barcode
  path: ./
  memory: 3.5G
  disk_quota: 2G
  buildpack: python_buildpack
  command: python barcode.py

Nota: il package manager conda-forge è ESTREMAMENTE avido di risorse in fase di installazione. Ha davvero bisogno di 3/4 GB di RAM, come indicato nel manifest. Nel momento in cui l'applicazione è installata possiamo andare sulla CF e cambiare manualmente la disponibilità (a regime non ha bisogno di più di 100 MB di RAM), ma per l'installazione ci serve tutta. E poichè la RAM dell'istanza trial è limitata a 4GB, significa che per caricare una nuova applicazione dobbiamo stoppare tutte le istanze eventualmente esistenti.


Fase 3: caricare l'applicazione su CF

Per caricare l'applicazione utilizzeremo il tool da riga di comando "cf" (installato come da istruzioni sopra).

Per prima cosa dobbiamo impostare l'endpoint delle API CF da usare (naturalmente dobbiamo rimpiazzare il valore nell'esempio sotto con quanto letto nella schermata del subaccount):

cf api https://api.cf.eu20.hana.ondemand.com

Poi effettuiamo il login (verranno chieste le credenziali dell'utente SAP):

cf login

E infine carichiamo l'applicazione:

cf push

Ci vorrà qualche minuto, e spesso sembrerà impallato; attendiamo con fiducia il termine. Se tutto va per il meglio dovremmo vedere il log del caricamento, dell'installazione e dell'avvio dell'applicazione sulla CF.

Il percorso del nostro servizio è quello indicato alla voce "routes", prendiamone nota.

Nell'interfaccia della BTP, dentro il subaccount, nel menu di sinistra clicchiamo su "Cloud Foundry", "Spaces"...

..e poi sull'unico space presente:

Qui dovremmo vedere la nostra applicazione in esecuzione:


Fase 4: testare l'applicazione

Per testare l'applicazione ci serve un qualunque client http (possiamo usare Postman o qualcosa del genere). Nel mio caso userò l'estensione "REST client" per VSCode. Una volta installata, è sufficiente creare un file con estensione "http" e scrivere come segue:

POST https://XXXXXXXXXXXXXXX.cfapps.us10-001.hana.ondemand.com/barcode

< r44-2686a.pdf

Ovviamente bisogna impostare la route corretta (come vista al termine del deployment) e usare un file PDF presente nella cartella di lavoro e che abbia un barcode nella prima pagina.

Se l'estensione VSCode è installata correttamente, sopra l'url apparirà il comando "send request".

Lo clicchiamo e voilà!

Funziona!