קנייה של איסוף באינטרנט בחנות: ארוחת בונז'ור – חלק 2 – בניית עגלת קניות

1. מבוא

53003251caaf2be5.png 8826bd8cb0c0f1c7.png

עדכון אחרון: 30.10.2020

יוצרים עגלת קניות ב-Business Messages!

זהו ה-Codelab השני בסדרה שנועדה ליצור תהליך שעובר המשתמש לגבי 'איסוף באינטרנט' של 'איסוף מהחנות'. בתהליכים רבים של מסחר אלקטרוני, עגלת קניות היא המפתח להצלחה של המרת משתמשים ללקוחות משלמים. בנוסף, עגלת הקניות היא דרך להבין טוב יותר את הלקוחות שלכם ומציעה להם הצעות לגבי פריטים אחרים שעשויים לעניין אותם. בשיעור Lab זה נתמקד ביצירת חוויית עגלת הקניות ובפריסת האפליקציה ב-Google App Engine.

איך יוצרים עגלת קניות טובה?

עגלות קניות חיוניות לחוויית קנייה מוצלחת באינטרנט. כפי שמתברר, Business Messages לא רק עוזרת ללקוחות פוטנציאליים לשאול שאלות בנוגע למוצר מסוים, אלא גם לספק ללקוחות פוטנציאליים את חוויית הקנייה כולה, עד להשלמה של תהליך התשלום.

9d17537b980d0e62.png

מעבר לעגלת קניות איכותית, חוויית קנייה טובה מאפשרת למשתמשים לעיין בפריטים לפי קטגוריה ומאפשרת לעסק להמליץ על מוצרים אחרים שהקונה עשוי להתעניין בהם. לאחר הוספת פריטים לעגלת הקניות, המשתמש יכול לבדוק את כל עגלת הקניות, וכן להסיר פריטים או להוסיף עוד פריטים לפני התשלום.

מה תפַתחו

בחלק הזה בסדרת Codelab, עומדים להרחיב את הסוכן הדיגיטלי שיצרתם בחלק 1 עבור החברה הפיקטיבית, Bonjour Meal, כך שמשתמשים יוכלו לעיין בקטלוג של פריטים ולהוסיף פריטים לעגלת הקניות.

ב-Codelab הזה, האפליקציה שלך

  • הצגת קטלוג שאלות ב-Business Messages
  • להציע פריטים שעשויים לעניין את המשתמשים
  • בודקים את תוכן עגלת הקניות ויוצרים סיכום של המחיר הכולל

ab2fb6a4ed33a129.png

מה תלמדו

  • איך פורסים אפליקציות אינטרנט ב-App Engine ב-Google Cloud Platform
  • כיצד להשתמש במנגנון אחסון קבוע כדי לשמור את המצב של עגלת קניות

ה-Codelab הזה מתמקד בהרחבה של הסוכן הדיגיטלי מחלק 1 בסדרת ה-Codelab הזו.

מה צריך?

2. תהליך ההגדרה

ב-Codelab הזה אנחנו יוצאים מנקודת הנחה שיצרתם את הסוכן הראשון שלכם והשלמתם את חלק 1 ב-Codelab. לכן, לא נעבור על היסודות של הפעלת ממשקי Business Messages ו-Business Communications API, יצירת מפתחות של חשבונות שירות, פריסת אפליקציה או הגדרת webhook ב-Business Communications Console. עם זאת, אנחנו נשכפל אפליקציה לדוגמה כדי לוודא שהאפליקציה שלכם תואמת לנתונים שאנחנו בונים עליהם. לאחר מכן, נאפשר את ממשק ה-API של Datastore ב-Google Cloud Platform כדי שיוכל לשמור נתונים הקשורים לעגלת הקניות.

שכפול האפליקציה מ-GitHub

במסוף, משכפלים את Django Echo Bot Sample בספריית העבודה של הפרויקט באמצעות הפקודה הבאה:

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

מעתיקים את קובץ פרטי הכניסה מסוג JSON שנוצר עבור חשבון השירות אל תיקיית המשאבים של הדוגמה, ומשנים את השם של פרטי הכניסה ל-'bm-agent-service-account-credentials.json'.

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

הפעלת Google Datastore API

