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 Tarihi: 30.10.2020

Business Messages'ta Alışveriş Sepeti Oluşturma

Bu, internetten satın alma ile mağazadan teslim alma kullanıcı yolculuğu oluşturmayı hedefleyen bir serinin ikinci codelab'idir. Birçok e-ticaret yolculuğunda, kullanıcıları ödeme yapan müşterilere dönüştürmenin başarısında alışveriş sepeti son derece önemlidir. Alışveriş sepeti, müşterilerinizi daha iyi anlamanın ve ilgilerini çekebilecek diğer ürünler hakkında öneriler sunmanın 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ş deneyiminin anahtarıdır. Görünüşe göre Business Messages, potansiyel bir müşteriyle ürün hakkında Soru-Cevap sürecini kolaylaştırmakla kalmıyor, aynı zamanda görüşme içinde ödeme işleminin tamamlanmasına kadar tüm alışveriş deneyimini kolaylaştırabiliyor.

9d17537b980d0e62.png

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

Oluşturacağınız araç

Codelab serisinin bu bölümünde, 1. bölümde oluşturduğunuz dijital aracıyı Bonjour Meal adlı hayali şirket için genişleteceksiniz. Böylece kullanıcılar ürün kataloğuna göz atıp alışveriş sepetine ürün ekleyebilir.

Bu codelab'de uygulamanız:

  • Business Messages'ta soru kataloğu gösterin
  • Kullanıcıların ilgilenebileceği öğeler önerin
  • Alışveriş sepetinin içeriğini inceleyip toplam fiyat özeti oluşturmak

ab2fb6a4ed33a129.png

Neler öğreneceksiniz?

  • Google Cloud Platform'da App Engine'de bir web uygulaması dağıtma
  • Bir alışveriş sepetinin durumunu kaydetmek için kalıcı depolama mekanizması nasıl kullanılır?

Bu codelab'in odağı, bu codelab serisinin 1. bölümünden dijital aracıyı genişletmektir.

Gerekenler

2. Kurulum

Bu codelab'de, ilk aracınızı oluşturduğunuz ve codelab'in 1. bölümünü tamamladığınız varsayılmaktadır. Bu nedenle, Business Messages ve Business Communications API'lerini etkinleştirme, hizmet hesabı anahtarları oluşturma, uygulama dağıtma veya Business Communications Console'da webhook'unuzu ayarlama ile ilgili temel bilgileri ele almayacağız. Bununla birlikte, uygulamanızın üzerinde yaptığımız geliştirmelerle tutarlı olduğundan emin olmak için örnek bir uygulamayı klonlayacak ve alışveriş sepetiyle ilgili verileri saklamak için Google Cloud Platform'daki Datastore API'sini etkinleştireceğiz.

Uygulamayı GitHub'dan klonlama

Bir terminalde, aşağıdaki komutla Django Echo Bot Sample'ı (Django Echo Bot Örneği) 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ştirme

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

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

  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 bir terminalde şu 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 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]

Az önce dağıttığınız kod, Business Messages'tan mesaj almak için webhook içeren bir web uygulaması içeriyor. Bu belgede, codelab'in 1. bölümünde yaptığımız tüm işlemler yer almaktadır. Henüz yapmadıysanız lütfen webhook'unuzu yapılandırın.

Uygulama, bir kullanıcının Bonjour Meal'ın çalışma saatlerini sorması gibi bazı basit sorulara yanıt verecektir. Bunu, Business Communications Console'daki Temsilci Bilgileri'nden alabileceğiniz test URL'leri aracılığıyla mobil cihazınızda test etmeniz gerekir. Test URL'leri, mobil cihazınızda Business Messages deneyimini başlatır ve temsilcinizle buradan 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, bir web sayfasını kazıyabilir veya kendi envanter izleme sisteminizi oluşturabilirsiniz. Odağımız bir envanter sistemi oluşturmak değil. Temsilcimiz için resimleri ve ürün bilgilerini içeren basit bir statik dosya kullanacağız. Bu bölümde, bu statik dosyadan bilgiler alıp ileti dizisinde bu bilgileri gösterecek ve kullanıcının alışveriş sepetine eklenebilecek ürünlere göz atmasına olanak tanıyacağız.

