Mağazadan Online Teslim Alma: Bonjour Yemeği - Bölüm 2 - Alışveriş Sepeti Oluşturma

1. Giriş

53003251caaf2be5.png 8826bd8cb0c0f1c7.png

Son güncelleme: 30.10.2020

Business Messages'da alışveriş sepeti oluşturma

Bu codelab, internetten satın alma ve mağazadan teslim alma kullanıcı yolculuğu oluşturmayı amaçlayan bir serinin ikinci codelab'idir. Birçok e-ticaret yolculuğunda, kullanıcıları ödeme yapan müşterilere dönüştürme başarısı için alışveriş sepeti çok önemlidir. Alışveriş sepeti, müşterilerinizi daha iyi anlamanın ve ilgilenebilecekleri diğer öğelerle ilgili öneriler sunmanın da bir yoludur. Bu codelab'de, alışveriş sepeti deneyimini oluşturmaya ve uygulamayı Google App Engine'e dağıtmaya odaklanacağız.

İyi bir alışveriş sepetinin özellikleri nelerdir?

Alışveriş sepetleri, başarılı bir online alışveriş deneyimi için çok önemlidir. Business Messages, yalnızca potansiyel bir müşteriyle ürün hakkında soru-cevap sürecini kolaylaştırmakla kalmaz, aynı zamanda görüşme içinde ödeme işlemini tamamlayarak tüm alışveriş deneyimini kolaylaştırabilir.

9d17537b980d0e62.png

İyi bir alışveriş deneyimi, iyi bir alışveriş sepetinin yanı sıra kullanıcıların öğelere kategoriye göre göz atmasına ve işletmenin, alıcının ilgilenebileceği diğer ürünleri önermesine olanak tanır. Alışveriş sepetine daha fazla ürün ekledikten sonra kullanıcı, ödeme yapmadan önce sepetinin tamamını inceleyebilir, ürünleri kaldırabilir veya daha fazla ürün ekleyebilir.

Ne oluşturacaksınız?

Bu codelab serisinin bu bölümünde, Bonjour Meal adlı kurgusal şirket için 1. bölümde oluşturduğunuz dijital temsilciyi genişleterek kullanıcıların ürün kataloğuna göz atmasını ve alışveriş sepetine ürün eklemesini sağlayacaksınız.

Bu codelab'de uygulamanız:

  • Business Messages'da soru kataloğu gösterme
  • Kullanıcıların ilgilenebileceği öğeler önerme
  • Alışveriş sepetinin içeriğini inceleme ve toplam fiyat özeti oluşturma

ab2fb6a4ed33a129.png

Neler öğreneceksiniz?

  • Google Cloud Platform'da App Engine'e web uygulaması dağıtma
  • Alışveriş sepetinin durumunu kaydetmek için kalıcı depolama alanını kullanma

Bu codelab, bu codelab serisinin 1. bölümündeki dijital temsilciyi genişletmeye odaklanmaktadır.

Gerekenler

2. Hazırlanma

Bu codelab'de ilk temsilcinizi oluşturduğunuzu ve codelab'in 1. bölümünü tamamladığınızı varsayıyoruz. Bu nedenle, İş Mesajları ve İşletme İletişimleri API'lerini etkinleştirme, hizmet hesabı anahtarları oluşturma, uygulama dağıtma veya İşletme İletişimleri Konsolu'nda webhook'unuzu ayarlama gibi temel konuları ele almayacağız. Bununla birlikte, uygulamanızın üzerine inşa ettiğimiz yapıyla tutarlı olduğundan emin olmak için örnek bir uygulamayı klonlayacağız ve alışveriş sepetiyle ilgili verilerin kalıcı olarak saklanabilmesi için Google Cloud Platform'da Datastore API'sini etkinleştireceğiz.

Uygulamayı GitHub'dan kopyalama

Bir terminalde, aşağıdaki komutu kullanarak Django Echo Bot Sample'ı projenizin çalışma dizinine klonlayın:

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