בחלק הראשון של ה-Codelab הזה, הפעלתם את Business Messages API, BusinessCommunications API ו-Cloud Build API.

ב-Codelab הזה, מאחר שנעבוד עם Google Datastore, נצטרך גם להפעיל את ה-API הזה:

  1. פותחים את Google Datastore API במסוף Google Cloud.
  2. חשוב לוודא שאתם עובדים עם פרויקט GCP הנכון.
  3. לוחצים על Enable.

פריסת האפליקציה לדוגמה

במסוף, עוברים לספריית שלב 2 של הדוגמה.

מריצים את הפקודות הבאות במסוף כדי לפרוס את הדוגמה:

$ gcloud config set project PROJECT_ID*
$ gcloud app deploy
  • PROJECT_ID הוא מזהה הפרויקט שבו השתמשתם כדי להירשם באמצעות ממשקי ה-API.

שימו לב לכתובת ה-URL של האפליקציה שנפרסה בפלט של הפקודה האחרונה:

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

הקוד שפרסת מכיל אפליקציית אינטרנט עם תגובה לפעולה מאתר אחר (webhook) כדי לקבל הודעות מ-Business Messages. הוא מכיל את כל מה שעשינו מחלק 1 של ה-Codelab. אם עוד לא עשית זאת, עליך להגדיר את ה-webhook.

האפליקציה תגיב לכמה בירורים פשוטים, כמו למשל משתמש ששואל לגבי שעות הפעילות של ארוחות בונז'ור. יש לבדוק זאת במכשיר הנייד באמצעות כתובות ה-URL לבדיקה שניתן לאחזר בפרטי הסוכן שב-Business Communications Console. כתובות ה-URL לבדיקה יפעילו את חוויית Business Messages בנייד, ותהיה לך אפשרות להתחיל לתקשר עם הנציג שלך שם.

3. קטלוג המוצרים

מערכת מלאי שטחי פרסום

ברוב המקרים, ניתן לשלב את החשבון ישירות עם מלאי שטחי הפרסום של המותג דרך API פנימי. במקרים אחרים, אתם עשויים לגרד דף אינטרנט או לבנות מערכת משלכם למעקב אחר מלאי. אנחנו לא מתמקדים בבניית מערכת מלאי. אנחנו נשתמש בקובץ סטטי פשוט עם תמונות ופרטי מוצרים בשביל הנציג שלנו. בקטע הזה נשלוף מידע מהקובץ הסטטי הזה, נציג את המידע בשיחה ונאפשר למשתמש לעיין בפריטים הזמינים להוספה לעגלת הקניות.

קובץ המלאי הסטטי נראה כך:

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 כדי לקרוא את הקובץ הזה!

קריאות מהמלאי שלנו

המלאי הוא קובץ סטטי בשם 'inventory.json' שנמצא בספריית ./resources. אנחנו צריכים להוסיף לוגיקת Python ל-View.py כדי לקרוא את התוכן של קובץ ה-JSON ואז להציג אותו בשיחה. עכשיו ניצור פונקציה שקוראת נתונים מקובץ ה-JSON ומחזירה את רשימת המוצרים הזמינים.

ניתן למקם את הגדרת הפונקציה הזו בכל מקום ב-Views.py.

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

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

זה אמור לספק לנו את המידע שאנחנו צריכים כדי לקרוא את הנתונים מהמלאי. עכשיו אנחנו צריכים דרך להציג את פרטי המוצר האלה בדיון.

הצגת קטלוג המוצרים

כדי לפשט את ה-Codelab הזה, יש לנו קטלוג מוצרים כללי להצגת כל הפריטים במלאי בשיחה של Business Messages באמצעות קרוסלה אחת של כרטיסי Rich.

כדי להציג את קטלוג המוצרים, אנחנו עומדים ליצור הצעה לתשובה עם הטקסט 'הצגת תפריט' ו'נתוני דיווח חוזר על המרה' "show-product-catalog". כשמשתמשים יקישו על התשובה המוצעת ואפליקציית האינטרנט שלכם תקבל את נתוני הדיווח החוזר על ההמרה, נשלח את קרוסלת הכרטיסים המתקדמים. נוסיף קבוע חדש עבור ההצעה לתשובה הזו בחלק העליון של views.py.

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

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

