Online-Abholung im Geschäft: Bonjour-Mahlzeit – Teil 2 – Einkaufswagen erstellen

1. Einführung

53003251caaf2be5.png 8826bd8cb0c0f1c7.png

Zuletzt aktualisiert:30.10.2020

Einkaufswagen in Business Messages erstellen

Dies ist das zweite Codelab in einer Reihe, um die User Journey „Online kaufen und im Geschäft abholen“ zu gestalten. Bei vielen E-Commerce-Prozessen ist der Einkaufswagen der Schlüssel dafür, aus Nutzern zahlende Kunden zu machen. Der Warenkorb ist außerdem eine Möglichkeit, mehr über Ihre Kunden zu erfahren, und bietet die Möglichkeit, Vorschläge zu weiteren Artikeln zu machen, die für sie von Interesse sein könnten. In diesem Codelab konzentrieren wir uns auf das Erstellen des Warenkorbs und die Bereitstellung der Anwendung in der Google App Engine.

Was macht einen guten Einkaufswagen aus?

Einkaufswagen sind der Schlüssel zu einem erfolgreichen Online-Shopping-Erlebnis. Wie sich herausstellte, ist Business Messages nicht nur gut darin, Fragen und Antworten zu einem Produkt bei potenziellen Kunden zu ermöglichen, sondern kann auch das gesamte Einkaufserlebnis bis hin zur Durchführung einer Zahlung im Gespräch erleichtern.

9d17537b980d0e62.png

Abgesehen von einem guten Warenkorb ermöglicht ein gutes Einkaufserlebnis den Nutzenden, Artikel nach Kategorie zu suchen, und ermöglicht dem Unternehmen, andere Produkte zu empfehlen, an denen der Käufer interessiert sein könnte. Nachdem sie weitere Artikel in den Warenkorb gelegt haben, können sie sich den gesamten Warenkorb ansehen und vor dem Bezahlen Artikel entfernen oder weitere hinzufügen.

Inhalt

In diesem Abschnitt der Codelab-Reihe erweitern Sie den digitalen Agent, den Sie in Teil 1 für das fiktive Unternehmen Bonjour Meal erstellt haben. So können Nutzer einen Artikelkatalog durchsuchen und Artikel in den Einkaufswagen legen.

In diesem Codelab

  • Katalog mit Fragen in Business Messages anzeigen
  • Artikel vorschlagen, die für Nutzer interessant sein könnten
  • Überprüfen Sie den Inhalt des Einkaufswagens und erstellen Sie eine Zusammenfassung des Gesamtpreises.

ab2fb6a4ed33a129.png

Lerninhalte

  • Webanwendung in App Engine auf der Google Cloud Platform bereitstellen
  • So verwenden Sie einen dauerhaften Speichermechanismus, um den Zustand eines Einkaufswagens zu speichern

Dieses Codelab konzentriert sich auf die Erweiterung des digitalen Agenten aus Teil 1 dieser Codelab-Reihe.

Voraussetzungen

  • Ein GCP-Projekt, das registriert und für die Verwendung mit Business Messages genehmigt wurde
  • Auf unserer Entwicklerwebsite finden Sie eine Anleitung dazu,
  • Eine für Ihr GCP-Projekt erstellte JSON-Anmeldedatendatei für das Dienstkonto
  • Ein Gerät mit Android 5 oder höher ODER ein iOS-Gerät mit der Google Maps App
  • Erfahrung mit der Programmierung von Webanwendungen
  • Eine Internetverbindung.

2. Einrichtung

In diesem Codelab wird davon ausgegangen, dass Sie Ihren ersten Agent erstellt und Teil 1 des Codelab abgeschlossen haben. Daher werden wir nicht auf die Grundlagen der Business Messages API und Business Communications API, der Erstellung von Dienstkontoschlüsseln, der Bereitstellung von Anwendungen oder der Einrichtung des Webhooks in der Business Communications Console eingehen. Vor diesem Hintergrund klonen wir eine Beispielanwendung, um sicherzustellen, dass Ihre Anwendung mit dem übereinstimmt, auf dem wir aufbauen. Wir aktivieren die API für Datastore auf der Google Cloud Platform, um Daten zu dem Warenkorb zu speichern.

Anwendung von GitHub klonen

Klonen Sie in einem Terminal das Django Echo Bot Sample-Beispiel mit dem folgenden Befehl in das Arbeitsverzeichnis Ihres Projekts:

$ git clone https://github.com/google-business-communications/bm-bonjour-meal-django-starter-code

Kopieren Sie die für das Dienstkonto erstellte JSON-Datei mit den Anmeldedaten in den Ressourcenordner des Beispiels und benennen Sie die Anmeldedaten in "bm-agent-service-account-credentials.json" um.

bm-bonjour-meal-django-starter-code/bonjourmeal-codelab/step-2/resources/bm-agent-service-account-credentials.json

Google Datastore API aktivieren

In Teil 1 dieses Codelab haben Sie die Business Messages API, Business Communication API und Cloud Build API aktiviert.

Da wir in diesem Codelab mit Google Datastore arbeiten, müssen wir auch diese API aktivieren:

  1. Öffnen Sie die Google Datastore API in der Google Cloud Console.
  2. Prüfen Sie, ob Sie mit dem richtigen GCP-Projekt arbeiten.
  3. Klicken Sie auf Aktivieren.

Beispielanwendung bereitstellen

Wechseln Sie in einem Terminal zum Verzeichnis "step-2" des Beispiels.

Führen Sie die folgenden Befehle in einem Terminal aus, um das Beispiel bereitzustellen:

