Acquista online Ritiro in negozio: Pasto Bonjour - Parte 2 - Costruire un carrello degli acquisti

1. Introduzione

53003251caaf2be5.png 8826bd8cb0c0f1c7.png

Ultimo aggiornamento: 30/10/2020

Costruire un carrello degli acquisti su Business Messages

Questo è il secondo codelab di una serie finalizzata a creare un percorso utente relativo all'acquisto online e al ritiro in negozio. In molti percorsi di e-commerce, un carrello degli acquisti è fondamentale per la conversione degli utenti in clienti paganti. Il carrello degli acquisti è anche un modo per comprendere meglio i tuoi clienti e un modo per offrire suggerimenti su altri articoli che potrebbero essere di loro interesse. In questo codelab, ci concentreremo sulla creazione dell'esperienza con il carrello degli acquisti e sul deployment dell'applicazione in Google App Engine.

Cosa caratterizza un buon carrello degli acquisti?

I carrelli degli acquisti sono fondamentali per un'esperienza di acquisto online di successo. Come si è scoperto, Business Messages non solo è in grado di facilitare le domande e risposte su un prodotto con un potenziale cliente, ma può anche facilitare l'intera esperienza di acquisto fino al completamento di un pagamento durante la conversazione.

9d17537b980d0e62.png

Oltre a un buon carrello degli acquisti, una buona esperienza di acquisto consente agli utenti di sfogliare gli articoli per categoria e consente all'attività di consigliare altri prodotti che potrebbero interessare all'acquirente. Dopo aver aggiunto altri articoli al carrello, l'utente può esaminare l'intero carrello ed essere in grado di rimuovere articoli o aggiungerne altri prima del pagamento.

Cosa creerai

In questa sezione della serie codelab, hai intenzione di ampliare l'agente digitale che hai creato nella parte 1 per l'azienda fittizia, Bonjour Meal, in modo che gli utenti possano sfogliare un catalogo di articoli e aggiungere articoli a un carrello degli acquisti.

In questo codelab, la tua app

  • Mostrare un catalogo di domande in Business Messages
  • Suggerisci articoli che potrebbero interessare agli utenti
  • Esamina i contenuti del carrello degli acquisti e crea un riepilogo del prezzo totale

ab2fb6a4ed33a129.png

Cosa imparerai a fare

  • Come eseguire il deployment di un'applicazione web su App Engine su Google Cloud Platform
  • Come utilizzare un meccanismo di archiviazione permanente per salvare lo stato di un carrello degli acquisti

Questo codelab è incentrato sull'estensione dell'agente digitale della parte 1 di questa serie codelab.

Che cosa ti serve

  • Un progetto Google Cloud che è stato registrato e approvato per l'utilizzo con Business Messages
  • Visita il nostro sito per sviluppatori per istruzioni su come
  • Un file di credenziali JSON dell'account di servizio generato per il tuo progetto Google Cloud
  • Un dispositivo Android 5 o versioni successive OPPURE un dispositivo iOS con l'app Google Maps
  • Esperienza con la programmazione di applicazioni web
  • Una connessione a Internet.

2. Preparazione

Questo codelab presuppone che tu abbia creato il tuo primo agente e completato la parte 1 del codelab. Pertanto, non esamineremo le nozioni di base sull'abilitazione delle API Business Messages e Business Communications, sulla creazione di chiavi degli account di servizio, sul deployment di un'applicazione o sulla configurazione del webhook nella Business Communications Console. Detto questo, cloniamo un'applicazione di esempio per assicurarci che la tua applicazione sia coerente con ciò che stiamo sviluppando e abiliteremo l'API per Datastore su Google Cloud Platform in modo che sia in grado di memorizzare i dati relativi al carrello degli acquisti.

Clonazione dell'applicazione da GitHub

In un terminale, clona l'esempio di bot di Django Echo nella directory di lavoro del progetto con il seguente comando:

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

Copia il file delle credenziali JSON creato per l'account di servizio nella cartella delle risorse dell'esempio e rinomina le credenziali in "bm-agent-service-account-credentials.json".

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

Abilita l'API Google Datastore

Nella parte 1 di questo codelab, hai abilitato l'API Business Messages, l'API Businesscommunication e l'API Cloud Build.

Per questo codelab, poiché lavoreremo con Google Datastore, dobbiamo abilitare anche questa API:

  1. Apri l'API Google Datastore nella console Google Cloud.
  2. Assicurati di lavorare con il progetto Google Cloud corretto.
  3. Fai clic su Abilita.