מכאן, אנחנו מנתחים את ההודעה ומנתבים אותה לפונקציה חדשה ששולחת קרוסלה של כרטיסים מתקדמים שמכילה את קטלוג המוצרים. קודם כול צריך להרחיב את הפונקציה route_message כך שתתבצע קריאה לפונקציה 'send_product_catalog' כשמקישים על התשובה המוצעת, ואז נגדיר את הפונקציה.

בקטע הקוד הבא, מוסיפים עוד תנאי להצהרת if בפונקציה route_message כדי לבדוק אם normalized_message שווה לערך הקבוע שהגדרנו קודם, CMD_SHOW_PRODUCT_CATALOG.

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

...
def route_message(message, conversation_id):
    '''
    Routes the message received from the user to create a response.

    Args:
        message (str): The message text received from the user.
        conversation_id (str): The unique id for this user and agent.
    '''
    normalized_message = message.lower()

    if normalized_message == CMD_RICH_CARD:
        send_rich_card(conversation_id)
    elif normalized_message == CMD_CAROUSEL_CARD:
        send_carousel(conversation_id)
    elif normalized_message == CMD_SUGGESTIONS:
        send_message_with_suggestions(conversation_id)
    elif normalized_message == CMD_BUSINESS_HOURS_INQUIRY:
        send_message_with_business_hours(conversation_id)
    elif normalized_message == CMD_ONLINE_SHOPPING_INQUIRY:
        send_online_shopping_info_message(conversation_id)
    elif normalized_message == CMD_SHOW_PRODUCT_CATALOG:
        send_product_catalog(conversation_id)
    else:
        echo_message(message, conversation_id)
...

בואו נקפיד להשלים את התהליך ולהגדיר את send_product_catalog. הפונקציה send_product_catalog מפעילה את get_menu_carousel,. הפעולה הזו יוצרת את הקרוסלה של כרטיסי התצוגה המתקדמת מקובץ המלאי שקראנו קודם לכן.

ניתן למקם את הגדרות הפונקציות בכל מקום ב-Views.py. שימו לב שבקטע הקוד הבא נעשה שימוש בשני קבועים חדשים שצריך להוסיף לחלק העליון של הקובץ.

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

אם בוחנים את היצירה של פריטי הקרוסלה, אנחנו יוצרים גם מופע של המחלקה BusinessMessagesSuggestion. כל הצעה מייצגת בחירה של משתמש למוצר בקרוסלה. כשמשתמש מקיש על ההצעה לתשובה, מערכת Business Messages שולחת ל-webhook את ה-PostbackData שכולל קובץ JSON שמתאר את הפריט ואת הפעולה שהמשתמש רוצה לבצע (להוסיף או להסיר ממנה) אל ה-webhook. בקטע הבא ננתח הודעות שנראות כך, כדי לאפשר הוספה בפועל של הפריט לעגלת הקניות.

לאחר שערכנו שינויים אלו, נפרוס את יישום האינטרנט ב-Google App Engine וננסה את החוויה!

$ gcloud app deploy

אחרי שפלטפורמת השיחה טעונה במכשיר הנייד, שלחו את ההודעה 'show-product-catalog' כדי לראות קרוסלה של מוצרים שנראית כך.

4639da46bcc5230c.png

אם מקישים על הוספת פריט,שום דבר לא קורה חוץ מאשר על ידי הנציג לחזור על נתוני הדיווח החוזר על ההמרה מהתשובה המוצעת. בקטע הבא נשתמש בקטלוג המוצרים ונשתמש בו כדי לבנות את עגלת הקניות שאליה הפריט יתווסף.

אתם יכולים להרחיב את קטלוג המוצרים שיצרתם במגוון דרכים. יכול להיות שיהיו לכם אפשרויות שונות בתפריט המשקאות או אפשרויות צמחוניות. קרוסלות או צ'יפים של הצעות הם דרך מצוינת לתת למשתמשים פירוט של האפשרויות בתפריט כדי להגיע לקבוצה של מוצרים שהם מחפשים. כהרחבה ל-Codelab הזה, אפשר להרחיב את מערכת קטלוג המוצרים כדי שמשתמש יוכל לראות את המשקאות בנפרד מהאוכל בתפריט, ואפילו להגדיר אפשרויות צמחוניות.