$ gcloud config set project PROJECT_ID*
$ gcloud app deploy
  • PROJECT_ID ist die Projekt-ID des Projekts, das Sie zur Registrierung bei den APIs verwendet haben.

Notieren Sie sich die URL der bereitgestellten Anwendung in der Ausgabe des letzten Befehls:

Deployed service [default] to [https://PROJECT_ID.appspot.com]

Der Code, den Sie gerade bereitgestellt haben, enthält eine Webanwendung mit einem Webhook, über den Nachrichten von Business Messages empfangen werden können. Hier finden Sie alle unsere bisherigen Schritte aus Teil 1 des Codelabs. Falls noch nicht geschehen, konfigurieren Sie den Webhook.

Die Anwendung beantwortet einige einfache Anfragen, z. B. wenn ein Nutzer nach den Öffnungszeiten von Bonjour Meal fragt. Sie sollten dies auf Ihrem Mobilgerät über die Test-URLs testen, die Sie in den Agent-Informationen in der Business Communications Console abrufen können. Über die Test-URLs wird Business Messages auf Ihrem Mobilgerät gestartet und Sie können dort mit dem Agent interagieren.

3. Der Produktkatalog

Inventarsystem

In den meisten Fällen ist eine direkte Integration in das Inventar einer Marke über eine interne API möglich. In anderen Fällen können Sie z. B. Scraping einer Webseite erfassen oder Ihr eigenes Inventar-Tracking-System erstellen. Wir konzentrieren uns nicht darauf, ein Inventarsystem zu erstellen. Wir verwenden eine einfache statische Datei, die Bilder und Produktinformationen für unseren Kundenservicemitarbeiter enthält. In diesem Abschnitt rufen wir Informationen aus dieser statischen Datei ab, blenden diese Informationen in der Unterhaltung ein und ermöglichen es den Nutzenden, die Artikel zu durchsuchen, die sie in den Einkaufswagen legen können.

Die statische Inventardatei sieht so aus:

bonjourmeal-codelab/step-2/resources/inventory.json

{

    "food": [
        {
            "id":0,
            "name": "Ham and cheese sandwich",
            "price": "6.99",
            "image_url": "https://storage.googleapis.com/bonjour-rail.appspot.com/ham-and-cheese.png",
            "remaining": 8
        },
        {
            "id":1,
            "name": "Chicken veggie wrap",
            "price": "9.99",
            "image_url": "https://storage.googleapis.com/bonjour-rail.appspot.com/chicken-veggie-wrap.png",
            "remaining": 2
        },
        {
            "id":2,
            "name": "Assorted cheese plate",
            "price": "7.99",
            "image_url": "https://storage.googleapis.com/bonjour-rail.appspot.com/assorted-cheese-plate.png",
            "remaining": 6
        },
        {
            "id":3,
            "name": "Apple walnut salad",
            "price": "12.99",
            "image_url": "https://storage.googleapis.com/bonjour-rail.appspot.com/apple-walnut-salad.png",
            "remaining": 1
        }
    ]
}

Lassen Sie uns die Python-Anwendung abrufen, um diese Datei einzulesen.

Unsere Bestandsaufnahme

Das Inventar ist eine statische Datei namens „inventory.json“ im Verzeichnis „./resources“. Wir müssen in „views.py“ eine Python-Logik hinzufügen, um den Inhalt der JSON-Datei zu lesen und dann in der Unterhaltung anzuzeigen. Erstellen wir eine Funktion, die Daten aus der JSON-Datei einliest und die Liste der verfügbaren Produkte zurückgibt.

Diese Funktionsdefinition kann überall in „views.py“ platziert werden.

bonjourmeal-codelab/step-2/bopis/views.py

...
def get_inventory_data():
        f = open(INVENTORY_FILE)
        inventory = json.load(f)
        return inventory
...

Auf diese Weise erhalten wir alles, was wir brauchen, um die Daten aus dem Inventar zu lesen. Jetzt brauchen wir eine Möglichkeit, diese Produktinformationen in die Diskussion einfließen zu lassen.

Produktkatalog aufrufen

Der Einfachheit halber haben wir für dieses Codelab einen allgemeinen Produktkatalog erstellt, in dem alle Inventarelemente in der Business Messages-Unterhaltung über ein einzelnes Rich Card-Karussell angezeigt werden.

Um den Produktkatalog aufzurufen, erstellen wir einen Antwortvorschlag, der den Text "Menü anzeigen" und postbackData "show-product-catalog" enthält. Wenn Nutzer auf die vorgeschlagene Antwort tippen und Ihre Webanwendung die Postback-Daten empfängt, wird das Rich Card-Karussell gesendet. Fügen wir nun eine neue Konstante für diesen Antwortvorschlag oben in "views.py" hinzu.

bonjourmeal-codelab/step-2/bopis/views.py

...
CMD_SHOW_PRODUCT_CATALOG = 'show-product-catalog'
...

Von hier aus analysieren wir die Nachricht und leiten sie an eine neue Funktion weiter, die ein Rich Card-Karussell mit dem Produktkatalog sendet. Erweitern Sie zuerst die Funktion route_message, um eine Funktion „send_product_catalog“ aufzurufen, wenn auf die vorgeschlagene Antwort getippt wird. Dann definieren wir die Funktion.

Fügen Sie im folgenden Snippet eine zusätzliche Bedingung zur if-Anweisung in der Funktion route_message hinzu, um zu prüfen, ob normalized_message mit der zuvor definierten Konstante CMD_SHOW_PRODUCT_CATALOG übereinstimmt.

bonjourmeal-codelab/step-2/bopis/views.py

...
def route_message(message, conversation_id):
    '''
    Routes the message received from the user to create a response.

    Args:
        message (str): The message text received from the user.
        conversation_id (str): The unique id for this user and agent.
    '''
    normalized_message = message.lower()

    if normalized_message == CMD_RICH_CARD:
        send_rich_card(conversation_id)
    elif normalized_message == CMD_CAROUSEL_CARD:
        send_carousel(conversation_id)
    elif normalized_message == CMD_SUGGESTIONS:
        send_message_with_suggestions(conversation_id)
    elif normalized_message == CMD_BUSINESS_HOURS_INQUIRY:
        send_message_with_business_hours(conversation_id)
    elif normalized_message == CMD_ONLINE_SHOPPING_INQUIRY:
        send_online_shopping_info_message(conversation_id)
    elif normalized_message == CMD_SHOW_PRODUCT_CATALOG:
        send_product_catalog(conversation_id)
    else:
        echo_message(message, conversation_id)
...

Schließen wir nun den Ablauf ab und definieren wir send_product_catalog. send_product_catalog ruft get_menu_carousel, auf, wodurch aus der Inventardatei, die wir bereits gelesen haben, ein Karussell mit Rich Cards erstellt wird.

Die Funktionsdefinitionen können an einer beliebigen Stelle in „views.py“ platziert werden. Beachten Sie, dass im folgenden Snippet zwei neue Konstanten verwendet werden, die am Anfang der Datei hinzugefügt werden sollten.

bonjourmeal-codelab/step-2/bopis/views.py

...

CMD_ADD_ITEM = 'add-item'
CMD_SHOW_CART = 'show-cart'

...

def get_menu_carousel():
    """Creates a sample carousel rich card.

    Returns:
       A :obj: A BusinessMessagesCarouselCard object with three cards.
    """

    inventory = get_inventory_data()

    card_content = []

    for item in inventory['food']:
        card_content.append(BusinessMessagesCardContent(
            title=item['name'],
            description=item['price'],
            suggestions=[
                BusinessMessagesSuggestion(
                    reply=BusinessMessagesSuggestedReply(
                        text='Add item',
                        postbackData='{'+f'"action":"{CMD_ADD_ITEM}","item_name":"{item["id"]}"'+'}'))

                ],
            media=BusinessMessagesMedia(
                height=BusinessMessagesMedia.HeightValueValuesEnum.MEDIUM,
                contentInfo=BusinessMessagesContentInfo(
                    fileUrl=item['image_url'],
                    forceRefresh=False))))

    return BusinessMessagesCarouselCard(
        cardContents=card_content,
        cardWidth=BusinessMessagesCarouselCard.CardWidthValueValuesEnum.MEDIUM)

def send_product_catalog(conversation_id):
    """Sends the product catalog to the conversation_id.

    Args:
        conversation_id (str): The unique id for this user and agent.
    """
    rich_card = BusinessMessagesRichCard(carouselCard=get_menu_carousel())

    fallback_text = ''

    # Construct a fallback text for devices that do not support carousels
    for card_content in rich_card.carouselCard.cardContents:
        fallback_text += (card_content.title + '\n\n' + card_content.description
                          + '\n\n' + card_content.media.contentInfo.fileUrl
                          + '\n---------------------------------------------\n\n')

    message_obj = BusinessMessagesMessage(
        messageId=str(uuid.uuid4().int),
        representative=BOT_REPRESENTATIVE,
        richCard=rich_card,
        fallback=fallback_text,
        suggestions=[
        BusinessMessagesSuggestion(
            reply=BusinessMessagesSuggestedReply(
                text='See my cart',
                postbackData=CMD_SHOW_CART)
            ),
        BusinessMessagesSuggestion(
            reply=BusinessMessagesSuggestedReply(
                text='See the menu',
                postbackData=CMD_SHOW_PRODUCT_CATALOG)
            ),
        ]
        )

    send_message(message_obj, conversation_id)
...

Wenn Sie sich die Karussellelemente ansehen, wird auch eine Instanz der BusinessMessagesSuggestion-Klasse erstellt. Jeder Vorschlag steht für eine Nutzerauswahl für ein Produkt im Karussell. Wenn ein Nutzer auf die vorgeschlagene Antwort tippt, sendet Business Messages die postbackData-Daten an den Webhook. Sie enthalten eine JSON-Beschreibung für den Artikel und die Aktion, die der Nutzer ausführen möchte (zum Beispiel in den Einkaufswagen legen oder daraus entfernen). Im folgenden Abschnitt werden Nachrichten wie diese analysiert, damit der Artikel tatsächlich in den Warenkorb gelegt werden kann.

Nachdem wir diese Änderungen vorgenommen haben, können Sie die Webanwendung in Google App Engine bereitstellen und die Umgebung testen!

$ gcloud app deploy

Wenn die Konversationsoberfläche auf Ihrem Mobilgerät geladen ist, senden Sie die Nachricht „Produktkatalog anzeigen“. Sie sollten dann ein Karussell mit Produkten sehen, die so aussehen.

4639da46bcc5230c.png

Wenn Sie auf Element hinzufügen tippen, passiert nichts, außer der Agent gibt die Postback-Daten aus der vorgeschlagenen Antwort wieder. Im nächsten Abschnitt werden wir den Produktkatalog nutzen, um den Warenkorb zu erstellen, dem der Artikel hinzugefügt wird.

Der Produktkatalog, den Sie gerade erstellt haben, kann auf verschiedene Arten erweitert werden. Sie haben möglicherweise eine andere Getränkekarte oder vegetarische Optionen. Mit Karussells oder Vorschlags-Chips können Nutzer die Menüoptionen aufschlüsseln, um zu einer Gruppe von Produkten zu gelangen, nach denen sie suchen. Als Erweiterung dieses Codelabs können Sie das Produktkatalogsystem so erweitern, dass Nutzende Getränke getrennt von Speisen auf der Speisekarte sehen oder sogar vegetarische Optionen auswählen können.

4. Der Warenkorb

In diesem Abschnitt des Codelabs bauen wir die Warenkorbfunktion auf dem vorherigen Abschnitt auf, mit dem wir die verfügbaren Produkte durchsuchen können.

Unter anderem können Nutzer Artikel in den Warenkorb legen, Artikel aus dem Warenkorb entfernen, die Anzahl der einzelnen Artikel im Warenkorb verfolgen und Artikel im Warenkorb prüfen.

Die Verfolgung des Zustands des Warenkorbs bedeutet, dass wir Daten in der Webanwendung beibehalten müssen. Der Einfachheit halber verwenden wir Google Datastore in der Google Cloud Platform, um Daten zu speichern und zu testen. Die Konversations-ID bleibt zwischen einem Nutzer und dem Unternehmen gleich, sodass wir sie verwenden können, um Nutzer mit Warenkorb-Artikeln zu verknüpfen.

Zunächst stellen wir eine Verbindung mit Google Datastore her und speichern die Unterhaltungs-ID, wenn wir sie sehen.

Mit Datastore verbinden

Wir stellen immer dann eine Verbindung zu Google Datastore her, wenn eine Interaktion im Einkaufswagen ausgeführt wird, zum Beispiel, wenn ein Nutzer einen Artikel hinzufügt oder löscht. Weitere Informationen zur Verwendung dieser Clientbibliothek für die Interaktion mit Google Datastore finden Sie in der offiziellen Dokumentation.

Das folgende Snippet definiert eine Funktion zum Aktualisieren des Warenkorbs. Die Funktion verwendet die folgende Eingabe: conversation_id und message. message enthält eine JSON-Beschreibung der Aktion, die der Nutzer ausführen möchte. Diese ist bereits in Ihr Karussell integriert, in dem der Produktkatalog angezeigt wird. Die Funktion erstellt einen Google Datastore-Client und ruft sofort eine ShoppingCart-Entität ab, wobei der Schlüssel die Unterhaltungs-ID ist.

Kopieren Sie die folgende Funktion in Ihre Datei view.py. Im nächsten Abschnitt erfahren Sie mehr dazu.

bonjourmeal-codelab/step-2/bopis/views.py

from google.oauth2 import service_account
from google.cloud import datastore

def update_shopping_cart(conversation_id, message):
        credentials = service_account.Credentials.from_service_account_file(
        SERVICE_ACCOUNT_LOCATION)

        client = datastore.Client(credentials=credentials)
        key = client.key('ShoppingCart', conversation_id)
        entity = datastore.Entity(key=key)
        result = client.get(key)
        
        # TODO: Add logic to add and remove items from cart
        
        entity.update(result)
        client.put(entity)

Erweitern wir nun diese Funktion, um einen Artikel in den Warenkorb zu legen.

Artikel in den Einkaufswagen legen

Wenn der Nutzer im Produktkarussell auf eine von Element hinzufügen vorgeschlagene Aktion tippt, enthalten „postbackData“ JSON, das die Aktion beschreibt, die der Nutzer ausführen möchte. Das JSON-Wörterbuch hat die beiden Schlüssel „action“ und „item_name“. Dieses JSON-Wörterbuch wird an Ihren Webhook gesendet. Das Feld „item_name“ enthält die eindeutige Kennung, die mit dem Artikel in der Datei „inventory.json“ verknüpft ist.

Sobald wir den Befehl „cart“ und den Warenkorbartikel aus der Nachricht geparst haben, können wir bedingte Anweisungen schreiben, um den Artikel hinzuzufügen. Hier sind einige Grenzfälle zu berücksichtigen, wenn der Datenspeicher die Unterhaltungs-ID nie gesehen hat oder ob der Einkaufswagen diesen Artikel zum ersten Mal empfängt. Im Folgenden finden Sie eine Erweiterung der oben definierten update_shopping_cart-Funktion. Durch diese Änderung wird ein Artikel zum Einkaufswagen hinzugefügt, der von Google Datastore gespeichert wird.

Das folgende Snippet ist eine Erweiterung der vorherigen Funktion, die zu "views.py" hinzugefügt wurde. Sie können den Unterschied hinzufügen oder das Snippet kopieren und die vorhandene Version der update_shopping_cart-Funktion ersetzen.

bonjourmeal-codelab/step-2bopis/views.py

def update_shopping_cart(conversation_id, message):
    credentials = service_account.Credentials.from_service_account_file(
      SERVICE_ACCOUNT_LOCATION)
    inventory = get_inventory_data()

    cart_request = json.loads(message)
    cart_cmd = cart_request["action"]
    cart_item = cart_request["item_name"]

    item_name = inventory['food'][int(cart_item)]['name']

    client = datastore.Client(credentials=credentials)
    key = client.key('ShoppingCart', conversation_id)
    entity = datastore.Entity(key=key)
    result = client.get(key)

    if result is None:
        if cart_cmd == CMD_ADD_ITEM:
            entity.update({
                item_name: 1
            })

    else:
        if cart_cmd == CMD_ADD_ITEM:
            if result.get(item_name) is None:
                result[item_name] = 1
            else:
                result[item_name] = result[item_name] + 1

        entity.update(result)
    client.put(entity)

Diese Funktion wird später erweitert, um das Szenario zu unterstützen, bei dem cart_cmd den String „del-item“ enthält, der in CMD_DEL_ITEM definiert ist.

Zusammenführung

Achten Sie darauf, dass Sie die Anschlüsse in die Funktion route_message einfügen. Wenn Sie dann eine Meldung zum Hinzufügen eines Artikels zum Warenkorb erhalten, wird die Funktion update_shopping_cart aufgerufen. Außerdem musst du eine Konstante zum Hinzufügen von Elementen definieren. Dabei muss die Konvention verwendet werden, die wir in diesem Codelab verwenden.

bonjourmeal-codelab/step-2bopis/views.py

...

CMD_DEL_ITEM = 'del-item'

...

def route_message(message, conversation_id):
    '''
    Routes the message received from the user to create a response.

    Args:
        message (str): The message text received from the user.
        conversation_id (str): The unique id for this user and agent.
    '''
    normalized_message = message.lower()

    if normalized_message == CMD_RICH_CARD:
        send_rich_card(conversation_id)
    elif normalized_message == CMD_CAROUSEL_CARD:
        send_carousel(conversation_id)
    elif normalized_message == CMD_SUGGESTIONS:
        send_message_with_suggestions(conversation_id)
    elif normalized_message == CMD_BUSINESS_HOURS_INQUIRY:
        send_message_with_business_hours(conversation_id)
    elif normalized_message == CMD_ONLINE_SHOPPING_INQUIRY:
        send_online_shopping_info_message(conversation_id)
    elif normalized_message == CMD_SHOW_PRODUCT_CATEGORY:
        send_product_catalog(conversation_id)
    elif CMD_ADD_ITEM in normalized_message or CMD_DEL_ITEM in normalized_message:
       update_shopping_cart(conversation_id, message)
    else:
        echo_message(message, conversation_id)

...

Derzeit können Sie Artikel in den Warenkorb legen. Wenn Sie Ihre Änderungen in Google App Engine bereitstellen, sollten Sie die Änderungen am Warenkorb im Google Datastore-Dashboard in der GCP Console sehen können. Im Screenshot unten in der Google Datastore-Konsole sehen Sie eine einzelne Entität, die nach der Unterhaltungs-ID benannt ist, gefolgt von einigen Beziehungen zu den Inventarartikeln und der Menge der Artikel im Einkaufswagen.

619dc18a8136ea69.png

Im nächsten Abschnitt erstellen wir eine Möglichkeit, die Artikel im Einkaufswagen aufzulisten. Über den Mechanismus zur Überprüfung des Einkaufswagens sollten wir alle Artikel im Einkaufswagen, die Anzahl dieser Artikel und eine Option zum Entfernen eines Artikels aus dem Einkaufswagen anzeigen können.

Artikel im Einkaufswagen überprüfen

Das Auflisten der Artikel im Einkaufswagen ist die einzige Möglichkeit, den Zustand des Einkaufswagens nachzuvollziehen und zu wissen, welche Artikel wir entfernen können.

Senden Sie uns zuerst eine freundliche Nachricht wie „Hier ist Ihr Einkaufswagen:“, gefolgt von einer weiteren Nachricht, die ein interaktives Kartenkarussell mit zugehörigen Antwortvorschlägen für „Eine entfernen“ oder „Eine hinzufügen“ enthält. Im Karussell der Rich-Karte sollte zusätzlich die Anzahl der im Einkaufswagen gespeicherten Artikel angegeben werden.

Bevor wir unsere Funktion schreiben, sollten Sie sich darüber im Klaren sein: Wenn sich nur ein Artikeltyp im Einkaufswagen befindet, können wir ihn nicht als Karussell rendern. Karussells für interaktive Infokarten müssen mindestens zwei Karten enthalten. Auf der anderen Seite möchten wir, wenn sich keine Artikel im Warenkorb befinden, eine einfache Meldung anzeigen lassen, die besagt, dass der Warenkorb leer ist.

Vor diesem Hintergrund definieren wir eine Funktion namens send_shopping_cart. Diese Funktion stellt eine Verbindung zu Google Datastore her und fordert eine ShoppingCart-Entität basierend auf der Unterhaltungs-ID an. Danach rufen wir die get_inventory_data-Funktion auf und verwenden ein Rich-Karten-Karussell, um den Status des Einkaufswagens zu erfassen. Außerdem müssen wir die ID eines Produkts anhand des Namens abrufen und eine Funktion deklarieren, die in Google Datastore nach diesem Wert sucht. Während das Karussell erstellt wird, können wir Antwortvorschläge miteinander verknüpfen, um Artikel zu löschen oder Artikel nach Produkt-ID hinzuzufügen. Das folgende Snippet führt alle diese Vorgänge aus. Kopieren Sie den Code an einer beliebigen Stelle in view.py.

bonjourmeal-codelab/step-2/bopis/views.py

...
def get_id_by_product_name(product_name):
  inventory = get_inventory_data()
  for item in inventory['food']:
    if item['name'] == product_name:
      return int(item['id'])
  return False


def send_shopping_cart(conversation_id):
  credentials = service_account.Credentials.from_service_account_file(
      SERVICE_ACCOUNT_LOCATION)

  # Retrieve the inventory data
  inventory = get_inventory_data()

  # Pull the data from Google Datastore
  client = datastore.Client(credentials=credentials)
  key = client.key('ShoppingCart', conversation_id)
  result = client.get(key)

  shopping_cart_suggestions = [
      BusinessMessagesSuggestion(
          reply=BusinessMessagesSuggestedReply(
              text='See total price', postbackData='show-cart-price')),
      BusinessMessagesSuggestion(
          reply=BusinessMessagesSuggestedReply(
              text='Empty the cart', postbackData='empty-cart')),
      BusinessMessagesSuggestion(
          reply=BusinessMessagesSuggestedReply(
              text='See the menu', postbackData=CMD_SHOW_PRODUCT_CATALOG)),
  ]

  if result is None or len(result.items()) == 0:
    message_obj = BusinessMessagesMessage(
        messageId=str(uuid.uuid4().int),
        representative=BOT_REPRESENTATIVE,
        text='There are no items in your shopping cart.',
        suggestions=shopping_cart_suggestions)

    send_message(message_obj, conversation_id)
  elif len(result.items()) == 1:

    for product_name, quantity in result.items():
      product_id = get_id_by_product_name(product_name)

      fallback_text = ('You have one type of item in the shopping cart')

      rich_card = BusinessMessagesRichCard(
          standaloneCard=BusinessMessagesStandaloneCard(
              cardContent=BusinessMessagesCardContent(
                  title=product_name,
                  description=f'{quantity} in cart.',
                  suggestions=[
                      BusinessMessagesSuggestion(
                          reply=BusinessMessagesSuggestedReply(
                              text='Remove one',
                              postbackData='{'+f'"action":"{CMD_DEL_ITEM}","item_name":"{product_id}"'+'}'))
                  ],
                  media=BusinessMessagesMedia(
                      height=BusinessMessagesMedia.HeightValueValuesEnum.MEDIUM,
                      contentInfo=BusinessMessagesContentInfo(
                          fileUrl=inventory['food'][product_id]
                          ['image_url'],
                          forceRefresh=False)))))

      message_obj = BusinessMessagesMessage(
          messageId=str(uuid.uuid4().int),
          representative=BOT_REPRESENTATIVE,
          richCard=rich_card,
          suggestions=shopping_cart_suggestions,
          fallback=fallback_text)

      send_message(message_obj, conversation_id)
  else:
    cart_carousel_items = []

    # Iterate through the cart and generate a carousel of items
    for product_name, quantity in result.items():
      product_id = get_id_by_product_name(product_name)

      cart_carousel_items.append(
          BusinessMessagesCardContent(
              title=product_name,
              description=f'{quantity} in cart.',
              suggestions=[
                  BusinessMessagesSuggestion(
                      reply=BusinessMessagesSuggestedReply(
                          text='Remove one',
                          postbackData='{'+f'"action":"{CMD_DEL_ITEM}","item_name":"{product_id}"'+'}'))
              ],
              media=BusinessMessagesMedia(
                  height=BusinessMessagesMedia.HeightValueValuesEnum.MEDIUM,
                  contentInfo=BusinessMessagesContentInfo(
                      fileUrl=inventory['food'][product_id]
                      ['image_url'],
                      forceRefresh=False))))

    rich_card = BusinessMessagesRichCard(
        carouselCard=BusinessMessagesCarouselCard(
            cardContents=cart_carousel_items,
            cardWidth=BusinessMessagesCarouselCard.CardWidthValueValuesEnum
            .MEDIUM))

    fallback_text = ''

    # Construct a fallback text for devices that do not support carousels
    for card_content in rich_card.carouselCard.cardContents:
      fallback_text += (
          card_content.title + '\n\n' + card_content.description + '\n\n' +
          card_content.media.contentInfo.fileUrl +
          '\n---------------------------------------------\n\n')

    message_obj = BusinessMessagesMessage(
        messageId=str(uuid.uuid4().int),
        representative=BOT_REPRESENTATIVE,
        richCard=rich_card,
        suggestions=shopping_cart_suggestions,
        fallback=fallback_text,
    )

    send_message(message_obj, conversation_id)

...

Vergewissern Sie sich, dass Sie bereits CMD_SHOW_CART oben in view.py definiert haben, und rufen Sie send_shopping_cart auf, wenn der Nutzer eine Nachricht mit dem Inhalt "Warenkorb anzeigen" sendet.

bonjourmeal-codelab/step-2/bopis/views.py

...
def route_message(message, conversation_id):
    '''
    Routes the message received from the user to create a response.

    Args:
        message (str): The message text received from the user.
        conversation_id (str): The unique id for this user and agent.
    '''
    normalized_message = message.lower()

    if normalized_message == CMD_RICH_CARD:
        send_rich_card(conversation_id)
    elif normalized_message == CMD_CAROUSEL_CARD:
        send_carousel(conversation_id)
    elif normalized_message == CMD_SUGGESTIONS:
        send_message_with_suggestions(conversation_id)
    elif normalized_message == CMD_BUSINESS_HOURS_INQUIRY:
        send_message_with_business_hours(conversation_id)
    elif normalized_message == CMD_ONLINE_SHOPPING_INQUIRY:
        send_online_shopping_info_message(conversation_id)
    elif normalized_message == CMD_SHOW_PRODUCT_CATEGORY:
        send_product_catalog(conversation_id)
    elif CMD_ADD_ITEM in normalized_message or CMD_DEL_ITEM in normalized_message:
        update_shopping_cart(conversation_id, message)
    elif normalized_message == CMD_SHOW_CART:
        send_shopping_cart(conversation_id)
    else:
        echo_message(message, conversation_id)
...

34801776a97056ac.png

Gemäß der Logik, die wir in der send_shopping_cart-Funktion eingeführt haben, erhalten wir bei der Eingabe von „Warenkorb anzeigen“ entweder eine Meldung, dass sich nichts im Einkaufswagen befindet, eine Rich Card mit dem einen Artikel im Einkaufswagen oder ein Karussell mit Karten mit mehreren Artikeln. Außerdem haben wir drei Antwortvorschläge: „Gesamtpreis sehen“, „Einkaufswagen leeren“ und „Menü ansehen“.

Nehmen Sie die Codeänderungen oben vor, um zu testen, ob in Ihrem Einkaufswagen Artikel erfasst werden, die Sie hinzufügen, und ob Sie den Einkaufswagen über die Unterhaltungsoberfläche überprüfen können, wie in den Screenshots oben gezeigt. Sie können die Änderungen mit diesem Befehl bereitstellen. Der Befehl wird aus dem Verzeichnis „p_step-2“ ausgeführt, in dem Sie die Änderungen hinzufügen.

$ gcloud app deploy

Wir werden die Funktion „Gesamtpreis anzeigen“ im nächsten Abschnitt erstellen, nachdem wir die Funktion zum Entfernen eines Artikels aus dem Einkaufswagen entwickelt haben. Die Funktion get_cart_price verhält sich ähnlich wie die Funktion „Warenkorb ansehen“ in dem Sinne, dass Daten in Datastore mit der Datei „inventory.json“ verglichen werden, um einen Gesamtpreis für den Einkaufswagen zu ermitteln. Dies wird für den nächsten Teil des Codelabs nützlich sein, in dem wir Zahlungen integrieren.

Artikel aus dem Einkaufswagen entfernen

Schließlich können wir das Verhalten des Einkaufswagens vervollständigen, indem wir eine Funktion zum Entfernen des Einkaufswagens einführen. Ersetzen Sie die vorhandene update_shopping_cart-Funktion durch das folgende Snippet.

bonjourmeal-codelab/step-2/ bopis/views.py

def update_shopping_cart(conversation_id, message):
    credentials = service_account.Credentials.from_service_account_file(
      SERVICE_ACCOUNT_LOCATION)
    inventory = get_inventory_data()

    cart_request = json.loads(message)
    cart_cmd = cart_request["action"]
    cart_item = cart_request["item_name"]

    item_name = inventory['food'][int(cart_item)]['name']


    client = datastore.Client(credentials=credentials)
    key = client.key('ShoppingCart', conversation_id)
    entity = datastore.Entity(key=key)
    result = client.get(key)

    if result is None:
        if cart_cmd == CMD_ADD_ITEM:
            entity.update({
                item_name: 1
            })
        elif cart_cmd == CMD_DEL_ITEM:
            # The user is trying to delete an item from an empty cart. Pass and skip
            pass

    else:
        if cart_cmd == CMD_ADD_ITEM:
            if result.get(item_name) is None:
                result[item_name] = 1
            else:
                result[item_name] = result[item_name] + 1

        elif cart_cmd == CMD_DEL_ITEM:
            if result.get(item_name) is None:
                # The user is trying to remove an item that's no in the shopping cart. Pass and skip
                pass
            elif result[item_name] - 1 > 0:
                result[item_name] = result[item_name] - 1
            else:
                del result[item_name]

        entity.update(result)
    client.put(entity)

Bestätigungsnachricht senden

Wenn der Nutzer einen Artikel in den Einkaufswagen legt, sollten Sie eine Bestätigungsnachricht senden, in der Sie die Aktion und die Bearbeitung der Anfrage bestätigen. Dies hilft nicht nur dabei, Erwartungen zu wecken, sondern hält das Gespräch am Laufen.

Erweitern wir die Funktion update_shopping_cart so, dass eine Nachricht an die Unterhaltungs-ID gesendet wird, die besagt, dass der Artikel hinzugefügt oder entfernt wurde, und Vorschläge dazu erhalten, den Einkaufswagen zu überprüfen oder das Menü noch einmal aufzurufen.

bonjourmeal-codelab/step-2/bopis/views.py

def update_shopping_cart(conversation_id, message):

     # No changes to the function, except appending the following logic
     ...
   
    if cart_cmd == CMD_ADD_ITEM:
        message = 'Great! You\'ve added an item to the cart.'
    else:
        message = 'You\'ve removed an item from the cart.'

    message_obj = BusinessMessagesMessage(
        messageId=str(uuid.uuid4().int),
        representative=BOT_REPRESENTATIVE,
        text=message,
        suggestions=[
            BusinessMessagesSuggestion(
            reply=BusinessMessagesSuggestedReply(
                text='Review shopping cart',
                postbackData=CMD_SHOW_CART)
            ),
            BusinessMessagesSuggestion(
            reply=BusinessMessagesSuggestedReply(
                text='See menu again',
                postbackData=CMD_SHOW_PRODUCT_CATALOG)
            ),
            ])
    send_message(message_obj, conversation_id)

905a1f3d89893ba0.png

Das sollte es schaffen! Ein vollwertiges Warenkorb-Erlebnis, über das Nutzende Artikel hinzufügen, Artikel entfernen und Artikel im Warenkorb überprüfen können.

Wenn Sie die Einkaufswagenfunktion in der Business Messages-Unterhaltung sehen möchten, stellen Sie die Anwendung bereit, um mit dem Agent zu interagieren. Führen Sie dazu diesen Befehl im Verzeichnis "step-2" aus.

$ gcloud app deploy

5. Vorbereitung auf Zahlungen

Als Vorbereitung auf die Integration eines Zahlungsabwicklers im nächsten Teil der Reihe benötigen wir eine Möglichkeit, den Preis des Einkaufswagens zu ermitteln. Erstellen wir eine Funktion, die den Preis für uns abruft, indem sie die Warenkorbdaten in Google Datastore kreuzt, den Preis jedes Artikels aus dem Inventar abruft und den Preis mit der Menge der einzelnen Artikel im Einkaufswagen multipliziert.

bonjourmeal-codelab/step-2/bopis/views.py

...
def get_cart_price(conversation_id):
    # Pull the data from Google Datastore
    credentials = service_account.Credentials.from_service_account_file(
    SERVICE_ACCOUNT_LOCATION)
    client = datastore.Client(credentials=credentials)
    key = client.key('ShoppingCart', conversation_id)
    entity = datastore.Entity(key=key)
    result = client.get(key)

    # Retrieve the inventory data
    inventory = get_inventory_data()
   
    # Start off with a total of 0 before adding up the total
    total_price = 0

    if len(result.items()) != 0:
      for product_name, quantity in result.items():
        total_price = total_price + float(
            inventory['food'][get_id_by_product_name(product_name)]['price']) * int(quantity)

    return total_price

...

Und schließlich können wir diese Funktion verarbeiten und eine Nachricht an den Nutzer senden.

bonjourmeal-codelab/step-2/bopis/views.py

...

def send_shopping_cart_total_price(conversation_id):
    cart_price = get_cart_price(conversation_id)

    message_obj = BusinessMessagesMessage(
        messageId=str(uuid.uuid4().int),
        representative=BOT_REPRESENTATIVE,
        suggestions=[],
        text=f'Your cart\'s total price is ${cart_price}.')

    send_message(message_obj, conversation_id)
...

Um alles miteinander zu verknüpfen, aktualisieren wir die Funktion route_message und die Konstante, um die obige Logik auszulösen.

bonjourmeal-codelab/step-2/bopis/views.py

...
CMD_GET_CART_PRICE = 'show-cart-price'
...
def route_message(message, conversation_id):
    '''
    Routes the message received from the user to create a response.

    Args:
        message (str): The message text received from the user.
        conversation_id (str): The unique id for this user and agent.
    '''
    normalized_message = message.lower()

    if normalized_message == CMD_RICH_CARD:
        send_rich_card(conversation_id)
    elif normalized_message == CMD_CAROUSEL_CARD:
        send_carousel(conversation_id)
    elif normalized_message == CMD_SUGGESTIONS:
        send_message_with_suggestions(conversation_id)
    elif normalized_message == CMD_BUSINESS_HOURS_INQUIRY:
        send_message_with_business_hours(conversation_id)
    elif normalized_message == CMD_ONLINE_SHOPPING_INQUIRY:
        send_online_shopping_info_message(conversation_id)
    elif normalized_message == CMD_SHOW_PRODUCT_CATEGORY:
        send_product_catalog(conversation_id)
    elif CMD_ADD_ITEM in normalized_message or CMD_DEL_ITEM in normalized_message:
        update_shopping_cart(conversation_id, message)
    elif normalized_message == CMD_SHOW_CART:
        send_shopping_cart(conversation_id)
    elif normalized_message == CMD_GET_CART_PRICE:
        send_shopping_cart_total_price(conversation_id)
    else:
        echo_message(message, conversation_id)
...

Hier sind einige Screenshots, die veranschaulichen, was mit der obigen Logik erreicht wird:

8feacf94ed0ac6c4.png

Wenn die Einbindung in den Zahlungsabwickler im nächsten Teil des Codelabs bereit ist, rufen wir die Funktion get_cart_price auf, um die Daten an den Zahlungsabwickler zu übergeben und den Zahlungsvorgang zu starten.

Sie können diese Einkaufswagenfunktion wieder in der Business Messages-Unterhaltung ausprobieren, indem Sie die Anwendung bereitstellen und mit Ihrem Agent interagieren.

$ gcloud app deploy

6. Glückwunsch

Sie haben erfolgreich einen Einkaufswagen in Business Messages erstellt.

Eine Sache, die wir in diesem Codelab nicht besprochen haben, ist die Funktion zum Leeren des gesamten Warenkorbs. Wenn Sie möchten, können Sie die Anwendung so erweitern, dass die Funktion „Einkaufswagen leeren“ ausgeführt wird. Die Lösung ist in Schritt 3 des geklonten Quellcodes verfügbar.

In einem späteren Abschnitt werden wir einen externen Zahlungsabwickler einbinden, damit Nutzer eine Zahlungstransaktion über Ihre Marke abschließen können.

Was macht einen guten Einkaufswagen aus?

Ein positives Einkaufserlebnis in einer Unterhaltung unterscheidet sich nicht von einer mobilen App oder einem Geschäft im Geschäft. Das Hinzufügen von Artikeln und Entfernen von Artikeln und das Berechnen des Warenkorbpreises sind nur einige der Funktionen, die wir in diesem Codelab kennengelernt haben. Im Gegensatz zu einem echten Einkaufswagen ist es hingegen möglich, den Preis aller Artikel im Einkaufswagen zu einem bestimmten Zeitpunkt anzeigen zu können, während Sie Artikel hinzufügen oder entfernen. Mit diesen hochwertigen Funktionen heben Sie sich von der Masse ab.

Was liegt als Nächstes an?

Wenn Sie bereit sind, sehen Sie sich einige der folgenden Themen an, um mehr über komplexere Interaktionen in Business Messages zu erfahren:

Referenzdokumente