Deployment dell'applicazione di esempio

In un terminale, vai alla directory del passaggio 2 dell'esempio.

Esegui questi comandi in un terminale per eseguire il deployment dell'esempio:

$ gcloud config set project PROJECT_ID*
$ gcloud app deploy
  • PROJECT_ID è l'ID del progetto utilizzato per la registrazione con le API.

Prendi nota dell'URL dell'applicazione di cui è stato eseguito il deployment nell'output dell'ultimo comando:

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

Il codice di cui hai appena eseguito il deployment contiene un'applicazione web con un webhook per ricevere messaggi da Business Messages. Contiene tutto ciò che abbiamo fatto nella parte 1 del codelab. Se non l'hai ancora fatto, configura il webhook.

L'applicazione risponderà ad alcune semplici richieste, ad esempio un utente che chiede informazioni sull'orario di apertura del pasto Bonjour. Devi verificarlo sul tuo dispositivo mobile tramite gli URL di test che puoi recuperare dalle informazioni sull'agente all'interno di Business Communications Console. Gli URL di test avvieranno l'esperienza Business Messages sul tuo dispositivo mobile e potrai iniziare a interagire con l'agente da lì.

3. Catalogo dei prodotti

Un sistema di inventario

Nella maggior parte dei casi, puoi effettuare l'integrazione diretta con l'inventario di un brand tramite un'API interna. In altri casi, potresti eseguire lo scraping di una pagina web o creare un tuo sistema di monitoraggio dell'inventario. Il nostro obiettivo non è creare un sistema di inventario; utilizzeremo un semplice file statico contenente immagini e informazioni sui prodotti per il nostro agente. In questa sezione, estrarremo le informazioni da questo file statico, le mostreremo nella conversazione e consentiremo all'utente di sfogliare gli articoli disponibili per l'aggiunta al carrello degli acquisti.

Il file di inventario statico ha il seguente aspetto:

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
        }
    ]
}

Facciamo in modo che l'applicazione Python legga questo file.

Leggere dal nostro inventario

L'inventario è un file statico denominato "inventory.json" che si trova nella directory ./resources. Dobbiamo aggiungere una logica Python a views.py per leggere i contenuti del file JSON e poi visualizzarli nella conversazione. Creiamo una funzione che legge i dati dal file JSON e restituisca l'elenco dei prodotti disponibili.

Questa definizione di funzione può essere inserita ovunque in views.py.

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

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

Questo dovrebbe darci ciò di cui abbiamo bisogno per leggere i dati dall'inventario. Ora abbiamo bisogno di un modo per far emergere queste informazioni sul prodotto nella conversazione.

Visualizzazione del catalogo dei prodotti

Per semplicità, in questo codelab abbiamo a disposizione un catalogo generale dei prodotti per mostrare tutti gli elementi dell'inventario nella conversazione Business Messages tramite un unico carosello di schede interattive.

Per visualizzare il catalogo dei prodotti, creeremo una risposta suggerita con testo "Mostra menu" e postbackData "show-product-catalog". Quando gli utenti toccano la risposta suggerita e la tua applicazione web riceve i dati dei postback, invieremo il carosello di schede interattive. Aggiungiamo una nuova costante per questa risposta suggerita nella parte superiore di views.py.

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

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

Da qui analizziamo il messaggio e lo indirizziamo a una nuova funzione che invia un carosello di schede interattive contenente il catalogo dei prodotti. Prima estendi la funzione route_message per chiamare una funzione "send_product_catalog" quando viene toccato la risposta suggerita, quindi definiremo la funzione.

Nello snippet seguente, aggiungi una condizione aggiuntiva all'istruzione if nella funzione route_message per verificare se normalized_message è uguale alla costante definita in precedenza, CMD_SHOW_PRODUCT_CATALOG.

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)
...

Assicurati di completare il flusso e definire send_product_catalog. send_product_catalog chiama get_menu_carousel,, che genera il carosello di schede interattive dal file dell'inventario letto in precedenza.

Le definizioni delle funzioni possono essere inserite ovunque in views.py. Tieni presente che lo snippet seguente utilizza due nuove costanti da aggiungere all'inizio del file.

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)
...

