{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Retail Demo Store Messaging Workshop - Braze\n", "\n", "In this workshop we will use [Braze](https://braze.com/) to add the ability to personalize marketing messages to customers of the Retail Demo Store using customer behavioral data and the Personalize models you trained in the prior workshops. We are going to define a campaign in Braze to send target users an email with product recommendations from the Amazon Personalize campaign we created in the Personalization workshop earlier. You will also create an email template using the Braze Connected Content feature. When the campaign is launched, Connected Content will send emails to the users that belong in the target group. These emails will be formatted using the Personalize Campaign you created earlier.\n", "\n", "Recommended Time: 1 hour\n", "\n", "## Prerequisites\n", "\n", "This module uses Amazon Personalize to generate and associate personalized product recommendations for users. The content of this workshop is presented with the assumption that you have either completed the [Personalization](../1-Personalization/Lab-1-Introduction-and-data-preparation.ipynb) workshop or those resources have been pre-provisioned in your AWS environment. If you are unsure and attending an AWS managed event such as a workshop, check with your event lead.\n", "\n", "This part of the workshop requires access to a Braze account. If you do not have a Braze account, please contact your Braze representative. We also assume that you have completed at least the [Getting Started Braze LAB course](https://lab.braze.com/).\n", "\n", "You will need to create a Braze API key for the duration of the workshop. This API key will be used to create a set of temporary users for the workshop. These temporary users will be deleted at the end of the workshop." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Architecture\n", "\n", "Before you set up Braze to send personalized messages to users, let's review the relevant parts of the Retail Demo Store architecture and how it uses Braze to integrate with the machine learning campaigns created in Personalize.\n", "\n", "![Braze Personalization Architecture](images/braze-personalize-arch.png)\n", "\n", "Braze will send emails to your users based on their behavior or based on attributes of their user profiles. A discussion of Braze campaigns is beyond the scope of this document but Braze will ingest real-time events from mobile and web applications. This data can be used to identify users and to build user profiles that can be used to determine when to message or email users. \n", "\n", "This event data flow will happen in parallel to the same behavioral event data being sent to Amazon Personalize. In this workshop, the demo store uses Amplify to send events to Personalize as shown in the diagram. This data is used to train a recommendations model that can in turn be used by Braze Connected Content to personalize content to users of your mobile and web applications when your Braze campaign runs. \n", "\n", "Braze Connected content will be able to get these recommendations via a recommendation service running in AWS. Earlier in this workshop, this service was deployed using by the Cloud Formation templates you used to deploy the workshop environment in ECS." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Building a Braze Email Template\n", "\n", "Before you can run a campaign in Braze, you will need to create an email template that will be used to email your users. This template will use the Braze Connected Content feature to fetch product recommendations for the users you will be emailing via your campaign.\n", "\n", "First, you will need to get the name of the load balancer for the Retail Demo Store recommendations service you deployed at the beginning of the Retail Demo Store workshop.\n", "\n", "The following code will query the AWS account you are using for this workshop, get a list of load balancers, and find the one that belongs to the Retail Demo Store and has the tag `recommendations`. This URL will be stored in the `recommendations_elb_domain_name` variable after you run this code." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import boto3\n", "\n", "elbv2 = boto3.client('elbv2')\n", " \n", "recommendations_elb_domain_name = None\n", " \n", "elbs_paginator = elbv2.get_paginator('describe_load_balancers')\n", "for elbs_page in elbs_paginator.paginate():\n", " for elb in elbs_page['LoadBalancers']:\n", " tags_response = elbv2.describe_tags(ResourceArns = [ elb['LoadBalancerArn'] ])\n", " for tag_desc in tags_response['TagDescriptions']:\n", " for tag in tag_desc['Tags']:\n", " if tag['Key'] == 'RetailDemoStoreServiceName' and tag['Value'] == 'recommendations':\n", " recommendations_elb_domain_name = elb['DNSName']\n", " break\n", " if recommendations_elb_domain_name:\n", " break\n", " if recommendations_elb_domain_name:\n", " break\n", " if recommendations_elb_domain_name:\n", " break\n", " \n", "assert recommendations_elb_domain_name is not None, 'Unable to find Recommendations service ELB'\n", " \n", "print('Recommendations DNS name: {}'.format(recommendations_elb_domain_name))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next, you are going to prepare the HTML content for the email itself. For simplicity, this workshop will only support HTML email templates. The template provided with this workshop comes from the Braze sample templates, and can be easily adapted to other platforms if needed.\n", "\n", "The following code will modify the `./braze-templates/braze-connected-content-email-template.html` template file in the workbook directory you found this notebook in. This code will insert the correct Connected Content code for you, and will prepare a ZIP file which you will use in a moment to upload your template to your Braze campaign. Make sure that you have your Jupyter directory tab open to the directory which contains this notebook." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import zipfile\n", "\n", "template_file_path = './braze-templates/braze-connected-content-email-template.html'\n", "zip_file_path = './braze-templates/braze-connected-content-email-template.zip'\n", "\n", "begin = '{% connected_content http://'\n", "end = '/recommendations?userID={{${user_id} | remove_first: \"test_user_rds_\"}}&fullyQualifyImageUrls=1&numResults=4 :save result %}'\n", "\n", "connected_content_call = f'{begin}{recommendations_elb_domain_name}{end}'\n", "\n", "text = open(template_file_path).read()\n", "new_text = '\\n'.join(connected_content_call if line.startswith('{% connected_content') else line for line in text.splitlines())\n", "open(template_file_path, 'w').write(new_text)\n", "\n", "zipfile.ZipFile(zip_file_path, mode='w').write(template_file_path)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Once the above code has completed, check that there is a `braze-connected-content-email-template.zip` in the `./braze-templates` directory in Jupyter. You will need this file when you configure a campaign in the following steps. Once you have confirmed that this file is in the `./braze-templates` directory, download it to your computer:\n", "\n", "![](./images/braze-jupyter-download.png)\n", "\n", "### Anatomy of a Braze Connected Content Template\n", "\n", "Before you create your campaign, let's quickly review how the Connected Content template you just built works.\n", "\n", "The template is an HTML file that uses the [Braze Liquid](https://www.braze.com/docs/user_guide/personalization_and_dynamic_content/liquid/) templating language to format product recommendation results from a Braze Connected Content call.\n", "\n", "The Connected Content call happens at the very top of the HTML template:\n", "\n", "```\n", "{% connected_content http:///recommendations?userID={{${user_id} | remove_first: \"test_user_rds_\"}}&fullyQualifyImageUrls=1&numResults=4 :save result %}\n", "```\n", "\n", "This line tells Braze to call the Retail Demo Store recommendation REST service when building the email for the user specified in `user_id`. This user ID is stripped of its `test_user_rds_` prefix using a Braze Liquid template function. This is done because the Retail Demo Store uses integer user IDs, and the users you will upload will be created with string user identifiers prefixed with a workshop-specific identifier. This was done for the workshop so that your Braze account does not encounter user ID conflicts with existing user IDs. This is not strictly required for a production deployment of Amazon Personalize and Braze.\n", "\n", "When the Connected Content call returns successfully, the JSON results returned from the recommendation service are stored in the variable `result` via the `:save` function." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### What Does the Recommendations Service Return?\n", "\n", "In order to understand how the Connected Content template works, it is helpful to take a look at the responses that are returned from the recommendations service. \n", "\n", "The code below will invoke your recommendations service, and return a response for one of the users that exists in the Retail Demo Store user database. You can change the `user_id` below to any valid ID.\n", "\n", "If this code fails with `recommendations_elb_domain_name` not defined, you will need to re-run the recommendations service code two cells before this one.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import requests\n", "import json\n", "\n", "user_id = 10\n", "response = requests.get('http://{}/recommendations?userID={}&fullyQualifyImageUrls=1&numResults=4'.format(recommendations_elb_domain_name, user_id))\n", "print(json.dumps(response.json(), indent = 2))\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The return value from the recommendation service should look like:\n", "\n", "```\n", "[\n", " {\n", " \"product\": {\n", " \"id\": \"2\",\n", " \"url\": \"http://recs.cloudfront.net/#/product/2\",\n", " \"sk\": \"\",\n", " \"name\": \"Striped Shirt\",\n", " \"category\": \"apparel\",\n", " \"style\": \"shirt\",\n", " \"description\": \"A classic look for the summer season.\",\n", " \"price\": 9.99,\n", " \"image\": \"http://recs.cloudfront.net/images/apparel/1.jpg\",\n", " \"featured\": \"true\"\n", " }\n", " },\n", " {\n", " \"product\": {\n", " \"id\": \"1\",\n", " \"url\": \"http://recs.cloudfront.net/#/product/1\",\n", " \"sk\": \"\",\n", " \"name\": \"Black Leather Backpack\",\n", " \"category\": \"accessories\",\n", " \"style\": \"bag\",\n", " \"description\": \"Our handmade leather backpack will look great at the office or out on the town.\",\n", " \"price\": 109.99,\n", " \"image\": \"http://recs.cloudfront.net/images/accessories/1.jpg\",\n", " \"featured\": \"true\"\n", " }\n", " },\n", "\n", " ...\n", "]\n", "```\n", "\n", "Setting up a similar service in your deployment of Amazon Personalize will give you the ability to surface product recommendations directly into Braze and other services that require real-time user personalization such as a mobile application or a web application. \n", "\n", "The recommendation service deployed in the workshop queries a Personalize campaign with the `user_id` that you provide. Personalize will return a list of recommended product IDs for the user. The service then looks up the catalog ID for each product and decorates the response with the product URL, image, name, etc. These attributes should be available in your catalog management system. In the case of the Retail Demo Store, there is a catalog service that provides (simulated) catalog items for the workshop. The advantage of a service like this is that your target services like Braze do not need to do an additional lookup in order to get the information they require in order to surface a recommendation to a user, such as product name and the image associated with the product.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### How Does the Template Use the Returned Results?\n", "\n", "Once the `result` variable is populated with the above JSON, the template can reference the product data in the variable using the Braze Liquid templating language.\n", "\n", "A product cell in the template might look like this:\n", "\n", "```\n", "\n", "\t\n", "\t\t\n", "\t\t\n", "\t\t\n", "\t\n", "
{{result[1].product.name}}{{result[1].product.price}}
\n", "```\n", "\n", "Note the `result[1].product.name` variable will be replaced with the name of the product that is first in the list of products returned, and the same goes for `result[1].product.price` for that product's price. The same can be done for other product attributes such as the product's image." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Creating a Braze Campaign\n", "\n", "In a separate browser window or tab, log in to the Braze [console](https://dashboard.braze.com/auth). You will need to have a Braze account configured for you to continue this workshop, or use your existing Braze account.\n", "\n", "![Braze Login Screen](images/braze-create-template-0.png)\n", "\n", "On the left hand pane of the Braze console, click the Templates & Media link. This will show the Templates list screen. Click the From File button under Basic Email Templates at the top of the screen.\n", "\n", "![](images/braze-create-template-1.png)\n", "\n", "Click the Upload from File button:\n", "\n", "![](images/braze-create-template-2.png)\n", "\n", "Navigate to the directory in which you downloaded the `braze-connected-content-email-template.zip` file you created earlier. Click the Open or Save button to upload the ZIP file to Braze.\n", "\n", "![](images/braze-create-template-3.png)\n", "\n", "Braze will validate the file, and return a status window. Click the Build Email button to continue.\n", "\n", "![](images/braze-create-template-4.png)\n", "\n", "On the next screen, give the template a name, such as `Braze Connected Content Workshop Template`.\n", "\n", "![](images/braze-create-template-5.png)\n", "\n", "On the bottom right of the screen, click the Save Template button. \n", "\n", "Make sure to click the Save button often, the editor will not auto-save your changes!\n", "\n", "![](images/braze-create-template-6.png)\n", "\n", "Scroll back up and click the Edit Email Body link\n", "\n", "![](images/braze-create-template-7.png)\n", "\n", "This will take you to the HTML template editor. \n", "\n", "At the very top, you should see the Connected Content call that calls the Personalize recommendation service, with the URL of your deployment of the recommendations service.\n", "\n", "![](images/braze-create-template-8.png)\n", "\n", "When you are finished looking at your template, click the Done button at the bottom right of the editor screen.\n", "\n", "![](images/braze-create-template-9.png)\n", "\n", "Before navigating away from the template editor, make sure to click the Save Template button on the bottom right of the template window, or your earlier changes will be lost.\n", "\n", "![](images/braze-create-template-10.png)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Create Braze User Profiles\n", "\n", "Before you can run a campaign from Braze, you will need to create a set of demo user profiles with valid emails in Braze. \n", "\n", "For this workshop, we recommend using a set of test emails that (preferably) map to your own email address or to an email address that you can control.\n", "\n", "The following code will use the Braze Users API to create 5 test users in your Braze account. These users will all have a dummy user ID that starts with `test_user_rds`. This will allow us to separate these profiles from other user IDs that you may have in Braze, and will allow us to easily delete the test users later.\n", "\n", "For this code to work, you will need to get:\n", "\n", "1. Your Braze endpoint URL, you can look this up [here](https://www.braze.com/docs/api/basics/#api-definitions). Once you have your endpoint, set the `braze_endpoint_url` variable to the URL from the documentation.\n", "2. Your Braze API key. We recommend creating an API key just for this workshop, with the `users.track` and the `users.delete` permission at a minimum. This key can be deleted after your are done with the workshop. Change the `braze_api_key` variable below to your API key value.\n", "3. Your email. Your email account will need to support plus email addressing for this workshop to work correctly. Input your email into the `your_email` variable below." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import requests\n", "\n", "braze_endpoint_url = ''\n", "braze_api_key = ' If you are participating in an AWS managed event such as a workshop and using an AWS provided temporary account, you can skip the following cleanup steps unless otherwise instructed." ] } ], "metadata": { "kernelspec": { "display_name": "conda_python3", "language": "python", "name": "conda_python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.3-final" } }, "nbformat": 4, "nbformat_minor": 4 }