Statik envanter dosyası aşağıdaki gibi 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
        }
    ]
}

Bu dosyayı okuyacak Python uygulamasını getirelim.

Envanterimizden okuma

Envanter, ./resources dizininde bulunan "inventory.json" adlı statik bir dosyadır. JSON dosyasının içeriğini okumak ve ardından ileti dizisinde göstermek için view.py'ye bazı Python mantığı eklememiz gerekir. JSON dosyasındaki verileri okuyan ve mevcut ürünlerin listesini döndüren bir işlev oluşturalım.

Bu işlev tanımı, view.py içinde 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 bize envanterdeki verileri okumamız için gerekenleri sağlamalıdır. Şimdi bu ürün bilgisini görüşmede öne çıkaracak bir yönteme ihtiyacımız var.

Ürün kataloğunu gösterme

Bu codelab'de kolaylık sağlamak amacıyla, tüm envanter öğelerinin Business Messages görüşmesinde tek bir zengin kart bandı üzerinden gösterilmesini sağlayan genel bir ürün kataloğumuz bulunmaktadır.

Ürün kataloğunu görüntülemek için "Menüyü Göster" ve geri gönderme verilerini "show-product-catalog" içeren bir önerilen yanıt oluşturacağız. Kullanıcılar önerilen yanıta dokunduğunda ve web uygulamanız geri gönderme verilerini aldığında zengin kart bandını göndeririz. Bu önerilen yanıt için view.py’nin üst kısmına yeni bir sabit değer ekleyelim.

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

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

Buradan mesajı ayrıştırır ve ürün kataloğunu içeren bir zengin kart bandı gönderen yeni bir işleve yönlendirir. Öncelikle, önerilen yanıta dokunulduğunda bir işlevi "send_product_catalog" çağırmak için route_message işlevini genişletin, ardından işlevi tanımlayacağız.

Aşağıdaki snippet'te, normalized_message değerinin daha önce tanımladığımız CMD_SHOW_PRODUCT_CATALOG değerinde olup olmadığını kontrol etmek için route_message işlevindeki if ifadesine 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)
...

Akışı tamamladığınızdan ve send_product_catalog öğesini tanımlayalım. send_product_catalog, daha önce okuduğumuz envanter dosyasından zengin kart bandını oluşturan get_menu_carousel, özelliğini çağırır.

İşlev tanımları, view.py içinde herhangi bir yere yerleştirilebilir. Aşağıdaki snippet'in, dosyanın en üstüne eklenmesi gereken iki yeni sabit değerden yararlandığı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)
...

Bant öğ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 gerçekleştirmek istediği işlemi (sepete ekleme veya sepetten kaldırma) açıklayan JSON içeren postbackData'yı webhook'unuza gönderir. Aşağıdaki bölümde, öğeyi sepete gerçekten ekleyebilmek için buna benzer iletileri ayrıştıracağız.

Bu değişiklikleri yaptığımıza göre şimdi web uygulamasını Google App Engine'e dağıtalım ve deneyimi deneyelim!

$ gcloud app deploy

Sohbet yüzeyi mobil cihazınıza yüklendikten sonra, "ürün-kataloğunu-göster" mesajını gönderin. Böyle bir ürün bandı gösterilir.

4639da46bcc5230c.png

Öğe ekle'ye dokunduğunuzda,temsilcinin önerilen yanıttaki geri gönderme verilerini yansıtması dışında hiçbir şey olmaz. Bir sonraki bölümde ürün kataloğundan yararlanacak ve ürünün ekleneceği alışveriş sepetini oluşturmak için bunu kullanacağız.