4. עגלת הקניות

בחלק זה של ה-Codelab, נבנה את הפונקציונליות של עגלת הקניות על סמך הקטע הקודם, שמאפשר לנו לעיין במוצרים הזמינים.

בין היתר, חוויית השימוש העיקרית של עגלת הקניות מאפשרת למשתמשים להוסיף פריטים לעגלת הקניות, להסיר פריטים מעגלת הקניות, לעקוב אחר המספר של כל פריט בעגלת הקניות ולבדוק את הפריטים שבעגלת הקניות.

כדי לעקוב אחרי מצב עגלת הקניות, אנחנו צריכים לשמור את הנתונים באפליקציית האינטרנט. כדי לפשט את הניסויים והפריסה, נשתמש ב-Google Datastore ב-Google Cloud Platform כדי לשמור את הנתונים. מזהה השיחה נותר קבוע בין המשתמש לעסק, כך שנוכל להשתמש בו כדי לשייך משתמשים לפריטים בעגלת הקניות.

קודם כול צריך להתחבר ל-Google Datastore ולשמור את מזהה השיחה כשאנחנו רואים אותו.

התחברות ל-Datastore

אנחנו נקשר ל-Google Datastore בכל פעם שתבוצע אינטראקציה עם עגלת הקניות, לדוגמה, כשמשתמש מוסיף או מוחק פריט. מידע נוסף על השימוש בספריית הלקוח הזו לצורך אינטראקציה עם Google Datastore זמין בתיעוד הרשמי.

קטע הקוד הבא מגדיר פונקציה לעדכון עגלת הקניות. הפונקציה מקבלת את הקלט הבא: conversation_id ו-message. message מכיל קובץ JSON שמתאר את הפעולה שהמשתמש רוצה לבצע. קובץ זה כבר מובנה בקרוסלה שבה מוצג קטלוג המוצרים. הפונקציה יוצרת לקוח של Google Datastore ומאחזרת באופן מיידי ישות ShoppingCart, שבה המפתח הוא מזהה השיחה.

העתק את הפונקציה הבאה לקובץ views.py שלך. בקטע הבא נרחיב על כך.

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)

נרחיב את הפונקציה הזו כדי להוסיף פריט לעגלת הקניות.

הוספת פריטים לעגלת הקניות

כשהמשתמש מקיש על הצעה לפעולה של הוספת פריט בקרוסלת המוצרים, ה-PostbackData מכיל קובץ JSON שמתאר את הפעולה שהמשתמש רוצה לבצע. מילון ה-JSON כולל שני מפתחות, "action" ו-"item_name", ומילון JSON זה נשלח אל ה-webhook. השדה item_name הוא המזהה הייחודי שמשויך לפריט ב-Inventory.json.

אחרי שהשגנו את פקודת עגלת הקניות ופריט העגלה מנותח מההודעה, נוכל לכתוב הצהרות מותנות כדי להוסיף את הפריט. נקודות קצה שכדאי לחשוב עליהן: אם מערכת Datastore מעולם לא ראה את מזהה השיחה או שעגלת הקניות מקבלת את הפריט הזה בפעם הראשונה. בהמשך מופיעה הרחבה של הפונקציונליות update_shopping_cart שהוגדרה למעלה. השינוי הזה מוסיף פריט לעגלת הקניות שנשמרת ב-Google Datastore.

קטע הקוד הבא הוא תוסף של הפונקציה הקודמת שנוספה ל-Views.py. אפשר להוסיף את ההפרש או להעתיק את קטע הקוד ולהחליף את הגרסה הקיימת של הפונקציה update_shopping_cart.

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

def update_shopping_cart(conversation_id, message):
    credentials = service_account.Credentials.from_service_account_file(
      SERVICE_ACCOUNT_LOCATION)
    inventory = get_inventory_data()

    cart_request = json.loads(message)
    cart_cmd = cart_request["action"]
    cart_item = cart_request["item_name"]

    item_name = inventory['food'][int(cart_item)]['name']

    client = datastore.Client(credentials=credentials)
    key = client.key('ShoppingCart', conversation_id)
    entity = datastore.Entity(key=key)
    result = client.get(key)

    if result is None:
        if cart_cmd == CMD_ADD_ITEM:
            entity.update({
                item_name: 1
            })

    else:
        if cart_cmd == CMD_ADD_ITEM:
            if result.get(item_name) is None:
                result[item_name] = 1
            else:
                result[item_name] = result[item_name] + 1

        entity.update(result)
    client.put(entity)