Se esamini la creazione degli elementi del carosello, creiamo anche un'istanza della classe BusinessMessagesSuggestion. Ogni suggerimento rappresenta una selezione dell'utente per un prodotto nel carosello. Quando un utente tocca la risposta suggerita, Business Messages invia al tuo webhook il postbackData, contenente il codice JSON, che descrive l'articolo e l'azione che l'utente vuole eseguire (aggiungere o rimuovere dal carrello). Nella sezione seguente, analizzeremo i messaggi di questo tipo in modo da poter aggiungere effettivamente l'articolo al carrello.

Ora che abbiamo apportato queste modifiche, esegui il deployment dell'applicazione web in Google App Engine e proviamo l'esperienza.

$ gcloud app deploy

Una volta caricata la piattaforma conversazionale sul tuo dispositivo mobile, invia il messaggio "show-product-catalog" e dovresti visualizzare un carosello di prodotti simili a questo.

4639da46bcc5230c.png

Se tocchi Aggiungi elemento, in realtà non accade nulla, tranne che l'agente esegue l'eco dei dati postback dalla risposta suggerita. Nella sezione successiva, utilizzeremo il catalogo dei prodotti e lo utilizzeremo per creare il carrello degli acquisti in cui aggiungere l'articolo.

Il catalogo dei prodotti che hai appena creato può essere esteso in diversi modi. Potresti avere diverse opzioni per il menù delle bevande o opzioni vegetariane. L'utilizzo di caroselli o chip di suggerimenti è un ottimo modo per consentire agli utenti di visualizzare in dettaglio le opzioni di menu per arrivare all'insieme di prodotti che stanno cercando. Come estensione di questo codelab, prova a estendere il sistema del catalogo dei prodotti in modo che un utente possa visualizzare le bevande separatamente dagli alimenti nel menu o persino specificare le opzioni vegetariane.

4. Il carrello degli acquisti

In questa sezione del codelab, creeremo la funzionalità del carrello degli acquisti a partire dalla sezione precedente, in modo da poter sfogliare i prodotti disponibili.

Tra le molte cose, la chiave dell'esperienza del carrello degli acquisti consente agli utenti di aggiungere e rimuovere articoli dal carrello, monitorare il numero di ogni articolo nel carrello ed esaminare gli articoli nel carrello.

Tenere traccia dello stato del carrello degli acquisti significa che dobbiamo conservare i dati nell'applicazione web. Per semplificare la sperimentazione e il deployment, utilizzeremo Google Datastore in Google Cloud Platform per rendere i dati persistenti. L'ID conversazione rimane costante tra un utente e l'attività, quindi possiamo utilizzarlo per associare gli utenti agli articoli del carrello degli acquisti.

Iniziamo connettendoci a Google Datastore e ripristinando l'ID conversazione quando lo vediamo.

Connessione a Datastore

Ci metteremo in contatto con Google Datastore ogni volta che viene eseguita un'interazione con il carrello, ad esempio, quando un utente aggiunge o elimina un articolo. Per ulteriori informazioni sull'utilizzo di questa libreria client per interagire con Google Datastore, consulta la documentazione ufficiale.

Lo snippet seguente definisce una funzione per aggiornare il carrello degli acquisti. La funzione accetta il seguente input: conversation_id e message. message contiene JSON che descrive l'azione che l'utente vuole eseguire e che è già integrato nel carosello che mostra il catalogo dei prodotti. La funzione crea un client Google Datastore e recupera immediatamente un'entità ShoppingCart, dove la chiave è l'ID conversazione.

Copia la seguente funzione nel file views.py. Continueremo ad ampliare questa funzionalità nella prossima sezione.

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)

Estendiamo questa funzione per aggiungere un articolo al carrello.

Aggiungere articoli al carrello

Quando l'utente tocca un'azione suggerita Aggiungi elemento dal carosello di prodotti, il postbackData contiene JSON che descrive l'azione che l'utente vuole eseguire. Il dizionario JSON ha due chiavi, "action" e "item_name", mentre il dizionario JSON viene inviato al tuo webhook. Il campo "item_name" è l'identificatore univoco associato all'elemento nel file Inventory.json.

Dopo aver analizzato il comando del carrello e l'articolo del carrello dal messaggio, possiamo scrivere dichiarazioni condizionali per aggiungere l'articolo. Alcuni casi limite da considerare sono il caso in cui Datastore non abbia mai rilevato l'ID conversazione o se il carrello degli acquisti riceve questo articolo per la prima volta. Di seguito è riportata un'estensione della funzionalità update_shopping_cart definita sopra. Questa modifica aggiunge al carrello degli acquisti un articolo che è memorizzato in modo permanente da Google Datastore.