Az önce oluşturduğunuz ürün kataloğu farklı şekillerde genişletilebilir. Farklı içecek menüsü veya vejetaryen seçenekleriniz olabilir. Bantlar veya öneri çipleri, kullanıcıların aradıkları ürüne ulaşmak için menü seçeneklerini ayrıntılı bir şekilde inceleyebilmelerini sağlamanın mükemmel bir yoludur. Bu codelab'in bir uzantısı olarak ürün kataloğu sistemini genişletmeyi deneyin. Böylece kullanıcı, içecekleri menüdeki yiyeceklerden ayrı olarak görebilir ve hatta vejetaryen seçenekleri belirleyebilir.

4. Alışveriş sepeti

Codelab'in bu bölümünde, mevcut ürünlere göz atmamıza olanak tanıyan önceki bölümden yola çıkarak alışveriş sepeti işlevini oluşturacağız.

Temel alışveriş sepeti deneyimi, kullanıcıların alışveriş sepetine ürün eklemesine, sepetten ürün çıkarmasına, alışveriş sepetindeki her ürün sayısını takip etmesine ve sepetteki öğeleri incelemesine olanak tanır.

Alışveriş sepetinin durumunu izlemek, web uygulamasında verileri saklamamız gerektiği anlamına gelir. Deneme ve dağıtım kolaylığı için verileri korumak amacıyla Google Cloud Platform'da Google Datastore'u kullanacağız. Görüşme Kimliği, kullanıcı ile işletme arasında sabit kalır. Dolayısıyla, kullanıcıları alışveriş sepeti öğeleriyle ilişkilendirmek için bu kimliği kullanabiliriz.

Google Datastore'a bağlanarak ve gördüğümüz ileti dizisi kimliğini devam ettirerek başlayalım.

Datastore ile bağlanma

Alışveriş sepetine herhangi bir etkileşim yürütüldüğünde (ör. kullanıcı bir öğe eklerken veya silerken) Google Datastore ile bağlantı kurarız. Google Datastore ile etkileşime geçmek için bu istemci kitaplığını 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 girdiyi alır: conversation_id ve message. message, kullanıcının gerçekleştirmek istediği işlemi açıklayan bir JSON içerir. Bu işlem, ürün kataloğunu gösteren bandınızda zaten yerleşik olarak bulunur. İşlev, Google Datastore istemcisi oluşturur ve hemen bir ShoppingCart varlığı getirir. Burada anahtar, sohbet kimliğidir.

Aşağıdaki işlevi view.py dosyanıza kopyalayın. İlerleyen bölümlerde bu özelliği genişletmeye devam edeceğiz.

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 fonksiyonun kapsamını genişleterek alışveriş sepetine bir ürün ekleyelim.

Alışveriş sepetine ürün ekleme

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

Alışveriş sepeti komutunu ve alışveriş sepeti öğesini mesajdan ayrıştırıldıktan sonra öğeyi eklemek için koşullu ifadeler yazabiliriz. Burada dikkate alınması gereken bazı uç durumlar, Datastore'un sohbet kimliğini hiç görmemesi veya alışveriş sepetinin bu öğeyi ilk kez almasıdır. Aşağıda, yukarıda tanımlanan update_shopping_cart işlevinin bir uzantısı verilmiştir. Bu değişiklik, alışveriş sepetine bir öğe ekler ve bu öğe Google Datastore tarafından saklanır.

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, cart_cmd işlevinin, CMD_DEL_ITEM öğesinde tanımlanan "del-item" dizesini içerdiği senaryoyu desteklemek için daha sonra genişletilecektir.

Hepsini bir araya getirme

