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

Creare un carrello degli acquisti su Business Messages

Questo è il secondo codelab di una serie volta a creare un percorso dell'utente Acquista online e ritira in negozio. In molti percorsi di e-commerce, il carrello è fondamentale per la conversione degli utenti in clienti paganti. Il carrello è anche un modo per comprendere meglio i tuoi clienti e offrire suggerimenti su altri articoli che potrebbero interessarli. In questo codelab ci concentreremo sulla creazione dell'esperienza del carrello e sul deployment dell'applicazione in Google App Engine.

Quali sono le caratteristiche di un buon carrello?

I carrelli sono fondamentali per un'esperienza di acquisto online positiva. Come si è scoperto, Business Messages non è utile solo per facilitare le domande e risposte su un prodotto con un potenziale cliente, ma può facilitare l'intera esperienza di acquisto fino al completamento di un pagamento all'interno della conversazione.

9d17537b980d0e62.png

Oltre a un buon carrello, una buona esperienza di acquisto consente agli utenti di sfogliare gli articoli per categoria e all'attività di consigliare altri prodotti a cui l'acquirente potrebbe essere interessato. Dopo aver aggiunto altri articoli al carrello, l'utente può rivedere l'intero carrello e rimuovere o aggiungere altri articoli prima di completare il pagamento.

Cosa creerai

In questa sezione della serie di codelab, estenderai 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 aggiungerli a un carrello.

In questo codelab, la tua app:

  • Mostrare un catalogo di domande in Business Messages
  • Suggerire articoli che potrebbero interessare agli utenti
  • Esamina i contenuti del carrello 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
  • Come utilizzare un meccanismo di archiviazione permanente per salvare lo stato di un carrello

Questo codelab si concentra sull'estensione dell'agente digitale della prima parte di questa serie di codelab.

Che cosa ti serve

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 per l'attivazione delle API Business Messages e Business Communications, la creazione di chiavi del service account, il deployment di un'applicazione o la configurazione del webhook nella console Business Communications. Detto questo, cloneremo un'applicazione di esempio per assicurarci che la tua applicazione sia coerente con ciò che stiamo creando e attiveremo l'API per Datastore su Google Cloud Platform per poter archiviare i dati relativi al carrello.

Clonazione dell'applicazione da GitHub

In un terminale, clona l'esempio di bot di eco Django nella directory di lavoro del tuo 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 del campione 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

Attiva l'API Google Datastore

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

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

  1. Apri l'API Google Datastore in Google Cloud Console.
  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 step-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 progetto del progetto che hai utilizzato per registrarti alle 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 che 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 prima parte del codelab. Se non l'hai ancora fatto, configura il webhook.

L'applicazione risponderà ad alcune semplici richieste, ad esempio quando un utente chiede gli orari di apertura di Bonjour Meal. Devi testare questa funzionalità sul tuo dispositivo mobile tramite gli URL di test che puoi recuperare dalle informazioni dell'agente nella Business Communications Console. Gli URL di test avvieranno l'esperienza Business Messages sul tuo dispositivo mobile e potrai iniziare a interagire con il tuo agente.

3. Il catalogo dei prodotti

Un sistema di inventario

Nella maggior parte dei casi, puoi eseguire l'integrazione direttamente 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, ma utilizzare 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 a un utente di sfogliare gli articoli disponibili da aggiungere a un 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.

Lettura dal nostro inventario

L'inventario è un file statico chiamato "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 mostrarli nella conversazione. Creiamo una funzione che legge i dati dal file JSON e restituisce 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
...

In questo modo dovremmo ottenere ciò che ci serve per leggere i dati dell'inventario. Ora abbiamo bisogno di un modo per mostrare queste informazioni sul prodotto nella conversazione.

Visualizzazione del catalogo dei prodotti

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

Per visualizzare il catalogo dei prodotti, creeremo una risposta suggerita con il testo "Mostra menu" e postbackData "show-product-catalog". Quando gli utenti toccano la risposta suggerita e la tua applicazione web riceve i dati di postback, invieremo il carosello di schede avanzate. 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 avanzate contenente il catalogo dei prodotti. Innanzitutto, estendi la funzione route_message per chiamare una funzione "send_product_catalog" quando viene toccata la risposta suggerita, quindi definiremo la funzione.

Nello snippet seguente, aggiungi un'ulteriore condizione 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)
...

Assicuriamoci 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 di inventario che abbiamo letto in precedenza.

