Kup online z odbiorem w sklepie: Bonjour Meal – część 2. Tworzenie koszyka zakupów
Informacje o tym ćwiczeniu (w Codelabs)
1. Wprowadzenie
Ostatnia aktualizacja: 30 października 2020 r.
Tworzenie koszyka na zakupy w ramach funkcji Business Messages
To drugi z cyklu samouczków, których celem jest stworzenie ścieżki użytkownika „Kup online, odbierz w sklepie”. W wielu ścieżkach e-commerce koszyk jest kluczowy dla skutecznego przekształcania użytkowników w płacących klientów. Koszyk to też sposób na lepsze poznanie klientów i proponowanie im innych produktów, którymi mogą być zainteresowani. W tym ćwiczeniu skupimy się na tworzeniu koszyka i wdrażaniu aplikacji w Google App Engine.
Co sprawia, że koszyk jest dobry?
Kosze na zakupy są kluczowe dla udanego procesu zakupów online. Jak się okazuje, wiadomości biznesowe nie tylko ułatwiają zadawanie pytań i odpowiedzi na nie w rozmowie z potencjalnym klientem, ale też mogą ułatwić cały proces zakupów, aż do dokonania płatności w ramach rozmowy.
Oprócz dobrego koszyka zakupowego, dobre wrażenia z zakupów obejmują możliwość przeglądania produktów według kategorii i rekomendowanie przez firmę innych produktów, którymi kupujący może być zainteresowany. Po dodaniu kolejnych produktów do koszyka użytkownik może przejrzeć cały koszyk i usunąć z niego produkty lub dodać kolejne przed dokonaniem płatności.
Co utworzysz
W tej części serii Codelab rozszerzysz agenta cyfrowego utworzonego w części 1 dla fikcyjnej firmy Bonjour Meal, aby użytkownicy mogli przeglądać katalog produktów i dodawać je do koszyka.
W tym ćwiczeniu z programowania Twoja aplikacja:
- wyświetlać katalog pytań w usłudze Business Messages;
- sugerować produkty, które mogą zainteresować użytkowników;
- Sprawdź zawartość koszyka i utwórz podsumowanie ceny
Czego się nauczysz
- Jak wdrożyć aplikację internetową w App Engine w Google Cloud Platform
- Jak za pomocą trwałego mechanizmu przechowywania zapisać stan koszyka
To ćwiczenie z programowania koncentruje się na rozszerzeniu agenta cyfrowego z części 1 tej serii ćwiczeń z programowania.
Czego potrzebujesz
- projekt Google Cloud Platform zarejestrowany i zatwierdzony do użytku z Wiadomościami Biznesowymi.
- Więcej informacji znajdziesz w naszej witrynie dla deweloperów.
- plik danych logowania na koncie usługi w formacie JSON wygenerowany dla projektu GCP.
- Urządzenie z Androidem w wersji 5.0 lub nowszej LUB urządzenie z iOS z aplikacją Mapy Google
- doświadczenie w programowaniu aplikacji internetowych;
- połączenie z internetem.
2. Konfiguracja
W tym ćwiczeniu zakładamy, że utworzyłeś/utworzyłaś pierwszego agenta i ukończyłeś/ukończyłaś część 1 ćwiczenia z kodem. Dlatego nie będziemy omawiać podstaw korzystania z interfejsów Business Messages API i Business Communications API, tworzenia kluczy kont usług, wdrażania aplikacji ani konfigurowania webhooka w konsoli Business Communications. W związku z tym sklonujemy przykładową aplikację, aby mieć pewność, że Twoja aplikacja jest zgodna z tym, co budujemy, i włączymy interfejs API Datastore na platformie Google Cloud Platform, aby umożliwić przechowywanie danych dotyczących koszyka.
Klonowanie aplikacji z GitHuba
W terminalu sklonuj przykładowy bot Echo w Django do katalogu roboczego projektu za pomocą tego polecenia:
$ git clone https://github.com/google-business-communications/bm-bonjour-meal-django-starter-code
Skopiuj plik danych logowania w formacie JSON utworzony dla konta usługi do folderu zasobów przykładu i nazwij go „bm-agent-service-account-credentials.json”.
bm-bonjour-meal-django-starter-code/bonjourmeal-codelab/step-2/resources/bm-agent-service-account-credentials.json
Włącz interfejs Google Datastore API
W części 1 tego ćwiczenia z programowania włączyliśmy interfejsy API Business Messages, Business Communications i Cloud Build.
W tym ćwiczeniu będziemy pracować z Google Datastore, więc musimy też włączyć ten interfejs API:
- W konsoli Google Cloud otwórz interfejs Google Datastore API.
- Upewnij się, że pracujesz nad właściwym projektem GCP.
- Kliknij Włącz.
Wdrażanie przykładowej aplikacji
W terminalu przejdź do katalogu step-2 przykładu.
Aby wdrożyć przykład, uruchom w terminalu te polecenia:
$ gcloud config set project PROJECT_ID*
$ gcloud app deploy
- PROJECT_ID to identyfikator projektu, który został użyty do zarejestrowania interfejsów API.
Zanotuj adres URL zaimplementowanej aplikacji w wyjściu ostatniego polecenia:
Deployed service [default] to [https://PROJECT_ID.appspot.com]
Kod, który właśnie wdrożyłeś, zawiera aplikację internetową z webhookiem do odbierania wiadomości z Business Messages. Zawiera ono wszystko, co zrobiliśmy w części 1 tego CodeLab. Jeśli jeszcze tego nie zrobiono, skonfiguruj webhook.
Aplikacja będzie odpowiadać na proste pytania, np. o godziny otwarcia Bonjour Meal. Należy przetestować to na urządzeniu mobilnym, korzystając z testowych adresów URL, które można pobrać z sekcji Informacje o agencie w konsoli usług komunikacji biznesowej. Testowe adresy URL otworzą na Twoim urządzeniu mobilnym środowisko Business Messages, w którym możesz rozpocząć interakcję z agentem.
3. Katalog produktów
systemu zarządzania asortymentem,
W większości przypadków możesz zintegrować się bezpośrednio z asortymentem marki za pomocą wewnętrznego interfejsu API. W innych przypadkach możesz wyodrębnić stronę internetową lub stworzyć własny system śledzenia zasobów. Naszym celem nie jest tworzenie systemu asortymentu. Użyjemy prostego pliku statycznego zawierającego zdjęcia i informacje o produktach dla naszego agenta. W tej sekcji pobieramy informacje z tego pliku stałego, wyświetlamy je w rozmowie i pozwalamy użytkownikowi przeglądać produkty, które można dodać do koszyka.
Plik asortymentu statycznego wygląda tak:
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
}
]
}
Uruchom aplikację Python, aby odczytać ten plik.
Czytanie z naszego asortymentu
Zasoby to plik statyczny o nazwie „inventory.json” znajdujący się w katalogu ./resources. Musimy dodać do pliku views.py trochę kodu Pythona, aby odczytać zawartość pliku JSON, a potem wyświetlić ją w rozmowie. Utwórzmy funkcję, która odczytuje dane z pliku JSON i zwróci listę dostępnych produktów.
Definicję tej funkcji można umieścić w dowolnym miejscu w pliku views.py.
bonjourmeal-codelab/step-2/bopis/views.py
...
def get_inventory_data():
f = open(INVENTORY_FILE)
inventory = json.load(f)
return inventory
...
Dzięki temu będziemy mieć wszystko, czego potrzebujemy do odczytania danych z zasobow. Teraz potrzebujemy sposobu na wyświetlanie tych informacji o produkcie w ramach rozmowy.
Wyświetlanie katalogu produktów
W tym samouczku w celu uproszczenia mamy ogólny katalog produktów, który wyświetla wszystkie elementy asortymentu w konwersacji w usłudze Business Messages za pomocą pojedynczego karuzela z bogatymi kartami.
Aby wyświetlić katalog produktów, utworzymy sugerowaną odpowiedź z tekstem „Pokaż menu” i danymi zwrotnymi „show-product-catalog
”. Gdy użytkownicy klikną sugerowaną odpowiedź, a Twoja aplikacja internetowa otrzyma dane zwrotne, wyślemy karuzel z kartami rozszerzonymi. Dodajmy nową stałą dla tej sugerowanej odpowiedzi u góry pliku views.py.
bonjourmeal-codelab/step-2/bopis/views.py
...
CMD_SHOW_PRODUCT_CATALOG = 'show-product-catalog'
...
Następnie analizujemy wiadomość i przesyłamy ją do nowej funkcji, która wysyła karuzel z elementami rozszerzonymi zawierający katalog produktów. Najpierw rozszerz funkcję route_message
, aby wywoływała funkcję „send_product_catalog
” po kliknięciu sugerowanej odpowiedzi. Następnie zdefiniujemy tę funkcję.
W tym fragmencie kodu dodaj do instrukcji if w funkcji route_message
dodatkowy warunek, aby sprawdzić, czy normalized_message
jest równe zdefiniowanej wcześniej stałej 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)
...
Sprawdźmy, czy proces został ukończony i czy masz zdefiniowane send_product_catalog
. send_product_catalog
wywołuje get_menu_carousel,
, który generuje karuzelę kart rozszerzonych na podstawie pliku asortymentu odczytanego wcześniej.
Definicje funkcji można umieścić w dowolnym miejscu w pliku views.py. Pamiętaj, że poniższy fragment kodu korzysta z 2 nowych stałych, które należy dodać na początku pliku.
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)
...
Jeśli przyjrzysz się tworzeniu elementów karuzeli, zobaczysz, że tworzymy też instancję klasy BusinessMessagesSuggestion
. Każda sugestia reprezentuje wybór użytkownika produktu na karuzeli. Gdy użytkownik kliknie sugerowaną odpowiedź, wiadomości biznesowe wyślą na Twój adres webhook dane zwrotne zawierające dane JSON opisujące produkt i działanie, które użytkownik chce wykonać (dodanie do koszyka lub usunięcie z niego). W sekcji poniżej przeanalizujemy wiadomości o takiej treści, aby móc dodać produkt do koszyka.
Po wprowadzeniu tych zmian wdrożymy aplikację internetową w Google App Engine i przetestujemy ją.
$ gcloud app deploy
Gdy na urządzeniu mobilnym wczytasz interfejs konwersacyjny, wyślij wiadomość „show-product-catalog” (pokaż katalog produktów). Powinien wtedy pojawić się karuzel produktów, który wygląda tak jak na poniższym obrazku.
Jeśli klikniesz Dodaj element, nic się nie stanie, ale agent powtórzy dane zwrotne z sugerowanej odpowiedzi. W następnej sekcji użyjemy katalogu produktów, aby utworzyć koszyk, do którego zostanie dodany produkt.
Katalog produktów, który właśnie utworzysz, można rozszerzyć na wiele sposobów. Możesz mieć inne opcje menu napojów lub opcje wegetariańskie. Karuzele lub elementy z propozycjami to świetny sposób na umożliwienie użytkownikom przechodzenia przez opcje menu w celu znalezienia produktów, których szukają. W rozszerzeniu tego Codelab spróbuj rozszerzyć system katalogu produktów, aby użytkownik mógł wyświetlać napoje oddzielnie od jedzenia w menu lub nawet wybierać opcje wegańskie.
4. Koszyk
W tej części tego ćwiczenia w Codelab rozszerzymy funkcję koszyka, która umożliwia przeglądanie dostępnych produktów, o funkcje z poprzedniej sekcji.
W ramach koszyka użytkownicy mogą m.in. dodawać produkty do koszyka, usuwać je z koszyka, śledzić liczbę produktów w koszyku i je przeglądać.
Śledzenie stanu koszyka wymaga przechowywania danych w aplikacji internetowej. Aby ułatwić eksperymentowanie i wdrażanie, użyjemy usługi Google Datastore w Google Cloud Platform do trwałego przechowywania danych. Identyfikator rozmowy pozostaje niezmienny w relacji między użytkownikiem a firmą, dzięki czemu możemy łączyć użytkowników z produktami w koszyku.
Najpierw połączmy się z Google Datastore i zapiszemy identyfikator rozmowy, gdy tylko się pojawi.
Łączenie z Datastore
Nawiązujemy połączenie z Google Datastore za każdym razem, gdy użytkownik wchodzi w interakcję z koszykiem, np. gdy dodaje lub usuwa produkt. Więcej informacji o korzystaniu z tej biblioteki klienta do interakcji z Google Datastore znajdziesz w oficjalnej dokumentacji.
Ten fragment kodu definiuje funkcję aktualizującą koszyk. Funkcja przyjmuje te dane wejściowe: conversation_id
i message
. message
zawiera dane JSON opisujące działanie, które użytkownik chce wykonać, które jest już wbudowane w karuzel wyświetlający katalog produktów. Funkcja tworzy klienta Google Datastore i natychmiast pobiera element ShoppingCart, którego kluczem jest identyfikator rozmowy.
Skopiuj do pliku views.py podany niżej kod funkcji. W następnej sekcji omówimy to bardziej szczegółowo.
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)
Rozszerzmy tę funkcję, aby dodać produkt do koszyka.
Dodawanie produktów do koszyka
Gdy użytkownik kliknie sugerowane działanie Dodaj produkt w karuzeli produktów, dane postbackData będą zawierać dane JSON opisujące działanie, które chce wykonać. Słownik JSON ma 2 klucze: „action” i „item_name”. Jest on wysyłany do Twojego webhooka. Pole „item_name” to unikalny identyfikator powiązany z produktem w pliku inventory.json.
Gdy mamy już zdania warunkowe z poleceniami dotyczącymi koszyka i elementami koszyka wyodrębnionymi z wiadomości, możemy napisać instrukcje warunkowe, aby dodać element. Niektóre szczególne przypadki, które należy wziąć pod uwagę, to sytuacja, w której Datastore nie ma jeszcze identyfikatora rozmowy lub koszyk po raz pierwszy otrzymuje dany produkt. Poniżej opisaliśmy rozszerzenie funkcji update_shopping_cart
. Ta zmiana powoduje dodanie do koszyka produktu, który jest utrwalony w Google Datastore.
Poniższy fragment kodu jest rozszerzeniem poprzedniej funkcji dodanej do pliku views.py. Możesz dodać różnicę lub skopiować fragment kodu i zastąpić nim dotychczasową wersję funkcji 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)
Ta funkcja zostanie rozszerzona, aby obsługiwać scenariusz, w którym cart_cmd
zawiera ciąg „del-item” zdefiniowany w CMD_DEL_ITEM
.
Połączenie wszystkich elementów
Pamiętaj, aby dodać funkcję route_message
, aby w razie otrzymania komunikatu o dodaniu produktu do koszyka została wywołana funkcja update_shopping_cart
. Musisz też zdefiniować stałą dodawania elementów, używając konwencji stosowanej w tym laboratorium.
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)
...
Obecnie możemy dodawać produkty do koszyka. Jeśli wdrożysz zmiany w Google App Engine, zmiany w koszyku powinny być widoczne w panelu Google Datastore w konsoli GCP. Na poniższym zrzucie ekranu konsoli Google Datastore widać pojedynczy element o nazwie odpowiadającej identyfikatorowi rozmowy, po którym następują relacje z elementami z inwentarza i ich ilością w koszyku zakupów.
W następnej sekcji utworzymy sposób wyświetlania produktów w koszyku. Mechanizm sprawdzania koszyka powinien pokazywać wszystkie produkty w koszyku, ich liczbę oraz opcję usunięcia produktu z koszyka.
Sprawdzanie produktów w koszyku
Wyświetlenie produktów w koszyku jest jedynym sposobem na poznanie stanu koszyka i sprawdzenie, które produkty można usunąć.
Najpierw wyślij przyjazny komunikat, np. „Twój koszyk na zakupy”, a potem wiadomość z karuzelką kart z zalecanymi odpowiedziami „Usuń” lub „Dodaj”. Karuzela z kartą multimedialną powinna dodatkowo zawierać liczbę produktów zapisanych w koszyku.
Zanim zaczniemy pisać funkcję, warto wiedzieć, że jeśli w koszyku jest tylko jeden typ produktu, nie możemy go wyświetlić jako karuzeli. Karuzele z kartami multimedialnymi muszą zawierać co najmniej 2 karty. Z drugiej strony, jeśli w koszyku nie ma żadnych produktów, chcemy wyświetlić prosty komunikat informujący, że koszyk jest pusty.
Z tego założenia zdefiniujmy funkcję o nazwie send_shopping_cart
. Ta funkcja łączy się z Google Datastore i wysyła żądanie dotyczące elementu ShoppingCart na podstawie identyfikatora rozmowy. Gdy to zrobimy, wywołamy funkcję get_inventory_data
i użyjemy karuzeli z kartą rozszerzoną, aby podać stan koszyka. Musimy też pobrać identyfikator produktu według nazwy. Możemy zadeklarować funkcję, która sprawdzi Google Datastore i określi tę wartość. Podczas tworzenia karuzeli możemy powiązać sugerowane odpowiedzi z usuwaniem elementów lub dodawaniem elementów według identyfikatora produktu. Poniżej znajduje się fragment kodu, który wykonuje wszystkie te operacje. Skopiuj kod w dowolnym miejscu do pliku 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)
...
Upewnij się, że na początku pliku views.py masz już zdefiniowaną funkcję CMD_SHOW_CART
, i jeśli użytkownik wyśle wiadomość zawierającą „show-cart”, wywołaj funkcję send_shopping_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)
...
Na podstawie logiki wprowadzonej w funkcji send_shopping_cart
, gdy wpiszesz „show-cart”, otrzymamy albo wiadomość, że w koszyku nic nie ma, albo kartę z jednym produktem w koszyku, albo karuzelę z kartami pokazującymi wiele produktów. Dodatkowo mamy 3 sugerowane odpowiedzi: „Zobacz łączną cenę”, „Opróżnij koszyk” i „Zobacz menu”.
Spróbuj wdrożyć powyższe zmiany kodu, aby sprawdzić, czy koszyk śledzi dodawane przez Ciebie produkty i czy możesz przeglądać koszyk z poziomu interfejsu rozmów, jak pokazano na powyższych zrzutach ekranu. Możesz wdrożyć zmiany, wykonując ten polecenie w katalogu step-2, w którym dodajesz zmiany.
$ gcloud app deploy
Funkcję „Zobacz łączną cenę” utworzymy w następnej sekcji, gdy utworzymy funkcję usuwania produktu z koszyka. Funkcja get_cart_price
będzie działać podobnie do funkcji „Zobacz koszyk”, czyli będzie zawierać wzajemne odniesienia do danych w magazynie danych i pliku inventory.json, aby wygenerować łączną cenę koszyka. Przyda się on w następnej części tego Codelab, w której zajmiemy się integracją z płatnościami.
Usuwanie produktów z koszyka
Na koniec możemy dopełnić działania związane z koszykiem, wprowadzając funkcję usuwania koszyka. Zastąp dotychczasową funkcję update_shopping_cart
tym fragmentem kodu.
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)
Wysyłanie wiadomości z potwierdzeniem
Gdy użytkownik doda produkt do koszyka, wyślij wiadomość z potwierdzeniem, że widzisz jego działanie i przetworzyłeś jego prośbę. Pozwala to nie tylko określić oczekiwania, ale też podtrzymać rozmowę.
Rozszerzymy funkcję update_shopping_cart
, aby wysyłała wiadomość z identyfikatorem rozmowy, która informuje o dodaniu lub usunięciu produktu, oraz zawiera sugestie dotyczące sprawdzenia koszyka lub ponownego wyświetlenia 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)
To powinno wystarczyć. Pełnofunkcyjny koszyk, który umożliwia użytkownikowi dodawanie i usuwanie produktów oraz sprawdzanie produktów w koszyku.
Jeśli chcesz zobaczyć funkcję koszyka w rozmowie w usłudze Business Messages, wdrożenie aplikacji, aby skontaktować się z agentem. Aby to zrobić, uruchom to polecenie w katalogu step-2.
$ gcloud app deploy
5. Przygotowanie do płatności
W przygotowaniu się do integracji z procesorem płatności w kolejnych częściach tej serii musimy znaleźć sposób na pobranie ceny koszyka. Stwórzmy funkcję, która pobiera cenę, korzystając z krzyżowych odniesień do danych koszyka w Google Datastore, pobiera cenę każdego produktu z asortymentu i pomnaża ją przez liczbę każdego produktu w koszyku.
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
...
W końcu możemy użyć tej funkcji i wysłać wiadomość do użytkownika.
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)
...
Aby to wszystko połączyć, zaktualizuj funkcję route_message
i wielkość stałą, aby wywołać powyższą logikę.
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)
...
Oto kilka zrzutów ekranu pokazujących, czego można dokonać dzięki tej metodzie:
Gdy w następnej części tego ćwiczenia będziemy gotowi do integracji z procesorem płatności, wywołamy funkcję get_cart_price
, aby przekazać dane do procesora płatności i rozpocząć proces płatności.
Możesz też wypróbować tę funkcję w ramach rozmowy w Business Messages, uruchamiając aplikację i współdziałając z agentem.
$ gcloud app deploy
6. Gratulacje
Gratulacje! Udało Ci się utworzyć koszyk w Business Messages.
W tym CodeLab nie omawiamy funkcji opróżniania całego koszyka. Jeśli chcesz, możesz rozszerzyć aplikację, aby udostępnić funkcję „Opróżnij koszyk”. Rozwiązanie jest dostępne w kroku 3 kodu źródłowego, który został sklonowany.
W przyszłości zintegrujemy się z zewnętrznym procesorem płatności, aby umożliwić użytkownikom realizację transakcji z Twoją marką.
Co sprawia, że koszyk jest dobry?
Dobre wrażenia z korzystania z koszyka w konwersacji nie różnią się od tych w aplikacji mobilnej czy w sklepie stacjonarnym. Dodawanie i usuwanie produktów oraz obliczanie ceny koszyka to tylko niektóre z funkcji, które poznasz w tym Codelab. Różnica w porównaniu z prawdziwym koszykiem na zakupy polega na tym, że w dowolnym momencie, gdy dodajesz lub usuwasz produkty, możesz zobaczyć cenę wszystkich produktów w koszyku. Dzięki tym przydatnym funkcjom wyróżnisz się na tle konkurencji.
Co dalej?
Gdy będziesz gotowy, zapoznaj się z podanych niżej tematów, aby dowiedzieć się więcej o bardziej złożonych interakcjach, które możesz przeprowadzać w ramach wiadomości biznesowych:
- Jak działają wiadomości biznesowe?
- Sprawdzone metody
- Wytyczne dotyczące logo
- Przesłanie zgłoszenia do pracownika obsługi klienta