Alışveriş sepetine ürün eklemeyle ilgili bir mesaj alırsanız update_shopping_cart işlevinin çağrılması için tesisat sistemini route_message işlevine eklediğinizden emin olun. Ayrıca, codelab'de kullandığımız kuraldan yararlanarak öğe eklemek için bir sabit değer 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 ekleyebiliyoruz. Değişikliklerinizi Google App Engine'e dağıtırsanız alışveriş sepeti değişikliklerinin GCP Console'daki Google Datastore kontrol paneline yansıtıldığını görebilirsiniz. Google Datastore konsolunun aşağıdaki ekran görüntüsüne bakın. Adını İleti Dizisi Kimliği'nden alan ve ardından alışveriş sepetindeki envanter öğeleriyle ve bu öğelerin miktarıyla olan bazı ilişkilerin ardından gelen tek bir varlık vardır.

619dc18a8136ea69.png

Sonraki bölümde, alışveriş sepetindeki öğeleri listelemek için bir yöntem oluşturacağız. Alışveriş sepeti inceleme mekanizması bize sepetteki tüm öğeleri, bu öğelerin miktarını ve bir ürünü sepetten çıkarma seçeneğini göstermelidir.

Alışveriş sepetindeki ürünler inceleniyor

Alışveriş sepetindeki ürünleri listelemek, alışveriş sepetinin durumunu anlamamızın ve hangi ürünleri kaldırabileceğimizi bilmemizin tek yoludur.

Önce "İşte alışveriş sepetiniz:" gibi samimi bir mesaj, ardından da "Birini kaldır" veya "Birini ekle" için önerilen yanıtlarla birlikte bir zengin kart bandını içeren başka bir mesaj gönderelim. Zengin kart bandı, alışveriş sepetinde kaydedilen öğe miktarını da listelemelidir.

İşe koyulmadan ve işlevimizi yazmadan önce dikkat edilmesi gereken bir nokta var: Alışveriş sepetinde yalnızca tek bir öğe türü varsa, bunu atlı karınca olarak oluşturamayız. Zengin kart rulolarında en az iki kart bulunmalıdır. Öte yandan, alışveriş sepetinde hiç ürün yoksa, alışveriş sepetinin boş olduğunu belirten basit bir mesaj göstermek istiyoruz.

Bunu akılda tutarak 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 varlığı ister. Bunu yaptıktan sonra, get_inventory_data işlevini çağırır ve alışveriş sepetinin durumunu raporlamak için bir zengin kart bandı kullanırız. Ayrıca ürünün kimliğini ada göre almamız gerekir. Böylece, değeri belirlemek için Google Datastore'u incelemek üzere bir işlev tanımlayabiliriz. Bant oluşturulurken, öğeleri silmek veya ürün kimliğine göre öğe eklemek için önerilen yanıtları ilişkilendirebiliriz. Aşağıdaki snippet tüm bu işlemleri gerçekleştirir. Kodu view.py dosyasındaki 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)

...

view.py'nin üst kısmında CMD_SHOW_CART tanımlamış olduğunuzdan emin olun ve kullanıcı "show-cart"ı içeren bir mesaj gönderirse send_shopping_cart yöntemini ç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, "alışveriş sepeti" yazdığınızda, alışveriş sepetinde hiçbir şey olmadığını belirten bir mesaj, alışveriş sepetindeki bir öğeyi gösteren bir zengin kart veya birden fazla öğe gösteren kart bandını alırız. Ayrıca, önerilen üç yanıt bulunmaktadır: "Toplam fiyatı göster", "Alışveriş sepetini boşalt" ve "Menüyü göster".

Alışveriş sepetinizin eklediğiniz öğeleri izleyip izlemediğini test etmek için yukarıdaki kod değişikliklerini uygulamayı deneyin. Yukarıdaki ekran görüntülerinde gösterildiği gibi, görüşmelerde alışveriş sepetini inceleyebilirsiniz. Değişiklikleri, eklediğiniz 2. adım dizininden bu komut çalıştırmasıyla dağıtabilirsiniz.

$ gcloud app deploy