Le definizioni delle funzioni possono essere inserite ovunque in views.py. Tieni presente che il seguente snippet utilizza due nuove costanti che devono essere aggiunte 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 la selezione di un utente per un prodotto nel carosello. Quando un utente tocca la risposta suggerita, Business Messages invia al webhook postbackData contenente il JSON che descrive l'articolo e l'azione che l'utente vuole intraprendere (aggiungere o rimuovere dal carrello). Nella sezione seguente, analizzeremo i messaggi di questo tipo per poter aggiungere l'articolo al carrello.

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

$ gcloud app deploy

Quando la superficie conversazionale è caricata sul tuo dispositivo mobile, invia il messaggio "show-product-catalog" (mostra catalogo prodotti) e dovresti visualizzare un carosello di prodotti simile a questo.

4639da46bcc5230c.png

Se tocchi Aggiungi elemento, non succede nulla, tranne che l'agente ripete i dati di postback della risposta suggerita. Nella sezione successiva, utilizzeremo il catalogo prodotti per creare il carrello in cui verrà aggiunto l'articolo.

Il catalogo dei prodotti che hai appena creato può essere esteso in vari 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 esplorare in dettaglio le opzioni di menu per arrivare a un insieme di prodotti che stanno cercando. Come estensione di questo codelab, prova a estendere il sistema di catalogo dei prodotti in modo che un utente possa visualizzare le bevande separatamente dal cibo nel menu o persino specificare opzioni vegetariane.

4. Il carrello degli acquisti

In questa sezione del codelab, creeremo la funzionalità del carrello degli acquisti partendo dalla sezione precedente, che ci consente di sfogliare i prodotti disponibili.

Tra le tante cose, l'esperienza principale del carrello consente agli utenti di aggiungere articoli al carrello, rimuoverli, tenere traccia del numero di ogni articolo nel carrello e rivedere gli articoli nel carrello.

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

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

Connessione a Datastore

Ci connetteremo a Google Datastore ogni volta che viene eseguita un'interazione con il carrello degli acquisti, ad esempio quando un utente aggiunge o elimina un articolo. Puoi scoprire di più sull'utilizzo di questa libreria client per interagire con Google Datastore nella documentazione ufficiale.

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

Copia la seguente funzione nel file views.py. Continueremo a espandere questo argomento 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.

Aggiunta di articoli al carrello

Quando l'utente tocca un'azione suggerita Aggiungi elemento dal carosello dei prodotti, postbackData contiene JSON che descrive l'azione che l'utente vuole intraprendere. Il dizionario JSON ha due chiavi, "action" e "item_name", e viene inviato al webhook. Il campo "item_name" è l'identificatore univoco associato all'articolo in inventory.json.

Una volta analizzati il comando del carrello e l'articolo del carrello dal messaggio, possiamo scrivere istruzioni condizionali per aggiungere l'articolo. Alcuni casi limite da considerare sono se Datastore non ha mai visto l'ID conversazione o se il carrello riceve questo articolo per la prima volta. Di seguito è riportata un'estensione della funzionalità update_shopping_cart definita sopra. Questa modifica aggiunge un articolo al carrello degli acquisti, che viene reso persistente da Google Datastore.

Il seguente snippet è un'estensione della funzione precedente aggiunta a views.py. Puoi aggiungere la differenza o 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 un secondo momento per supportare lo scenario in cui cart_cmd contiene la stringa "del-item" definita in CMD_DEL_ITEM.

Mettere tutto insieme

Assicurati di aggiungere l'idraulica nella funzione route_message in modo che, se ricevi un messaggio per aggiungere un articolo al carrello, venga chiamata la funzione update_shopping_cart. Dovrai anche definire una costante per aggiungere elementi utilizzando la convenzione che utilizziamo 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 ora, possiamo aggiungere articoli al carrello degli acquisti. Se implementi le modifiche in Google App Engine, dovresti vedere le modifiche al carrello riflesse nella dashboard di Google Datastore nella console GCP. Nello screenshot seguente della console Google Datastore è presente una singola entità denominata in base all'ID conversazione, seguita da alcune relazioni con gli articoli di inventario e dalla quantità di questi articoli nel carrello.

619dc18a8136ea69.png

Nella sezione successiva, creeremo un modo per elencare gli articoli nel carrello. Il meccanismo di revisione del carrello deve mostrare tutti gli articoli 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 per comprendere lo stato del carrello e sapere quali articoli possiamo rimuovere.

Innanzitutto, inviamo 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 un articolo" o "Aggiungi un articolo". Il carosello di schede avanzate deve inoltre elencare la quantità di articoli salvati nel carrello.

Prima di scrivere la funzione, tieni presente che se nel carrello degli acquisti è presente un solo tipo di articolo, non possiamo visualizzarlo come carosello. I caroselli di schede avanzate devono contenere almeno due schede. Al contrario, se non ci sono articoli nel carrello, vogliamo mostrare un semplice messaggio che indica che il carrello è vuoto.