נרחיב את הפונקציה הזו מאוחר יותר כדי לתמוך בתרחיש שבו השדה cart_cmd מכיל את המחרוזת 'del-item' שמוגדרת ב-CMD_DEL_ITEM.

קישור של כל התוכן

חשוב לוודא שהוספת את שירותי השרברבות בפונקציה route_message כדי שאם תתקבל הודעה שמבקשת להוסיף פריט לעגלה, תתבצע קריאה לפונקציה update_shopping_cart. תצטרכו גם להגדיר קבוע להוספת פריטים באמצעות המוסכמה שבה אנחנו משתמשים ב-Codelab.

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

...

CMD_DEL_ITEM = 'del-item'

...

def route_message(message, conversation_id):
    '''
    Routes the message received from the user to create a response.

    Args:
        message (str): The message text received from the user.
        conversation_id (str): The unique id for this user and agent.
    '''
    normalized_message = message.lower()

    if normalized_message == CMD_RICH_CARD:
        send_rich_card(conversation_id)
    elif normalized_message == CMD_CAROUSEL_CARD:
        send_carousel(conversation_id)
    elif normalized_message == CMD_SUGGESTIONS:
        send_message_with_suggestions(conversation_id)
    elif normalized_message == CMD_BUSINESS_HOURS_INQUIRY:
        send_message_with_business_hours(conversation_id)
    elif normalized_message == CMD_ONLINE_SHOPPING_INQUIRY:
        send_online_shopping_info_message(conversation_id)
    elif normalized_message == CMD_SHOW_PRODUCT_CATEGORY:
        send_product_catalog(conversation_id)
    elif CMD_ADD_ITEM in normalized_message or CMD_DEL_ITEM in normalized_message:
       update_shopping_cart(conversation_id, message)
    else:
        echo_message(message, conversation_id)

...

כרגע אנחנו יכולים להוסיף פריטים לעגלת הקניות. אם תפרוס את השינויים שלך ב-Google App Engine, תוכל לראות את השינויים בעגלת הקניות במרכז השליטה של Google Datastore שנמצא במסוף GCP. בצילום המסך שלמטה של מסוף Google Datastore יש ישות אחת שנקראת על שם מזהה השיחה, ואחריה חלק מהקשרים לפריטי המלאי ולכמות הפריטים שנמצאים בעגלת הקניות.

619dc18a8136ea69.png

בקטע הבא ניצור דרך להציג את הפריטים בעגלת הקניות. מנגנון הבדיקה של עגלת הקניות צריך להציג לנו את כל הפריטים בעגלת הקניות, את כמות הפריטים האלה ואפשרות להסיר פריט מהעגלה.

בדיקת פריטים בעגלת הקניות

רישום הפריטים בעגלת הקניות הוא הדרך היחידה שבה אנחנו יכולים להבין את מצב עגלת הקניות ולדעת אילו פריטים אנחנו יכולים להסיר.

קודם כול צריך לשלוח הודעה ידידותית, כמו 'זוהי עגלת הקניות שלך:', ואחריה הודעה נוספת שמכילה קרוסלה של כרטיסי Rich עם הצעות לתשובות שקשורות ל'הסרת הפריט' או ל'הוספת כרטיס'. בקרוסלה של הכרטיסים המתקדמים צריכה להופיע גם כמות הפריטים שנשמרו בעגלת הקניות.

משהו שצריך לשים לב אליו לפני שאנחנו נכנסים וכותבים את הפונקציה: אם יש רק סוג אחד של פריט בעגלת הקניות, לא נוכל לעבד אותו כקרוסלה. קרוסלות עם כרטיסים מתקדמים חייבות להכיל לפחות שני כרטיסים. מצד שני, אם אין פריטים בעגלת הקניות, כדאי להציג הודעה פשוטה שאומרת שהעגלה ריקה.

