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

קנייה באינטרנט ואיסוף בחנות:
Bonjour Meal – חלק 2 – בניית עגלת קניות

מידע על Codelab זה

subjectהעדכון האחרון: נוב׳ 14, 2024
account_circleנכתב על ידי Adam Chan

1.‏ מבוא

53003251caaf2be5.png 8826bd8cb0c0f1c7.png

תאריך עדכון אחרון: 30 באוקטובר 2020

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

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

מה הופך עגלת קניות ליעילה?

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

9d17537b980d0e62.png

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

מה תפַתחו

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

ב-codelab הזה, האפליקציה שלכם:

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

ab2fb6a4ed33a129.png

מה תלמדו

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

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

מה צריך להכין

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

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

יצירת עותקים כפולים של האפליקציה מ-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

בחלק 1 של סדנת הקוד הזו הפעלתם את Business Messages API,‏ Business communications API ו-Cloud Build API.

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

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

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

בטרמינל, עוברים לספרייה step-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 של סדנת הקוד. אם עדיין לא עשיתם זאת, עליכם להגדיר את ה-webhook.

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

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

מערכת מלאי

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

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

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. אנחנו צריכים להוסיף ל-views.py קצת לוגיקה של Python כדי לקרוא את התוכן של קובץ ה-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
...

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

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

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

כדי להציג את קטלוג המוצרים, נוצר הצעה לתגובה עם הטקסט 'הצגת התפריט' ו-postbackData‏ '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, שיוצר את הקרוסלה של כרטיסי ה-Rich מתוך קובץ המלאי שקראנו קודם.

אפשר למקם את הגדרות הפונקציות בכל מקום בקובץ 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 שולחת את postbackData שמכיל JSON שמתאר את הפריט ואת הפעולה שהמשתמש רוצה לבצע (הוספה או הסרה מעגלת הקניות) אל ה-webhook שלכם. בקטע הבא ננתח הודעות שנראות כך כדי שנוכל להוסיף את הפריט לעגלת הקניות.

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

$ gcloud app deploy

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

4639da46bcc5230c.png

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

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

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

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

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

כדי לעקוב אחרי המצב של עגלת הקניות, אנחנו צריכים לשמור נתונים באפליקציית האינטרנט. כדי לפשט את הניסוי והפריסה, נשתמש ב-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)

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

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

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

אחרי שניתוחנו את הפקודה לעגלת הקניות ואת פריט עגלת הקניות מההודעה, נוכל לכתוב הצהרות תנאי כדי להוסיף את הפריט. מקרים קיצוניים שצריך לקחת בחשבון הם אם מזהה השיחה מעולם לא הופיע במאגר הנתונים או אם הפריט הזה מופיע בעגלת הקניות בפעם הראשונה. הקוד הבא הוא הרחבה של הפונקציונליות של 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 תופעל. תצטרכו גם להגדיר קבוע להוספת פריטים לפי המוסכמה שבה אנחנו משתמשים לאורך הקוד.

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

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

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

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

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

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

בהתאם לכך, נגדיר פונקציה בשם 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, כשמקלידים 'show-cart', אנחנו מקבלים הודעה על כך שאין דבר בסוגר, כרטיס עשיר שבו מוצג הפריט היחיד בסוגר או קרוסלה של כרטיסים שבה מוצגים כמה פריטים. בנוסף, יש לנו שלוש הצעות לתשובות: 'הצגת המחיר הכולל', 'פינוי עגלת הקניות' ו'הצגת התפריט'.

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

$ gcloud app deploy

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

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

לבסוף, כדי להשלים את ההתנהגות של עגלת הקניות, אנחנו יכולים להוסיף פונקציונליות להסרת העגלה. מחליפים את הפונקציה הקיימת 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

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

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

$ gcloud app deploy

6.‏ מזל טוב

מעולה, סיימת ליצור חוויית קנייה בעגלת קניות ב-Business Messages.

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

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

מה הופך עגלת קניות ליעילה?

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

מה השלב הבא?

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

מסמכי עזרה