Lo snippet riportato di seguito è un'estensione della funzione precedentemente aggiunta al tuo view.py. Puoi aggiungere la differenza oppure copiare lo snippet e sostituire la versione esistente della funzione update_shopping_cart.

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)

Questa funzione verrà estesa in seguito per supportare lo scenario in cui cart_cmd contiene la stringa "del-item" definita in CMD_DEL_ITEM.

Integrazione

Assicurati di aggiungere gli impianti idraulici nella funzione route_message in modo che se ricevi un messaggio che ti chiede di aggiungere al carrello un articolo che viene chiamata la funzione update_shopping_cart. Dovrai inoltre definire una costante per l'aggiunta di elementi seguendo la convenzione che usiamo in tutto il codelab.

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)

...

Per il momento, è possibile aggiungere articoli al carrello degli acquisti. Se esegui il deployment delle modifiche in Google App Engine, dovresti riuscire a vedere le modifiche del carrello acquisti riflesse nella dashboard di Google Datastore nella console di Google Cloud. Guarda il seguente screenshot della console Google Datastore. c'è una singola entità che ha il nome dell'ID conversazione seguito da alcune relazioni con gli elementi dell'inventario e la quantità di questi articoli presenti nel carrello.

619dc18a8136ea69.png

Nella sezione successiva, creeremo un modo per elencare gli articoli nel carrello degli acquisti. Il meccanismo di verifica del carrello degli acquisti dovrebbe mostrare tutti gli articoli presenti nel carrello, la quantità di questi articoli e un'opzione per rimuovere un articolo dal carrello.

Revisione degli articoli nel carrello

L'elenco degli articoli nel carrello è l'unico modo che ci permette di conoscere lo stato del carrello e di sapere quali articoli possiamo rimuovere.

Innanzitutto, invia un messaggio amichevole come "Ecco il tuo carrello degli acquisti:", seguito da un altro messaggio contenente un carosello di schede interattive con risposte suggerite associate per "Rimuovi uno" o "Aggiungi un". Il carosello delle schede interattive dovrebbe mostrare anche la quantità di articoli salvati nel carrello.

Prima di scrivere la nostra funzione, è importante essere consapevoli: se è presente un solo tipo di articolo nel carrello, non possiamo renderlo come un carosello. I caroselli di schede interattive devono contenere almeno due schede. Se, invece, non ci sono articoli nel carrello, vogliamo mostrare un semplice messaggio che indica che il carrello è vuoto.

Alla luce di questo, definiamo una funzione denominata send_shopping_cart. Questa funzione si connette a Google Datastore e richiede un'entità ShoppingCart in base all'ID conversazione. Successivamente, chiameremo la funzione get_inventory_data e utilizzeremo un carosello di schede interattive per segnalare lo stato del carrello degli acquisti. Avremo bisogno anche di ottenere l'ID di un prodotto per nome e di dichiarare una funzione per esaminare Google Datastore per determinare questo valore. Durante la produzione del carosello, possiamo associare le risposte suggerite per eliminare o aggiungere articoli in base all'ID prodotto. Lo snippet riportato di seguito esegue tutte queste operazioni. Copia il codice ovunque in views.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)

...

Assicurati di aver già definito CMD_SHOW_CART nella parte superiore di views.py e chiama send_shopping_cart se l'utente invia un messaggio contenente "show-cart".

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

In base alla logica introdotta nella funzione send_shopping_cart, quando digiti "show-cart", viene visualizzato un messaggio che indica che il carrello non contiene articoli, una scheda informativa in cui viene mostrato l'unico articolo presente nel carrello oppure un carosello di schede con più articoli. Inoltre, abbiamo tre risposte suggerite: "Vedi il prezzo totale", "Svuota il carrello" e "Vedi il menu".

Prova a implementare le modifiche al codice riportate sopra per verificare che il carrello degli acquisti stia monitorando gli articoli che aggiungi e che tu possa esaminare il carrello dalla piattaforma delle conversazioni, come mostrato negli screenshot qui sopra. Puoi eseguire il deployment delle modifiche con questo comando eseguito dalla directory p step-2 in cui vuoi aggiungere le modifiche.

$ gcloud app deploy