על סמך זאת, נגדיר פונקציה בשם send_shopping_cart. הפונקציה הזו מתחברת ל-Google Datastore ומבקשת ישות של ShoppingCart על סמך מזהה השיחה. לאחר שנעשה זאת, נקרא לפונקציה get_inventory_data ונשתמש בקרוסלה של כרטיסים מתקדמים כדי לדווח על מצב עגלת הקניות. נצטרך גם לקבל את מזהה המוצר לפי שם, ונוכל להצהיר על פונקציה לבדיקה של Google Datastore כדי לקבוע את הערך הזה. בזמן יצירת הקרוסלה, אנחנו יכולים לשייך הצעות לתשובות כדי למחוק פריטים או להוסיף פריטים לפי מזהה מוצר. קטע הקוד שלמטה מבצע את כל הפעולות האלו. העתק את הקוד מכל מקום אל views.py.

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

...
def get_id_by_product_name(product_name):
  inventory = get_inventory_data()
  for item in inventory['food']:
    if item['name'] == product_name:
      return int(item['id'])
  return False


def send_shopping_cart(conversation_id):
  credentials = service_account.Credentials.from_service_account_file(
      SERVICE_ACCOUNT_LOCATION)

  # Retrieve the inventory data
  inventory = get_inventory_data()

  # Pull the data from Google Datastore
  client = datastore.Client(credentials=credentials)
  key = client.key('ShoppingCart', conversation_id)
  result = client.get(key)

  shopping_cart_suggestions = [
      BusinessMessagesSuggestion(
          reply=BusinessMessagesSuggestedReply(
              text='See total price', postbackData='show-cart-price')),
      BusinessMessagesSuggestion(
          reply=BusinessMessagesSuggestedReply(
              text='Empty the cart', postbackData='empty-cart')),
      BusinessMessagesSuggestion(
          reply=BusinessMessagesSuggestedReply(
              text='See the menu', postbackData=CMD_SHOW_PRODUCT_CATALOG)),
  ]

  if result is None or len(result.items()) == 0:
    message_obj = BusinessMessagesMessage(
        messageId=str(uuid.uuid4().int),
        representative=BOT_REPRESENTATIVE,
        text='There are no items in your shopping cart.',
        suggestions=shopping_cart_suggestions)

    send_message(message_obj, conversation_id)
  elif len(result.items()) == 1:

    for product_name, quantity in result.items():
      product_id = get_id_by_product_name(product_name)

      fallback_text = ('You have one type of item in the shopping cart')

      rich_card = BusinessMessagesRichCard(
          standaloneCard=BusinessMessagesStandaloneCard(
              cardContent=BusinessMessagesCardContent(
                  title=product_name,
                  description=f'{quantity} in cart.',
                  suggestions=[
                      BusinessMessagesSuggestion(
                          reply=BusinessMessagesSuggestedReply(
                              text='Remove one',
                              postbackData='{'+f'"action":"{CMD_DEL_ITEM}","item_name":"{product_id}"'+'}'))
                  ],
                  media=BusinessMessagesMedia(
                      height=BusinessMessagesMedia.HeightValueValuesEnum.MEDIUM,
                      contentInfo=BusinessMessagesContentInfo(
                          fileUrl=inventory['food'][product_id]
                          ['image_url'],
                          forceRefresh=False)))))

      message_obj = BusinessMessagesMessage(
          messageId=str(uuid.uuid4().int),
          representative=BOT_REPRESENTATIVE,
          richCard=rich_card,
          suggestions=shopping_cart_suggestions,
          fallback=fallback_text)

      send_message(message_obj, conversation_id)
  else:
    cart_carousel_items = []

    # Iterate through the cart and generate a carousel of items
    for product_name, quantity in result.items():
      product_id = get_id_by_product_name(product_name)

      cart_carousel_items.append(
          BusinessMessagesCardContent(
              title=product_name,
              description=f'{quantity} in cart.',
              suggestions=[
                  BusinessMessagesSuggestion(
                      reply=BusinessMessagesSuggestedReply(
                          text='Remove one',
                          postbackData='{'+f'"action":"{CMD_DEL_ITEM}","item_name":"{product_id}"'+'}'))
              ],
              media=BusinessMessagesMedia(
                  height=BusinessMessagesMedia.HeightValueValuesEnum.MEDIUM,
                  contentInfo=BusinessMessagesContentInfo(
                      fileUrl=inventory['food'][product_id]
                      ['image_url'],
                      forceRefresh=False))))

    rich_card = BusinessMessagesRichCard(
        carouselCard=BusinessMessagesCarouselCard(
            cardContents=cart_carousel_items,
            cardWidth=BusinessMessagesCarouselCard.CardWidthValueValuesEnum
            .MEDIUM))

    fallback_text = ''

    # Construct a fallback text for devices that do not support carousels
    for card_content in rich_card.carouselCard.cardContents:
      fallback_text += (
          card_content.title + '\n\n' + card_content.description + '\n\n' +
          card_content.media.contentInfo.fileUrl +
          '\n---------------------------------------------\n\n')

    message_obj = BusinessMessagesMessage(
        messageId=str(uuid.uuid4().int),
        representative=BOT_REPRESENTATIVE,
        richCard=rich_card,
        suggestions=shopping_cart_suggestions,
        fallback=fallback_text,
    )

    send_message(message_obj, conversation_id)