Bir öğeyi alışveriş sepetinden çıkarma işlevini oluşturduktan sonra bir sonraki bölümde "Toplam fiyatı gör" özelliğini oluşturacağız. get_cart_price işlevi, "Alışveriş sepetini göster" özelliğine benzer şekilde davranır. Bu şekilde, alışveriş sepeti için toplam fiyat üretmek üzere Datastore'daki verileri Inventory.json dosyasıyla karşılaştırabilir. Bu, codelab'in ödemelerle entegre edeceğimiz bir sonraki bölümü için yararlı olacaktır.

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

Son olarak, alışveriş sepetini kaldırma işlevi sunarak 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 ürün eklediğinde, bu kullanıcının isteğini işleme aldığınıza ve onaylandığına dair bir onay mesajı göndermeniz gerekir. Bu hem beklentilerin oluşmasına yardımcı olur hem de iletişimin sürdürülmesini sağlar.

update_shopping_cart işlevinin kapsamını, görüşme kimliğine öğenin eklendiğini veya kaldırıldığını belirten bir mesaj gönderecek ve müşterinin alışveriş sepetini inceleme veya menüyü tekrar görme önerisinde bulunacak ş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

Tamamdır! Kullanıcıların alışveriş sepetine öğe eklemesine, kaldırmasına ve alışveriş sepetindeki öğeleri incelemesine olanak tanıyan, eksiksiz bir alışveriş sepeti deneyimi.

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

$ gcloud app deploy

5. Ödemeler için hazırlanma

Serinin sonraki bölümünde ödeme işleyiciyle entegrasyona hazırlanırken alışveriş sepetinin fiyatını almanın bir yolunu bulmamız gerekiyor. Google Datastore'daki alışveriş sepeti verilerine çapraz referans vererek, her öğenin fiyatını envanterden alarak ve fiyatı alışveriş sepetindeki her bir öğenin miktarıyla çarparak fiyatı bizim için 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 da bu işlevi kullanıp kullanıcıya bir 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)
...

Hepsini birbirine bağlamak için route_message işlevini ve sabit değeri güncelleyerek yukarıdaki mantığı tetikleyelim.

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ığa ilişkin işlevin başarılı olduğunu gösteren bazı ekran görüntülerini aşağıda bulabilirsiniz:

8feacf94ed0ac6c4.png

Codelab'in bir sonraki bölümünde ödeme işleyiciyle entegrasyona hazır olduğumuzda, verileri ödeme işleyiciye iletmek ve ödeme akışını başlatmak için get_cart_price işlevini çağıracağız.

Bu alışveriş sepeti işlevini, uygulamayı dağıtıp temsilcinizle etkileşim kurarak Business Messages görüşmesinde deneyebilirsiniz.

$ gcloud app deploy

6. Tebrikler

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

Bu codelab'de değinmediğimiz bir şey de tüm alışveriş sepetini boşaltma özelliğidir. İsterseniz, uygulamayı "Sepeti boşalt" özelliğini karşılayacak şekilde genişletmeyi deneyebilirsiniz. Çözüm, klonladığınız kaynak kodun 3. adımında bulunmaktadır.

Gelecekteki bir bölümde, kullanıcılarınızın markanızla ödeme işlemini tamamlamalarını sağlamak için harici bir ödeme işleyiciyle entegre edeceğiz.

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

Sohbetteki iyi bir alışveriş sepeti deneyimi, mobil uygulama veya fiziksel mağazadaki alışveriş sepetinden farklı değildir. Öğe ekleyebilme, öğe kaldırabilme ve alışveriş sepetinin fiyatını hesaplayabilme, bu codelab'de incelediğimiz özelliklerden yalnızca birkaçıdır. Gerçek bir alışveriş sepetinden farklı bir şey, ürün ekler veya öğe kaldırırken sepetteki tüm ürünlerin fiyatını herhangi bir zamanda görebilmektir. Bu tür yüksek değerli özellikler, konuşmaya dayalı ticaret deneyiminizin öne çıkmasını sağlar.

Sırada ne var?

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 konuların bazılarına göz atın:

Referans belgeler