Hizmet hesabı için oluşturulan JSON kimlik bilgileri dosyanızı örneğin kaynaklar klasörüne kopyalayın ve kimlik bilgilerini "bm-agent-service-account-credentials.json" olarak yeniden adlandırın.

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

Google Datastore API'yi etkinleştirin

Bu codelab'in 1. bölümünde Business Messages API, Business Communications API ve Cloud Build API'yi etkinleştirdiniz.

Bu codelab'de Google Datastore ile çalışacağımız için şu API'yi de etkinleştirmemiz gerekir:

  1. Google Cloud Console'da Google Datastore API'yi açın.
  2. Doğru GCP projesiyle çalıştığınızdan emin olun.
  3. Etkinleştir'i tıklayın.

Örnek uygulamayı dağıtma

Bir terminalde, örneğin 2. adım dizinine gidin.

Örneği dağıtmak için terminalde aşağıdaki komutları çalıştırın:

$ gcloud config set project PROJECT_ID*
$ gcloud app deploy
  • PROJECT_ID, API'lere kaydolmak için kullandığınız projenin proje kimliğidir.

Son komutun çıktısında dağıtılan uygulamanın URL'sini not edin:

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

Yeni dağıttığınız kod, Business Messages'dan mesaj almak için webhook'u olan bir web uygulaması içeriyor. Bu dosya, codelab'in 1. bölümünde yaptığımız her şeyi içerir. Henüz yapmadıysanız lütfen webhook'unuzu yapılandırın.

Uygulama, kullanıcının Bonjour Meal'ın çalışma saatlerini sorması gibi bazı basit soruları yanıtlayacak. Bu testi, Business Communications Console'daki Temsilci Bilgileri bölümünden alabileceğiniz test URL'leri aracılığıyla mobil cihazınızda yapmanız gerekir. Test URL'leri, mobil cihazınızda Business Messages deneyimini başlatır ve temsilcinizle etkileşim kurmaya başlayabilirsiniz.

3. Ürün kataloğu

Envanter sistemi

Çoğu durumda, dahili bir API aracılığıyla doğrudan bir markanın envanteriyle entegrasyon yapabilirsiniz. Diğer durumlarda ise bir web sayfasını kazıyabilir veya kendi envanter izleme sisteminizi oluşturabilirsiniz. Envanter sistemi oluşturmaya odaklanmıyoruz. Aracımız için resimler ve ürün bilgileri içeren basit bir statik dosya kullanacağız. Bu bölümde, bu statik dosyadan bilgi alıp bu bilgileri sohbette gösteririz. Ayrıca, kullanıcının alışveriş sepetine eklenebilecek öğelere göz atmasına izin veririz.

Statik envanter dosyası şu şekilde görünür:

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

Python uygulamasının bu dosyayı okumasını sağlayalım.

Envanterimizden okuma

Envanter, ./resources dizininde bulunan "inventory.json" adlı statik bir dosyadır. JSON dosyasının içeriğini okumak ve ardından bunu görüşmede göstermek için views.py dosyasına biraz Python mantığı eklememiz gerekiyor. JSON dosyasındaki verileri okuyup kullanılabilir ürünlerin listesini döndüren bir işlev oluşturalım.

Bu işlev tanımı, views.py dosyasında herhangi bir yere yerleştirilebilir.

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

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

Bu, envanterdeki verileri okumak için ihtiyacımız olan bilgileri sağlar. Şimdi bu ürün bilgilerini görüşmede göstermenin bir yolunu bulmamız gerekiyor.

Ürün kataloğunu gösterme

Bu codelab'i basitleştirmek için, tüm envanter öğelerini tek bir zengin kart karuseli aracılığıyla Business Messages görüşmesinde göstermek üzere genel bir ürün kataloğumuz var.

Ürün kataloğunu görüntülemek için "Menüyü göster" metnini ve "show-product-catalog" postbackData'sını içeren bir önerilen yanıt oluşturacağız. Kullanıcılar önerilen yanıta dokunduğunda ve web uygulamanız postback verilerini aldığında zengin kart karuselini göndeririz. views.py dosyasının en üstüne bu önerilen yanıt için yeni bir sabit ekleyelim.

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

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