...

יש לוודא שכבר הגדרת את CMD_SHOW_CART בחלק העליון של views.py ולהתקשר ל-send_shopping_cart אם המשתמש שולח הודעה הכוללת את 'show-cart'.

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

...
def route_message(message, conversation_id):
    '''
    Routes the message received from the user to create a response.

    Args:
        message (str): The message text received from the user.
        conversation_id (str): The unique id for this user and agent.
    '''
    normalized_message = message.lower()

    if normalized_message == CMD_RICH_CARD:
        send_rich_card(conversation_id)
    elif normalized_message == CMD_CAROUSEL_CARD:
        send_carousel(conversation_id)
    elif normalized_message == CMD_SUGGESTIONS:
        send_message_with_suggestions(conversation_id)
    elif normalized_message == CMD_BUSINESS_HOURS_INQUIRY:
        send_message_with_business_hours(conversation_id)
    elif normalized_message == CMD_ONLINE_SHOPPING_INQUIRY:
        send_online_shopping_info_message(conversation_id)
    elif normalized_message == CMD_SHOW_PRODUCT_CATEGORY:
        send_product_catalog(conversation_id)
    elif CMD_ADD_ITEM in normalized_message or CMD_DEL_ITEM in normalized_message:
        update_shopping_cart(conversation_id, message)
    elif normalized_message == CMD_SHOW_CART:
        send_shopping_cart(conversation_id)
    else:
        echo_message(message, conversation_id)
...

34801776a97056ac.png

בהתאם ללוגיקה שהשקנו בפונקציה send_shopping_cart, כשמקלידים 'הצגת עגלת הקניות', מקבלים הודעה על כך שאין פריטים בעגלת הקניות, כרטיס מתקדם שמציג את הפריט בעגלת הקניות או קרוסלה של כרטיסים שמציגה מספר פריטים. בנוסף, יש לנו שלוש הצעות לתשובות: 'להצגת המחיר הכולל', 'ריקון עגלת הקניות' ו'הצגת התפריט'.

נסה לפרוס את השינויים בקוד שלמעלה כדי לבדוק שעגלת הקניות עוקבת אחר פריטים שאתה מוסיף, ושניתן לבדוק את עגלת הקניות מתוך רשימת השיחות, כפי שמוצג בצילומי המסך שלמעלה. אתם יכולים לפרוס את השינויים באמצעות הרצת הפקודה הזו מהספרייה של Step-2 שבה מוסיפים את השינויים.

$ gcloud app deploy

לאחר בניית הפונקציונליות להסרת פריט מעגלת הקניות, ניצור את התכונה 'הצגת המחיר הכולל' בקטע הבא. הפונקציה get_cart_price תפעל באופן דומה לתכונה 'הצגת עגלת קניות', בכך שהיא תצליב נתונים ב-Datastore עם קובץInventory.json כדי לחשב מחיר כולל עבור עגלת הקניות. החלק הזה שימושי לחלק הבא ב-Codelab שבו אנחנו משתלבים עם תשלומים.

הסרת פריטים מעגלת הקניות