Tenendo a mente questo, definiamo una funzione chiamata send_shopping_cart. Questa funzione si connette a Google Datastore e richiede un'entità ShoppingCart in base all'ID conversazione. Una volta ottenuto, chiameremo la funzione get_inventory_data e utilizzeremo un carosello di schede interattive per segnalare lo stato del carrello. Dovremo anche ottenere l'ID di un prodotto in base al nome e possiamo dichiarare una funzione per esaminare Google Datastore per determinare questo valore. Durante la creazione del carosello, possiamo associare risposte suggerite per eliminare elementi o aggiungerne in base all'ID prodotto. Lo snippet riportato di seguito esegue tutte queste operazioni. Copia il codice in un punto qualsiasi di 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", riceverai un messaggio che indica che non c'è nulla nel carrello, una scheda avanzata che mostra l'unico articolo nel carrello o un carosello di schede che mostrano più articoli. Inoltre, abbiamo tre risposte suggerite: "Vedi prezzo totale", "Svuota il carrello" e "Vedi il menu".

Prova a implementare le modifiche al codice riportate sopra per verificare che il carrello tenga traccia degli articoli che aggiungi e che tu possa rivedere il carrello dalla superficie delle conversazioni, come mostrato negli screenshot precedenti. Puoi eseguire il deployment delle modifiche con questo comando eseguito dalla directory step-2 in cui stai aggiungendo le modifiche.

$ gcloud app deploy

Creeremo la funzionalità "Visualizza prezzo totale" nella sezione successiva dopo aver creato la funzionalità per rimuovere un articolo dal carrello. La funzione get_cart_price si comporterà in modo simile alla funzionalità "Visualizza carrello", in quanto farà riferimento incrociato ai dati in Datastore con il file inventory.json per produrre un prezzo totale per il carrello. Sarà utile per la parte successiva del codelab, in cui eseguiremo l'integrazione con i pagamenti.

Rimozione di articoli dal carrello

Infine, possiamo completare il comportamento del carrello introducendo la funzionalità per rimuoverlo. 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 che attesti la sua azione e che hai elaborato la sua richiesta. In questo modo, non solo si creano le aspettative giuste, ma si mantiene anche viva la conversazione.

Estendiamo la funzione update_shopping_cart in modo che invii un messaggio all'ID conversazione indicando che l'articolo è stato aggiunto o rimosso e fornendo suggerimenti per rivedere il carrello 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

Dovrebbe funzionare. Un'esperienza di carrello completa che consente a un utente di aggiungere, rimuovere e rivedere gli articoli nel carrello.

A questo punto, se vuoi visualizzare la funzionalità del carrello nella conversazione Business Messages, implementa l'applicazione per interagire con l'agente. Per farlo, esegui questo comando nella directory step-2.

$ gcloud app deploy

5. Preparazione dei pagamenti

In preparazione all'integrazione con un processore di pagamento nella prossima parte della serie, abbiamo bisogno di un modo per ottenere il prezzo del carrello. Creiamo una funzione che recuperi il prezzo per noi facendo riferimento incrociato ai dati del carrello 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 precedente.

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

Ecco alcuni screenshot per mostrare cosa ottiene la logica precedente:

8feacf94ed0ac6c4.png

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

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

$ gcloud app deploy

6. Complimenti

Congratulazioni, hai creato un'esperienza di carrello all'interno di Business Messages.

Una funzionalità che non abbiamo trattato in questo codelab è quella di svuotare l'intero carrello. Se vuoi, prova a estendere l'applicazione per implementare la funzionalità "Svuota il carrello". La soluzione è disponibile nel passaggio 3 del codice sorgente che hai clonato.

In una sezione futura, eseguiremo l'integrazione con un processore di pagamento esterno per consentire ai tuoi utenti di completare una transazione di pagamento con il tuo brand.

Quali sono le caratteristiche di un buon carrello?

Una buona esperienza con il carrello in una conversazione non è diversa da quella di un'app mobile o di un negozio fisico. La possibilità di aggiungere e rimuovere articoli e di calcolare il prezzo del carrello sono solo alcune delle funzionalità che abbiamo esplorato in questo codelab. A differenza di un carrello degli acquisti del mondo reale, puoi vedere il prezzo di tutti gli articoli nel carrello in qualsiasi momento, mentre aggiungi o rimuovi articoli. Questi tipi di funzionalità di alto valore faranno risaltare la tua esperienza di commercio conversazionale.

Passaggi successivi

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

Documentazione di riferimento