Buradan iletiyi ayrıştırıp ürün kataloğunu içeren zengin kart bandı gönderen yeni bir işleve yönlendiririz. Önce route_message işlevini genişleterek önerilen yanıta dokunulduğunda "send_product_catalog" işlevini çağırın, ardından işlevi tanımlayacağız.

Aşağıdaki snippet'te, route_message işlevindeki if ifadesine normalized_message değerinin daha önce tanımladığımız sabit CMD_SHOW_PRODUCT_CATALOG değerine eşit olup olmadığını kontrol eden ek bir koşul ekleyin.

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

Ayrıca akışı tamamladığınızdan ve send_product_catalog tanımladığınızdan emin olun. send_product_catalog çağrısı get_menu_carousel,, daha önce okuduğumuz envanter dosyasından zengin kart karuselini oluşturur.

İşlev tanımları, views.py dosyasının herhangi bir yerine yerleştirilebilir. Aşağıdaki snippet'te, dosyanın en üstüne eklenmesi gereken iki yeni sabitin kullanıldığını unutmayın.

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

Carousel öğelerinin oluşturulmasını incelerseniz BusinessMessagesSuggestion sınıfının bir örneğini de oluştururuz. Her öneri, banttaki bir ürün için kullanıcı seçimini temsil eder. Kullanıcı, önerilen yanıta dokunduğunda Business Messages, öğeyi ve kullanıcının yapmak istediği işlemi (sepete ekleme veya sepetten kaldırma) açıklayan JSON'u içeren postbackData'yı webhook'unuza gönderir. Aşağıdaki bölümde, öğeyi sepete ekleyebilmek için bu şekildeki mesajları ayrıştıracağız.

Bu değişiklikleri yaptığımıza göre, web uygulamasını Google App Engine'e dağıtalım ve deneyimi test edelim.

$ gcloud app deploy

Mobil cihazınızda sohbet yüzeyini yüklediğinizde "show-product-catalog" mesajını gönderin. Ardından, aşağıdaki gibi bir ürün bandı görmeniz gerekir.

4639da46bcc5230c.png

Öğe ekle'ye dokunursanız önerilen yanıttaki geri gönderme verileri aracının yanıtına yansıtılmaktan başka bir şey olmaz. Sonraki bölümde, ürün kataloğunu kullanarak öğenin ekleneceği alışveriş sepetini oluşturacağız.

Yeni oluşturduğunuz ürün kataloğu çeşitli şekillerde genişletilebilir. Farklı içecek menüsü seçenekleri veya vejetaryen seçenekler olabilir. Kullanıcıların menü seçeneklerinde ayrıntılı arama yaparak aradıkları ürünlere ulaşmalarını sağlamak için kaydırma çubukları veya öneri çipleri kullanmak harika bir yöntemdir. Bu codelab'in bir uzantısı olarak, ürün kataloğu sistemini genişleterek kullanıcının menüdeki içecekleri yiyeceklerden ayrı olarak görüntülemesini veya hatta vejetaryen seçenekleri belirlemesini sağlayabilirsiniz.

4. Alışveriş sepeti

Bu codelab bölümünde, önceki bölümden yararlanarak alışveriş sepeti işlevini oluşturacağız. Bu işlev, mevcut ürünlere göz atmamızı sağlar.

Alışveriş sepeti deneyimi, kullanıcıların sepete ürün eklemesine, sepetten ürün kaldırmasına, sepetindeki her bir ürünün sayısını takip etmesine ve sepetindeki ürünleri incelemesine olanak tanır.

Alışveriş sepetinin durumunu takip etmek için web uygulamasındaki verileri kalıcı hale getirmemiz gerekir. Deneme ve dağıtım kolaylığı için verileri kalıcı hale getirmek üzere Google Cloud Platform'da Google Datastore'u kullanacağız. Kullanıcı ile işletme arasındaki görüşme kimliği değişmediği için kullanıcıları alışveriş sepeti öğeleriyle ilişkilendirmek için bu kimliği kullanabiliriz.

