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, internetten satın alıp 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ının anahtarı alışveriş sepetidir. Alışveriş sepeti, müşterileriniz hakkında daha fazla bilgi edinmenin ve ilgilenebilecekleri diğer ürünlerle 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ş sepeti nasıl olmalıdır?

Alışveriş sepetleri, başarılı bir online alışveriş deneyiminin anahtarıdır. Business Messages, potansiyel bir müşteriyle ürün hakkında soru-cevap alışverişinde bulunmanın yanı sıra ödemenin tamamlanmasına kadar tüm alışveriş deneyimini kolaylaştırabilir.

9d17537b980d0e62.png

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

Oluşturacağınız uygulama

Codelab serisinin bu bölümünde, 1. bölümde oluşturduğunuz dijital temsilciyi, kullanıcıların bir öğe kataloğuna göz atıp alışveriş sepetine öğe ekleyebilmesi için hayali bir şirket olan Bonjour Meal için genişleteceksiniz.

Bu codelab'de uygulamanız:

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

ab2fb6a4ed33a129.png

Neler öğreneceksiniz?

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

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

Gerekenler

2. Hazırlanma

Bu codelab'de, ilk temsilcinizi 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 ayarlamayla ilgili temel bilgilerden bahsetmeyeceğiz. Bununla birlikte, uygulamanızın temel aldığımız uygulamayla tutarlı olduğundan emin olmak için bir örnek uygulamayı klonlayacağız ve alışveriş sepeti ile ilgili verileri kalıcı hale getirebilmek 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 Örneği'ni 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 kod alanının 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 bu 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

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]

Az önce dağıttığınız kod, Business Messages'dan mesaj almak için webhook içeren bir web uygulaması içerir. 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'in çalışma saatlerini sorması gibi bazı basit sorulara yanıt verir. Bunu, Business Communications Console'daki Temsilci Bilgileri bölümü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 burada temsilcinizle etkileşime geçebilirsiniz.

3. Ürün kataloğu

Envanter sistemi

Çoğu durumda, dahili bir API aracılığıyla doğrudan bir markanın envanteri ile entegrasyon yapabilirsiniz. Diğer durumlarda, bir web sayfasını kazabilir veya kendi envanter izleme sisteminizi oluşturabilirsiniz. Buradaki amacımız bir envanter sistemi oluşturmak değil. Temsilcimiz 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 sohbete ekleyeceğiz ve kullanıcının alışveriş sepetine eklenebilecek öğelere göz atmasına izin vereceğiz.

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 veri okuma

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

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

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

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

Bu sayede envanterden verileri okumak için ihtiyacımız olan bilgileri elde edebiliriz. Şimdi bu ürün bilgilerini sohbete göstermenin bir yolunu bulmamız gerekiyor.

Ürün kataloğunu gösterme

Bu kod laboratuvarının basit olması için tüm envanter öğelerini tek bir zengin kart bandı 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 değerini 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. views.py dosyasının en üstüne bu önerilen yanıt için yeni bir sabit değer ekleyelim.

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

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

Ardından mesajı ayrıştırıp ürün kataloğunu içeren zengin bir kart bandı gönderen yeni bir işleve yönlendiririz. Öncelikle, önerilen yanıta dokunulduğunda "send_product_catalog" işlevini çağıracak şekilde 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 sabit değere (CMD_SHOW_PRODUCT_CATALOG) eşit 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)
...

Ardından akışı tamamlayıp send_product_catalog değerini tanımlayalım. send_product_catalog, daha önce okuduğumuz envanter dosyasından zengin kart rulosunu oluşturan get_menu_carousel, işlevini çağırır.

İşlev tanımları, views.py dosyasının herhangi bir yerine yerleştirilebilir. Aşağıdaki snippet'te, dosyanın üst kısmına eklenmesi gereken iki yeni sabit 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)
...

Bant öğelerinin oluşturulmasını incelerseniz BusinessMessagesSuggestion sınıfının bir örneğini de oluşturduğumuzu görebilirsiniz. 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 öğesini webhook'ınıza gönderir. Aşağıdaki bölümde, öğeyi sepete ekleyebilmek için buna benzer mesajları ayrıştıracağız.

Bu değişiklikleri yaptığımıza göre, web uygulamasını Google App Engine'e dağıtıp deneyimi deneyelim.

$ gcloud app deploy

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

4639da46bcc5230c.png

Öğe ekle'ye dokunursanız temsilcinin önerilen yanıttaki geri gönderme verilerini yansıtması dışında hiç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ğunu çeşitli şekillerde genişletebilirsiniz. Farklı içecek menüsü seçenekleriniz veya vejetaryen seçenekleriniz olabilir. Bantlar veya öneri çipleri kullanmak, kullanıcıların aradıkları ürün grubuna ulaşmak için menü seçeneklerinde ayrıntılı arama yapmalarına olanak tanır. Bu kod laboratuvarının bir uzantısı olarak, ürün kataloğu sistemini kullanıcıların menüde içecekleri yiyeceklerden ayrı olarak görüntüleyebilmesi veya hatta vejetaryen seçenekleri belirtebilmesi için genişletmeyi deneyin.