לבסוף, נוכל להשלים את ההתנהגות של עגלת הקניות על ידי הוספת פונקציה להסרה של עגלת הקניות. מחליפים את הפונקציה הקיימת update_shopping_cart בקטע הקוד הבא.

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)

שליחה של הודעת אישור

כשהמשתמש מוסיף פריט לעגלת הקניות, צריך לשלוח הודעת אישור שמאשרת את הפעולה הרלוונטית ושטיפלתם בבקשה. זה לא רק עוזר לקבוע ציפיות, אלא גם מעודד את השיחה.

נרחיב את הפונקציה update_shopping_cart כך שתשלח למזהה השיחה הודעה שהפריט נוסף או הוסר, ונספק הצעות לבדוק את עגלת הקניות או לראות שוב את התפריט.

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

זהו זה! חוויית שימוש מלאה בעגלת הקניות שמאפשרת למשתמש להוסיף פריטים, להסיר פריטים ולבדוק פריטים בעגלת הקניות.

בשלב הזה, כדי לראות את הפונקציונליות של עגלת הקניות בשיחה של Business Messages, צריך לפרוס את האפליקציה כדי ליצור קשר עם הנציג. כדי לעשות את זה, מריצים את הפקודה הזו בספרייה של Step-2.

$ gcloud app deploy

5. הכנות לקראת ביצוע תשלומים

כהכנה לשילוב עם מעבד תשלומים בחלק הבא של הסדרה, אנחנו זקוקים דרך לקבלת המחיר של עגלת הקניות. בואו ניצור פונקציה שמאחזרת את המחיר עבורנו על ידי הצלבה של נתוני עגלת הקניות ב-Google Datastore, אחזור המחיר של כל פריט מהמלאי והכפלת המחיר בכמות של כל פריט בעגלת הקניות.

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

...

ולבסוף, אנחנו יכולים לצרוך את הפונקציה הזו ולשלוח הודעה למשתמש.

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

כדי לקשר את כולם, נעדכן את הפונקציה route_message ואת הקבוע כך שיפעיל את הלוגיקה שלמעלה.

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

הנה כמה צילומי מסך שמדגימים את השגת הלוגיקה שלמעלה:

8feacf94ed0ac6c4.png

כשנהיה מוכנים לשלב את החשבון עם ספק שירותי התשלומים בחלק הבא של ה-Codelab, נקרא לפונקציה get_cart_price כדי להעביר את הנתונים אל מעבד התשלומים ולהתחיל את תהליך התשלום.

שוב, אפשר לנסות את הפונקציונליות הזו של עגלת הקניות בשיחה של Business Messages על ידי פריסת האפליקציה ואינטראקציה עם הנציג.

$ gcloud app deploy

6. מזל טוב

מזל טוב, הצלחתם ליצור חוויית משתמש בעגלת הקניות ב-Business Messages.

משהו שלא הסברנו עליו ב-Codelab הזה הוא התכונה לריקון כל עגלת הקניות. אם ברצונך, אפשר להרחיב את היישום כדי למלא את התכונה 'ריקון עגלת הקניות'. הפתרון זמין בשלב 3 של קוד המקור ששכפלת.

באחד מהסעיפים הבאים נשלב שילוב עם חברה חיצונית לעיבוד תשלומים, כדי לאפשר למשתמשים שלכם להשלים עסקת תשלום עם המותג שלכם.

איך יוצרים עגלת קניות טובה?

חוויה טובה של עגלת קניות בשיחה אינה שונה מאפליקציה לנייד או בחנות פיזית. היכולת להוסיף פריטים, להסיר פריטים ולחשב את המחיר של עגלת הקניות הן רק חלק מהתכונות שדיברנו עליהן ב-Codelab הזה. משהו שונה מעגלת הקניות בעולם האמיתי הוא היכולת לראות את המחיר של כל הפריטים בעגלת הקניות בכל רגע נתון, כאשר מוסיפים פריטים או מסירים פריטים. תכונות כאלה בעלות ערך גבוה יבדלו את חוויית המסחר שלכם בשיחה!

מה השלב הבא?

כשתהיו מוכנים, כדאי לעיין בכמה מהנושאים הבאים כדי לקבל מידע על אינטראקציות מורכבות יותר שאפשר להשיג ב-Business Messages:

מסמכי עזר