Google Datastore'a bağlanarak ve görüşme kimliğini gördüğümüzde kalıcı hale getirerek başlayalım.

Veri deposuna bağlanma

Alışveriş sepetiyle ilgili herhangi bir etkileşim gerçekleştirildiğinde (ör. kullanıcı bir öğe eklediğinde veya sildiğinde) Google Datastore ile bağlantı kurarız. Bu istemci kitaplığını Google Datastore ile etkileşimde bulunmak için kullanma hakkında daha fazla bilgiyi resmi belgelerde bulabilirsiniz.

Aşağıdaki snippet, alışveriş sepetini güncellemek için bir işlev tanımlar. İşlev, şu girişi alır: conversation_id ve message. message, kullanıcının gerçekleştirmek istediği işlemi açıklayan JSON'u içerir. Bu JSON, ürün kataloğunu gösteren döngünüze zaten yerleştirilmiştir. Bu işlev, bir Google Datastore istemcisi oluşturur ve anahtarın görüşme kimliği olduğu bir ShoppingCart varlığını hemen getirir.

Aşağıdaki işlevi views.py dosyanıza kopyalayın. Bu konuyu bir sonraki bölümde daha ayrıntılı olarak ele alacağız.

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)

Bu işlevi sepete ürün ekleyecek şekilde genişletelim.

Sepete ürün ekleme

Kullanıcı, ürün bantındaki Öğe ekle önerilen işlemine dokunduğunda postbackData, kullanıcının gerçekleştirmek istediği işlemi açıklayan JSON'u içerir. JSON sözlüğünde "action" ve "item_name" olmak üzere iki anahtar bulunur ve bu JSON sözlüğü webhook'unuza gönderilir. "item_name" alanı, inventory.json dosyasındaki öğeyle ilişkilendirilmiş benzersiz tanımlayıcıdır.

Sepet komutu ve sepet öğesi mesajdan ayrıştırıldıktan sonra öğeyi eklemek için koşullu ifadeler yazabiliriz. Burada göz önünde bulundurulması gereken bazı uç durumlar, veri deposunun görüşme kimliğini hiç görmemiş olması veya alışveriş sepetinin bu öğeyi ilk kez almasıdır. Aşağıdaki işlev, yukarıda tanımlanan update_shopping_cart işlevinin bir uzantısıdır. Bu değişiklik, Google Datastore tarafından kalıcı hale getirilen bir öğeyi alışveriş sepetine ekler.

Aşağıdaki snippet, views.py dosyanıza eklenen önceki işlevin bir uzantısıdır. Farkı ekleyebilir veya snippet'i kopyalayıp update_shopping_cart işlevinin mevcut sürümünü değiştirebilirsiniz.

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)

Bu işlev daha sonra, cart_cmd'nın CMD_DEL_ITEM içinde tanımlanan "del-item" dizesini içerdiği senaryoyu destekleyecek şekilde genişletilecektir.

Birleştirme

Sepete öğe ekleme mesajı aldığınızda update_shopping_cart işlevinin çağrılması için route_message işlevine tesisat eklediğinizden emin olun. Ayrıca, codelab boyunca kullandığımız kuralı kullanarak öğe eklemek için bir sabit tanımlamanız gerekir.

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)

...

Şu an için alışveriş sepetine ürün ekleme özelliği sunulmaktadır. Değişikliklerinizi Google App Engine'e dağıtırsanız alışveriş sepeti değişikliklerinin GCP Console'da bulunan Google Datastore kontrol panelinde yansıtıldığını görebilirsiniz. Google Datastore konsolunun aşağıdaki ekran görüntüsünde, görüşme kimliğinin adını taşıyan tek bir öğe ve ardından envanter öğeleriyle ilişkiler ve alışveriş sepetindeki bu öğelerin miktarı gösterilmektedir.

619dc18a8136ea69.png

Bir sonraki bölümde, alışveriş sepetindeki öğeleri listeleyebileceğimiz bir yöntem oluşturacağız. Alışveriş sepeti inceleme mekanizması, sepetteki tüm ürünleri, bu ürünlerin miktarını ve bir ürünü sepetten çıkarma seçeneğini göstermelidir.