4. Alışveriş sepeti

Codelab'in bu bölümünde, mevcut ürünlere göz atmamızı sağlayan önceki bölümden yararlanarak alışveriş sepeti işlevini oluşturacağız.

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

Alışveriş sepetinin durumunu takip etmek için web uygulamasında verileri kalıcı olarak tutmamız gerekir. Deneme ve dağıtımı basitleştirmek için verileri kalıcı hale getirmek üzere Google Cloud Platform'daki Google Datastore'u kullanacağız. İleti dizisi kimliği, kullanıcı ile işletme arasında sabit kalır. Bu nedenle, 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 görüşme kimliğini kalıcı hale getirerek başlayalım.

Datastore ile bağlantı kurma

Alışveriş sepeti ile 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. Google Datastore ile etkileşimde bulunmak için bu istemci kitaplığını kullanma hakkında daha fazla bilgiyi resmi dokümanlar bölümünde bulabilirsiniz.

Aşağıdaki snippet, alışveriş sepetini güncelleyecek bir işlev tanımlar. İşlev şu girişleri 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 bantınızda zaten yerleşik olarak bulunur. İşlev, bir Google Datastore istemcisi oluşturur ve hemen bir ShoppingCart varlığı getirir. Bu varlığın anahtarı, ileti dizisi kimliğidir.

Aşağıdaki işlevi views.py dosyanıza kopyalayın. Bu konuyu önümüzdeki 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 öğe eklemek için genişletelim.

Alışveriş sepetine ürün ekleme

Kullanıcı, ürün bandında Öğe ekle önerilen işlemine dokunduğunda, geri gönderme verileri, 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'ınıza gönderilir. "item_name" alanı, inventory.json dosyasında öğeyle ilişkilendirilen benzersiz tanımlayıcıdır.

Mesajdan alınan sepet komutu ve sepet öğesi olduğunda, öğeyi eklemek için koşullu ifadeler yazabiliriz. Burada dikkate alınması gereken bazı uç durumlar, veri deposunun ileti kimliğini hiç görmemesi veya alışveriş sepetinin bu öğeyi ilk kez alması olabilir. Aşağıda, yukarıda tanımlanan update_shopping_cart işlevinin bir uzantısı verilmiştir. Bu değişiklik, alışveriş sepetine Google Datastore tarafından kalıcı olarak kaydedilen bir öğe ekler.

Aşağıdaki snippet, views.py dosyanıza eklenen önceki işlevin bir uzantısıdır. Farklılığı 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'in CMD_DEL_ITEM'te tanımlanan "del-item" dizesini içerdiği senaryoyu desteklemek için genişletilecektir.

Tüm bunları bir araya getirme

route_message işlevine bağlantı eklediğinizden emin olun. Böylece, sepete ürün ekleme mesajı aldığınızda update_shopping_cart işlevi çağrılır. Ayrıca, kod laboratuvarının tamamında 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 anda alışveriş sepetine ürün ekleyebiliyoruz. Değişikliklerinizi Google App Engine'a dağıtırsanız alışveriş sepeti değişikliklerinin GCP Console'daki Google Veri Deposu kontrol paneline yansıtıldığını görebilirsiniz. Google Datastore konsolunun aşağıdaki ekran görüntüsüne bakın. Gördüğünüz tek öğe, sohbet kimliğinin adını taşır ve ardından envanter öğeleriyle ve alışveriş sepetindeki bu öğelerin miktarıyla ilgili bazı ilişkiler bulunur.

619dc18a8136ea69.png

Sonraki bölümde, alışveriş sepeti öğelerini listeleme yöntemi oluşturacağız. Alışveriş sepeti inceleme mekanizması, sepetteki tüm öğeleri, bu öğelerin miktarını ve bir öğeyi sepetten kaldırma seçeneğini bize göstermelidir.

Alışveriş sepetindeki öğeleri inceleme

Alışveriş sepetinin durumunu anlayabilmemiz ve hangi öğeleri kaldırabileceğimizi öğrenebilmemiz için alışveriş sepetindeki öğeleri listelememiz gerekir.

Öncelikle "Alışveriş sepetiniz:" gibi samimi bir mesaj gönderelim. Ardından, "Bir öğeyi kaldır" veya "Bir öğe ekle" için önerilen yanıtları içeren zengin bir kart bandı içeren başka bir mesaj gönderelim. Zengin kart bandı, alışveriş sepetine kaydedilen öğelerin miktarını da listelemelidir.

