Πρόσφατα αντιμετωπίσαμε το πρόβλημα να εμφανίσουμε ένα σχήμα γραφήματος ποιότητας σε μια εφαρμογή ή ιστότοπο. Αλλά επιπλέον θέλετε να μπορείτε να στέλνετε συμβάντα ενημέρωσης από το back-end της python και να έχετε όλες τις ωραίες αλληλεπιδράσεις όπως το πάτημα κουμπιού και τα συμβάντα εισαγωγής κειμένου που έρχονται με το Angular, σωστά;
Ένα στοιχείο γραφήματος Bokeh μπορεί να μην είναι πάντα η βέλτιστη λύση, αλλά παρόλο που - θα θέλαμε να μοιραστούμε μαζί σας σε αυτό το αποθετήριο GitHub - αυτό που πιστεύουμε είναι ένα ωραίο, ελάχιστο παράδειγμα και ένας επίδειξης του τρόπου ενσωμάτωσης ενός backend python σε μια γωνιακή εφαρμογή.
Το σημείο εκκίνησης
είναι το BokehJS lib το οποίο ενσωματώνουμε σε ένα Angular έργο. Τα δεδομένα σχεδίασης παρέχονται από μια υπηρεσία websocket, στο παράδειγμά μας χρησιμοποιούμε το aiohttp, αλλά μπορείτε να ρυθμίσετε οποιαδήποτε άλλη σύνδεση websocket. Ένα γωνιακό στοιχείο μπορεί να ενσωματωθεί οπουδήποτε στο html με το όνομα ετικέτας του, το ακόλουθο απόσπασμα δείχνει το στοιχείο γραφήματος Bokeh
<bokeh-chart></bokeh-chart>
Το στοιχείο του διαγράμματος bokeh είναι ένα κανονικό γωνιακό στοιχείο, με ένα τμήμα html
<div [id]="id"></div>
και ένα δακτυλόγραφο μέρος. Το στοιχείο γραφήματος χρειάζεται μόνο να παρέχει το αναγνωριστικό στο δικό του τμήμα html. Τα δεδομένα για το γράφημα παρέχονται από μια υπηρεσία που καλείται ακριβώς κατά την προετοιμασία του στοιχείου στο ngOnInit. Το σχετικό τμήμα γραφομηχανής του στοιχείου γραφήματος Bokeh μοιάζει
... export class BokehChartComponent implements OnInit { public id: string; constructor( private bokehService: BokehService) { } ngOnInit() { this.id = "chart"; this.bokehService.getChart(this.id); } }
Δεδομένου ότι η lib BokehJS δεν έχει διαθέσιμους τύπους, η ενσωμάτωση στο γωνιακό δεν είναι τόσο απλή όσο θα έπρεπε. Κάποιος έχει πρόσβαση σε αυτό το lib μόνο μέσω του καθολικού εκτεθειμένου αντικειμένου του lib, το οποίο σε αυτήν την περίπτωση ονομάζεται επίσης Bokehκαι το οποίο είναι το μόνο άγκιστρο που είναι απαραίτητο για την ενσωμάτωση ενός γραφήματος.
// this is the global hook to the bokehjs lib (without types) declare var Bokeh: any;
Αυτή η μαγεία λειτουργεί μόνο όπως αναμένεται εάν συνδέσετε το συνηθισμένο σενάριο java στο κορυφαίο αρχείο html της γωνιακής εφαρμογής index.html
<head> ... <link href="https://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.css" rel="stylesheet" type="text/css"> <script src="https://cdn.pydata.org/bokeh/release/bokeh-1.0.4.min.js"></script> </head>
Η υπηρεσία Bokeh
παρέχει τα δεδομένα για το γράφημα μέσω μιας MessageService η οποία ενσωματώνει τη σύνδεση στο backend έτσι ώστε να χρειάζεται μόνο να στείλει ένα σωστά μορφοποιημένο μήνυμα με την εκτεθειμένη μέθοδο sendMsg(msg).
export class BokehService extends Connector { constructor(private msgService: MessageService) { super(‘BokehService’); this.msgService.register(this); } … public getChart(id: string) { const msg = { name: ‘addChart’, args: [id], action: ‘default’ }; this.msgService.sendMsg(msg); }
Αυτή η υπηρεσία εκθέτει επίσης μια μέθοδο στο backend, η οποία στην πραγματικότητα σχεδιάζει το γράφημα στο εγγενές στοιχείο DOM, όπου πρέπει πρώτα να διαγράψουμε τα προηγούμενα διαγράμματα.
public plot(msg: Message) { const id = msg.args.id; const el = document.getElementById(id); // first remove the previous charts as child // like this, bokeh does not let us update a chart while (el.hasChildNodes()) { el.removeChild(el.lastChild); } // be sure to include the correct dom-id as second argument Bokeh.embed.embed_item(msg.args.item, id); }
Η υπηρεσία back-end
στο παράδειγμά μας είναι γραμμένο σε python. Χρησιμοποιούμε το aiohttp ως ασύγχρονη λύση για έναν web server. Αμέσως μετά την εκκίνηση της γωνιακής εφαρμογής στο πρόγραμμα περιήγησης, το γωνιακό WebsocketService συνδέεται αμέσως με το backend της python στην πλευρά του διακομιστή. Θυμηθείτε ότι κατά την παραγωγή θα εφαρμόσατε περισσότερη ασφάλεια σε αυτό το σημείο, όπως ένας έλεγχος ταυτότητας. Το backend είναι έτοιμο να λάβει συμβάντα από angular, όπως π.χ. δώστε μου τα δεδομένα για το διάγραμμα Bokeh.
Το addChart που καλείται από το μήνυμα από το angular, στέλνει το chartItem ως στοιχείο json που συνδέεται με την υπηρεσία websocket
async def addChart(self, id_, user): """ Example for adding a bokeh chart from backend """ chartItem = self.chartProvider.chartExample() print("try to add chart for dom-id %s" % id_) context = {"name": "BokehService", "args": {"item": chartItem, "id": id_}, "action": "plot"} await self.send_event(json.dumps(context), user=user)
Το ενδιαφέρον μέρος εδώ είναι η μέθοδος send_event που στην πραγματικότητα βασίζεται στην υλοποίηση του διακομιστή websocket. Όπως αναφέρθηκε ήδη, αυτό το μέρος μπορεί να διαφέρει στην ατομική σας εφαρμογή.
Το ελάχιστο παράδειγμα για το γράφημα, γραμμένο επίσης ως συνάρτηση μέλους της κλάσης ChartProvider, φαίνεται πολύ απλό και απλώς παράγει τα δεδομένα για μια απλή αμαρτία στο Bokeh
import time import numpy as np from bokeh.plotting import figure from bokeh.embed import json_item class ChartProvider(): def chartExample(self): t0 = time.time() # prepare some data self.phi += 0.02 x = np.arange(0., 10., 0.1) y = np.sin(x + self.phi) # create a new plot p = figure() p.line(x, y, legend="SIN") chart_item = json_item(p) print(time.time()-t0) return chart_item