Alışveriş sepetindeki öğeleri inceleme

Alışveriş sepetindeki öğeleri listelemek, alışveriş sepetinin durumunu anlamanın ve hangi öğeleri kaldırabileceğimizi bilmenin tek yoludur.

Öncelikle "Alışveriş sepetiniz:" gibi samimi bir mesaj gönderelim. Ardından, "Birini kaldır" veya "Birini ekle" için ilişkili önerilen yanıtlar içeren bir zengin kart karuselinin yer aldığı başka bir mesaj gönderelim. Zengin kart karuselinde, alışveriş sepetine kaydedilen öğelerin miktarı da listelenmelidir.

İşlevimizi yazmaya başlamadan önce bilmemiz gereken bir şey var: Alışveriş sepetinde yalnızca bir tür öğe varsa bunu carousel olarak oluşturamayız. Zengin kart ruloları en az iki kart içermelidir. Diğer taraftan, alışveriş sepetinde ürün yoksa sepetin boş olduğunu belirten basit bir mesaj göstermek istiyoruz.

Bu nedenle, send_shopping_cart adlı bir işlev tanımlayalım. Bu işlev, Google Datastore'a bağlanır ve Conversation ID'ye göre bir ShoppingCart öğesi ister. Bu bilgiye ulaştığımızda get_inventory_data işlevini çağırır ve alışveriş sepetinin durumunu bildirmek için zengin kart karuselini kullanırız. Ayrıca, bir ürünün kimliğini adına göre almamız da gerekir. Bu değeri belirlemek için Google veri deposu'na bakacak bir işlev tanımlayabiliriz. Carousel oluşturulurken, öğeleri silmek veya ürün kimliğine göre öğe eklemek için önerilen yanıtları ilişkilendirebiliriz. Aşağıdaki snippet bu işlemlerin tümünü gerçekleştirir. Kodu views.py dosyasına herhangi bir yere kopyalayın.

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)

...

views.py dosyasının en üstünde CMD_SHOW_CART tanımladığınızdan emin olun ve kullanıcı "show-cart" içeren bir mesaj gönderirse send_shopping_cart işlevini çağırın.

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

send_shopping_cart işlevinde tanıttığımız mantığa göre, "show-cart" yazdığınızda sepette hiçbir şey olmadığını belirten bir mesaj, sepetteki tek öğeyi gösteren bir zengin kart veya birden fazla öğeyi gösteren bir kart karuselini görürsünüz. Ayrıca, "Toplam fiyatı gör", "Sepeti boşalt" ve "Menüyü gör" olmak üzere üç önerilen yanıtımız var.

Alışveriş sepetinizin eklediğiniz öğeleri izlediğini ve yukarıdaki ekran görüntülerinde gösterildiği gibi alışveriş sepetini görüşme yüzeyinden inceleyebildiğinizi test etmek için yukarıdaki kod değişikliklerini dağıtmayı deneyin. Değişikliklerinizi eklediğiniz adım-2 dizininden bu komutu çalıştırarak değişiklikleri dağıtabilirsiniz.

$ gcloud app deploy

Sepetten öğe kaldırma işlevini oluşturduktan sonraki bölümde "Toplam fiyatı gör" özelliğini oluşturacağız. get_cart_price işlevi, alışveriş sepeti için toplam fiyatı oluşturmak üzere veri deposundaki verileri inventory.json dosyasıyla çapraz referanslayarak "Alışveriş sepetini gör" özelliğine benzer şekilde çalışır. Bu, ödemelerle entegrasyon yapacağımız codelab'in bir sonraki bölümünde işimize yarayacak.

Öğeleri alışveriş sepetinden çıkarma

Son olarak, alışveriş sepetini kaldırma işlevi ekleyerek alışveriş sepeti davranışını tamamlayabiliriz. Mevcut update_shopping_cart işlevini aşağıdaki snippet ile değiştirin.

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)

Onay mesajı gönderme