İşlevimizi yazmaya başlamadan önce dikkat etmemiz gereken bir nokta var: Alışveriş sepetinde yalnızca bir tür ürün varsa bunu bant olarak oluşturamayız. Zengin kart ruloları en az iki kart içermelidir. Diğer yandan, alışveriş sepetinde ürün yoksa alışveriş sepetinin boş olduğunu belirten basit bir mesaj göstermek isteriz.

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 bilgilere sahip olduktan sonra get_inventory_data işlevini çağırır ve alışveriş sepetinin durumunu bildirmek için zengin kart bandı kullanırız. Ayrıca, bir ürünün adını kullanarak kimliğini almamız gerekir. Bu değeri belirlemek için Google Veri Deposu'na bakacak bir işlev tanımlayabiliriz. Bant oluşturulurken, ürün kimliğine göre öğe ekleme veya silme önerilen yanıtları ilişkilendirebiliriz. Aşağıdaki snippet'te tüm bu işlemler gerçekleştirilmektedir. Kodu views.py dosyasının herhangi bir yerine 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 değerini 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ık doğrultusunda, "show-cart" yazdığınızda sepette hiçbir şey olmadığını belirten bir mesaj, sepetteki tek ürünü gösteren zengin bir kart veya birden fazla ürünü gösteren bir kart bandı alırız. Ayrıca, "Toplam fiyatı göster", "Alışveriş sepetini boşalt" ve "Menüyü göster" olmak üzere üç önerilen yanıtımız var.

Yukarıdaki kod değişikliklerini dağıtarak alışveriş sepetinizin eklediğiniz öğeleri izlediğini ve yukarıdaki ekran görüntülerinde gösterildiği gibi sohbet yüzeyinden sepeti inceleyebildiğinizi test edin. Değişiklikleri, değişikliklerinizi eklediğiniz 2. adım dizininden çalıştırılan bu komutla dağıtabilirsiniz.

$ gcloud app deploy

Bir ürünü sepetten kaldırma işlevini oluşturduktan sonra bir sonraki bölümde "Toplam fiyatı göster" özelliğini oluşturacağız. get_cart_price işlevi, alışveriş sepeti için toplam fiyatı oluşturmak amacıyla Datastore'taki verileri inventory.json dosyasıyla çapraz referans yaparak "Alışveriş sepetini göster" özelliğine benzer şekilde çalışır. Bu, ödemelerle entegrasyon yaptığımız kod laboratuvarının bir sonraki bölümünde işinize yarayacaktır.

Alışveriş sepetinden öğe kaldırma

Son olarak, alışveriş sepetini kaldırma işlevini 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, beklentileri belirlemenize yardımcı olur ve sohbetin devam etmesini sağlar.

update_shopping_cart işlevini, sohbet kimliğine öğenin eklendiğini veya kaldırıldığını belirten bir mesaj gönderecek ve alışveriş sepetini incelemeleri ya da menüyü tekrar görmeleri için öneriler sunacak ş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

Bu kadar. Kullanıcının alışveriş sepetine ürün eklemesine, ürün çıkarmasına ve alışveriş sepeti içindeki ürünleri incelemesine olanak tanıyan tam özellikli bir alışveriş sepeti deneyimi.

Bu noktada, Business Messages görüşmesinde alışveriş sepeti işlevini görmek istiyorsanız 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. Ödemelere hazırlanma

Serinin bir sonraki bölümünde bir ödeme işleyiciyle entegrasyona hazırlanırken alışveriş sepetinin fiyatını almamız gerekir. Google Datastore'taki alışveriş sepeti verilerini çapraz referans yaparak, her bir öğenin fiyatını envanterden alarak ve fiyatı sepetteki 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, 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 route_message işlevini ve sabit değeri, yukarıdaki mantığı tetikleyecek şekilde 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 neleri başardığını gösteren bazı ekran görüntülerini aşağıda bulabilirsiniz:

8feacf94ed0ac6c4.png

Codelab'in 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.

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

$ gcloud app deploy

6. Tebrikler

Tebrikler, Business Messages'da alışveriş sepeti deneyimi oluşturmayı başarıyla tamamladınız.

Bu kod laboratuvarının kapsamına almadığımız bir özellik de alışveriş sepetinin tamamını boşaltma özelliğidir. İsterseniz "Alışveriş sepetini boşaltma" özelliğini karşılayacak şekilde uygulamayı 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 tamamlayabilmesi için harici bir ödeme işleyiciyle entegrasyon yapacağız.

İyi bir alışveriş sepeti nasıl olmalıdır?

Sohbette iyi bir alışveriş sepeti deneyimi, mobil uygulama veya fiziksel bir mağazadan farklı değildir. Öğe ekleme, öğe kaldırma ve sepetin fiyatını hesaplama, bu codelab'de incelediğimiz özelliklerden sadece birkaçıdır. Gerçek hayattan bir alışveriş sepetinden farklı olarak, ürün eklerken veya ürün çıkarırken sepetteki tüm öğelerin fiyatını herhangi bir zamanda görebilirsiniz. Bu tür yüksek değerli özellikler, sohbet tabanlı ticaret deneyiminizin öne çıkmasını sağlar.

Sırada ne var?

Hazır olduğunuzda Business Messages'da 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 dokümanları