Creeremo la funzione "Visualizza prezzo totale" nella sezione successiva, dopo aver sviluppato la funzionalità per rimuovere un articolo dal carrello. La funzione get_cart_price avrà un comportamento simile a quello della funzionalità "Visualizza il carrello degli acquisti", nel senso che eseguirà un controllo incrociato dei dati in Datastore con il file Inventory.json per produrre un prezzo totale per il carrello degli acquisti. Questa soluzione sarà utile per la prossima parte del codelab in cui eseguiremo l'integrazione con i pagamenti.

Rimozione di articoli dal carrello

Infine, possiamo completare il comportamento del carrello degli acquisti introducendo la funzionalità per la rimozione del carrello. Sostituisci la funzione update_shopping_cart esistente con il seguente 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)

Invio di un messaggio di conferma

Quando l'utente aggiunge un articolo al carrello, devi inviare un messaggio di conferma per confermare l'azione e l'elaborazione della richiesta. In questo modo, non solo crei aspettative, ma permette anche di mantenere viva la conversazione.

Estendiamo la funzione update_shopping_cart in modo che invii un messaggio all'ID conversazione indicante che l'articolo è stato aggiunto o rimosso e che fornisca suggerimenti per controllare il carrello degli acquisti o visualizzare di nuovo il menu.

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

Questo dovrebbe bastare! Un'esperienza del carrello completa che consente a un utente di aggiungere e rimuovere articoli, nonché di recensire gli articoli nel carrello.

A questo punto, se vuoi vedere la funzionalità del carrello nella conversazione Business Messages, esegui il deployment dell'applicazione per interagire con il tuo agente. A tale scopo, esegui questo comando nella directory del passaggio 2.

$ gcloud app deploy

5. Preparazione per i pagamenti

In vista dell'integrazione con un elaboratore dei pagamenti nella prossima parte della serie, abbiamo bisogno di un modo per ottenere il prezzo del carrello degli acquisti. Creiamo una funzione che recupera il prezzo per noi eseguendo un controllo incrociato dei dati del carrello degli acquisti in Google Datastore, recuperando il prezzo di ogni articolo dall'inventario e moltiplicando il prezzo per la quantità di ogni articolo nel carrello.

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

...

Infine, possiamo utilizzare questa funzione e inviare un messaggio all'utente.

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)
...

Per unire il tutto, aggiorniamo la funzione route_message e la costante per attivare la logica riportata sopra.

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)
...

Di seguito sono riportati alcuni screenshot che illustrano gli obiettivi della logica descritta sopra:

8feacf94ed0ac6c4.png

Quando saremo pronti per l'integrazione con l'elaboratore dei pagamenti nella prossima parte del codelab, chiameremo la funzione get_cart_price per passare i dati all'elaboratore dei pagamenti e avviare il flusso di pagamento.

Anche in questo caso, puoi provare questa funzionalità del carrello nella conversazione Business Messages eseguendo il deployment dell'applicazione e interagendo con il tuo agente.

$ gcloud app deploy

6. Congratulazioni

Congratulazioni, hai creato un'esperienza con il carrello degli acquisti in Business Messages.

Un aspetto che non abbiamo esaminato in questo codelab è la funzionalità che consente di svuotare l'intero carrello degli acquisti. Se vuoi, prova a estendere l'applicazione per soddisfare la funzionalità "Svuota il carrello". La soluzione è disponibile al passaggio 3 del codice sorgente che hai clonato.

In una delle prossime sezioni, eseguiremo l'integrazione con un elaboratore dei pagamenti esterno per consentire ai tuoi utenti di completare una transazione di pagamento con il tuo brand.

Cosa caratterizza un buon carrello degli acquisti?

Una buona esperienza con il carrello degli acquisti in una conversazione non è diversa da un'app mobile o in un negozio fisico. La possibilità di aggiungere e rimuovere articoli e calcolare il prezzo del carrello sono solo alcune delle funzionalità che abbiamo esplorato in questo codelab. Una differenza rispetto a un carrello degli acquisti reale è la possibilità di vedere il prezzo di tutti gli articoli nel carrello in un determinato momento, man mano che li aggiungi o li rimuovi. Questo tipo di funzionalità di alto valore metterà in risalto la tua esperienza di commercio conversazionale.

Passaggi successivi

Quando è tutto pronto, consulta alcuni dei seguenti argomenti per scoprire di più sulle interazioni più complesse che puoi ottenere in Business Messages:

Documenti di riferimento