Kullanıcı sepete bir öğe eklediğinde, işlemini onaylayan ve isteğini işleme aldığınızı belirten bir onay mesajı göndermeniz gerekir. Bu sayede hem beklentileri belirleyebilir hem de sohbeti devam ettirebilirsiniz.

update_shopping_cart işlevini, öğenin eklendiğini veya kaldırıldığını belirten bir mesajı görüşme kimliğine gönderecek ve alışveriş sepetini inceleme ya da menüyü tekrar görme önerileri verecek şekilde genişletelim.

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

İşlem tamamlandı. Kullanıcının alışveriş sepetine ürün eklemesine, ürünleri kaldırmasına ve sepetteki ürünleri incelemesine olanak tanıyan, tüm özelliklere sahip bir alışveriş sepeti deneyimi.

Bu noktada, Business Messages görüşmesinde alışveriş sepeti işlevini görmek istiyorsanız temsilcinizle etkileşim kurmak için uygulamayı dağıtın. Bu işlemi 2. adım dizininde bu komutu çalıştırarak yapabilirsiniz.

$ gcloud app deploy

5. Ödemelere hazırlanma

Serinin bir sonraki bölümünde bir ödeme işlemcisiyle entegrasyona hazırlanırken alışveriş sepetinin fiyatını almamız gerekir. Google Datastore'daki alışveriş sepeti verilerine çapraz referans vererek, her öğenin fiyatını envanterden alarak ve fiyatı sepetindeki her öğenin miktarıyla çarparak bizim için fiyatı alan bir işlev oluşturalım.

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

...

Son olarak, bu işlevi kullanabilir ve kullanıcıya mesaj gönderebiliriz.

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

Tüm bunları bir araya getirmek için yukarıdaki mantığı tetikleyecek route_message işlevini ve sabiti güncelleyelim.

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

Yukarıdaki mantığın neyi başardığını gösteren bazı ekran görüntülerini aşağıda bulabilirsiniz:

8feacf94ed0ac6c4.png

Bu kod laboratuvarının bir sonraki bölümünde ödeme işleyiciyle entegrasyon yapmaya hazır olduğumuzda, verileri ödeme işleyiciye aktarmak ve ödeme akışını başlatmak için get_cart_price işlevini çağıracağız.

Uygulamayı dağıtıp temsilcinizle etkileşime geçerek bu alışveriş sepeti işlevini Business Messages görüşmesinde deneyebilirsiniz.

$ gcloud app deploy

6. Tebrikler

Tebrikler, Business Messages'da alışveriş sepeti deneyimini başarıyla oluşturdunuz.

Bu codelab'de ele almadığımız bir özellik, alışveriş sepetinin tamamını boşaltma özelliğidir. İsterseniz uygulamayı "Sepeti boşalt" özelliğini karşılayacak şekilde genişletmeyi deneyin. Çözüm, klonladığınız kaynak kodun 3. adımında yer alır.

Gelecekteki bir bölümde, kullanıcılarınızın markanızla ödeme işlemi yapabilmesi için harici bir ödeme işlemcisiyle entegrasyon yapacağız.

İyi bir alışveriş sepetinin özellikleri nelerdir?

Bir görüşmedeki iyi bir alışveriş sepeti deneyimi, mobil uygulamadaki veya fiziksel mağazadaki deneyimden farklı değildir. Öğe ekleme, öğe kaldırma ve sepetin fiyatını hesaplama, bu codelab'de ele aldığımız özelliklerden sadece birkaçıdır. Gerçek dünyadaki alışveriş sepetinden farklı olarak, sepete ürün eklerken veya sepetten ürün çıkarırken sepetteki tüm ürünlerin fiyatını istediğiniz zaman görebilirsiniz. Bu tür yüksek değerli özellikler, sohbetli ticaret deneyiminizi öne çıkarır.

Yapabilecekleriniz

Hazır olduğunuzda, Business Messages'ta gerçekleştirebileceğiniz daha karmaşık etkileşimler hakkında bilgi edinmek için aşağıdaki konulardan bazılarına göz atın:

Referans belgeler