Я уверен, что все видели эти всплывающие окна в интернет-магазинах, которые говорят что-то вроде «15 человек просматривают этот товар прямо сейчас!» или «105 человек из Мичигана купили это вчера!”. На самом деле я искал один в дикой природе, чтобы включить скриншот, но, конечно, когда он мне нужен, я не могу его найти. В любом случае, я собираюсь создать один из них в Target, который использует реальные данные, полученные из Adobe Analytics.

В этом примере для простоты предположим, что у меня есть страница продукта, на которой выполняются действия A/B в Target для всех посетителей. 50% посетителей этой страницы увидят всплывающее окно, которое «побуждает» их совершить покупку, говоря что-то вроде: «n-количество людей, просмотревших этот товар вчера!» а остальные 50 % посетителей по умолчанию не получат такого всплывающего окна. n-число, используемое в содержимом всплывающего окна, будет реальным числом уникальных посетителей, просмотревших страницу продукта вчера, точно так же, как это отражено в таблице Adobe Analytics Workspace. Число будет меняться ежедневно, чтобы отражать данные предыдущего дня относительно текущей даты; то есть посетитель 12-го числа увидит данные 11-го числа, посетитель 13-го числа увидит данные 12-го числа и т. д.

Пример, который я создам, будет тривиальным, но концепции будут применяться для отображения всего из таблицы рабочей области в предложении Target!

Введение

Как и в отношении почти всего, о чем я пишу в эти дни, для краткости я должен предполагать некоторое предварительное знание Adobe Developer Console, некоторый опыт программирования и знакомство с терминологией Target и Adobe Analytics. .

Вот шаги:

  • Соберите всплывающий элемент, используя номер-заполнитель.
  • Сохраните приведенный выше код как целевое предложение. Обратите внимание на идентификатор предложения после сохранения.
  • Создайте действие в Target с двумя вариантами взаимодействия, используя компоновщик форм, и задайте содержимое доставки в варианте взаимодействия с предложением, которое я только что сохранил выше.
  • Теперь в рабочей области создайте отчет, который показывает число, которое я хочу отобразить в предложении. Используйте Отладчик Oberon, чтобы получить параметры, используемые для создания этого отчета в формате JSON.
  • Настройте проект консоли разработчика, который имеет доступ к Target API и Adobe Analytics API. Мне понадобятся ключи API на следующем шаге.
  • Напишите немного NodeJS, используя библиотеки Adobe AIO, чтобы получить этот номер из рабочей области и обновить свое целевое предложение, чтобы использовать этот номер.
  • Автоматизируйте NodeJS выше, чтобы обновлять предложение новыми данными каждый день (или так часто, как вы хотите, чтобы оно обновлялось).
  • Примечание. В этом примере предположим, что это аудитория только для компьютеров. Я никогда добровольно не призываю никого размещать всплывающие окна на мобильных устройствах!

Создайте всплывающее окно

Поскольку я буду использовать фрагмент HTML в качестве своего целевого предложения, мне нужен автономный, полный фрагмент кода, который создает всплывающее окно, стилизует его и т. д. Вы можете упростить эту часть, просто заменив какой-либо статический текст, например заголовок или призыв к действию. , но я уже сказал всплывающее окно, и моя клавиша удаления не работает. Я не помню синтаксис ключевых кадров для направленной анимации объектов, поэтому давайте просто запустим всплывающее окно:

const popup = document.createElement('div');
popup.id = 'popup';
const content = document.createElement('p');
const keyframes = document.createElement('style');
keyframes.innerText =
  '#popup {font-weight: bold !important;height: 100px !important;width: 350px !important;background-color: #fff !important;border: 3px solid red !important;position: absolute !important;bottom: 0 !important;margin: 1em !important;padding: 1em !important;border-radius: 1em !important;-webkit-animation: popin 4s forwards !important;animation-name: popin 4s forwards !important;}@-webkit-keyframes popin {from {opacity: 0;}to {opacity: 1;}}@keyframes popup {from {opacity: 0;}to {opacity: 1;}}';
document.querySelector('head').insertAdjacentElement('beforeend', keyframes);
let message = 'n people viewed this product yesterday!';
content.innerText = message;
popup.appendChild(content);
document.querySelector('body').appendChild(popup);

Сохраните как предложение

Теперь, когда у меня есть всплывающее окно, которое я хочу использовать в одном из своих целевых приложений, мне нужно сохранить его как предложение. Как только оно будет сохранено как предложение, я смогу доставить его в любом действии и более легко взаимодействовать с ним через Target API.

Поскольку я технически доставляю HTML, мне нужно обернуть этот код тегом a<script>, чтобы он был действительным HTML:

После сохранения предложения откройте это информационное окно, щелкнув значок (i) в столбце изменения, и сохраните идентификатор предложения на будущее!

Создайте активность в цели

Если вы никогда не пользовались компоновщиком форм, это немного отличается от VEC. У вас должны быть готовы все особенности вашей деятельности. Официальная документация по компоновщику форм находится здесь: https://experienceleague.adobe.com/docs/target/using/experiences/form-experience-composer.html?lang=en

Вот и все в Target UI, пока не пришло время активировать тест!

Получение номера, который я хочу использовать в своем предложении

Первый шаг к тому, чтобы получить данные, которые мне нужны, через API, — это сгенерировать их из отчета в рабочей области. Хотя это технически не требуется, инструмент отладки Adobe Oberon делает взаимодействие с API отчетов намного, намного проще. Вот как мои желаемые данные выглядят в рабочей области, количество уникальных посетителей моей страницы продукта вчера:

Чтобы просмотреть (и украсть) базовый запрос API, который сделал этот отчет, мне нужно сначала включить отладчик. Для этого убедитесь, что проект сохранен, и перейдите в раздел Справка › Включить отладчик. Включение отладчика добавит эту маленькую ошибку в ваши таблицы произвольной формы. Щелкните ее, и вы увидите базовые запросы JSON и XML, используемые API Adobe Analytics для создания данных для таблицы. В зависимости от того, сколько работы вы проделали с таблицей, вы также увидите исторические снимки запросов, которые она использовала.

Это то, что я ищу в представлении отладки. Я буду использовать этот кусок JSON позже, когда мне нужно будет программно извлечь эти данные.

Забегая немного вперед, когда я делаю этот вызов через Node SDK, вы видите, что данные такие же, как и в рабочей области. Это потому, что я делаю то же самое, что и рабочее пространство!

const auth = require('@adobe/jwt-auth');
const aaSDK = require('@adobe/aio-lib-analytics');
const dotenv = require('dotenv');

// get my env variables
dotenv.config();

// Adobe API Config vars
const config = {
  clientId: process.env.clientId,
  clientSecret: process.env.clientSecret,
  technicalAccountId: process.env.technicalAccountId,
  orgId: process.env.orgId,
  metaScopes: ['ent_analytics_bulk_ingest_sdk'],
  privateKey: process.env.privateKey.replace(/\\n/g, '\n'), // make the PK readable as a string
};

// json query goes here, I left it out here to save space
const query = {}


async function getAAData() {
  // get bearer token
  let { access_token } = await auth(config);

  //initialize sdk
  const aaClient = await aaSDK.init(process.env.companyId, config.clientId, access_token);

  // Query the data
  const myReport = await aaClient.getReport(query);

  console.log(JSON.stringify(myReport.obj.rows, null, 4));
}

getAAData();

Делаем запрос динамическим

Чтобы сделать мой запрос динамическим и вернуть данные за предыдущий день, мне нужно ввести в него предыдущую дату. Несмотря на то, что диапазон дат рабочей области указан «Вчера», в запросе вы можете видеть, что он использует фактические даты вместо ключевого слова:

globalFilters: [
    {
      type: 'dateRange',
      dateRange: '2022-12-13T00:00:00.000/2022-12-14T00:00:00.000',
    },
  ],

Это хорошо, потому что это означает, что я могу просто заменить жестко заданные даты любым диапазоном дат, который захочу. В данном случае даты «сегодня» и «вчера»

const today = new Date();
const todayDate = today.toISOString().split('T')[0];
const yesterday = today.setDate(today.getDate() - 1);
const yesterdayDate = new Date(yesterday).toISOString().split('T')[0];
const query = {
  rsid: process.env.rsid,
  globalFilters: [
    {
      type: 'dateRange',
      dateRange: `${yesterdayDate}T00:00:00.000/${todayDate}T00:00:00.000`,
    },
  ],
..........

Вероятно, есть более элегантный способ сделать это с более новой библиотекой дат из NPM, но это работает. Так что теперь мой отчет всегда будет извлекать данные за предыдущий день относительно текущей даты. Именно те данные, которые я хочу использовать в своем динамическом предложении.
Примечание при автоматизации: убедитесь, что вы знаете часовой пояс своего сервера! Даты могут отличаться, если используется UTC.

Обновление предложения

Теперь, когда я могу получить нужные мне данные из Adobe Analytics. Я хочу взять эти данные и программно поместить их в свое предложение. Для этого я могу использовать Target AIO Library, чтобы получить предложение, заменить данные и обновить их. Сначала давайте получим предложение:

const auth = require('@adobe/jwt-auth');
const dotenv = require('dotenv');
const targetSDK = require('@adobe/aio-lib-target');

// get my env variables
dotenv.config();

// Adobe API Config vars
const config = {
  clientId: process.env.clientId,
  clientSecret: process.env.clientSecret,
  technicalAccountId: process.env.technicalAccountId,
  orgId: process.env.orgId,
  metaScopes: ['ent_analytics_bulk_ingest_sdk', 'ent_marketing_sdk'],
  privateKey: process.env.privateKey.replace(/\\n/g, '\n'), // make the PK readable as a string
};

async function getTargetOffer() {
  let { access_token } = await auth(config);
  const targetClient = await targetSDK.init(process.env.tenant, config.clientId, access_token);
  const offer = await targetClient.getOfferById(123456); // fake offer number
  console.log(offer);
}

getTargetOffer();

Содержание предложения (также известное как код) можно найти в ответе в разделе obj.content. Я буду вызывать это предложение, получать его текущее содержимое, заменять номер, который в данный момент находится в сообщении, на последний номер из рабочей области, а затем вызывать функцию обновления для обновления содержимого.

Есть несколько способов сделать это, но самый простой — замена регулярных выражений. Сложность здесь заключается в том, что содержимое возвращается в виде набора соединенных строк, потому что Target сохраняет интервалы, которые присутствовали при первоначальном сохранении предложения. Чтобы немного облегчить себе задачу, я собираюсь открыть свое предложение в целевом пользовательском интерфейсе, взять код и минимизировать его. Затем, когда я использую API, содержимое предложения возвращается мне в виде одной строки, которую я могу заменить регулярным выражением:

async function getTargetOffer() {
  let { access_token } = await auth(config);
  const targetClient = await targetSDK.init(process.env.tenant, config.clientId, access_token);
  const offer = await targetClient.getOfferById(123456); // not the real offer ID
  let currentOfferContent = offer.obj.content;
  
  // Im using "700" here just for illustrative purposes
  let updatedOfferContent = currentOfferContent.replace(
    'let message="n people viewed this product yesterday!";',
    `let message="700 people viewed this product yesterday!";`
  );
  console.log(updatedOfferContent);
}

Итак, теперь, когда у меня есть обновленное содержание предложения (тот же самый код предложения с измененным номером), я могу вызвать метод updateOffer с этим новым содержанием.

const updateOffer = await targetClient.updateOffer(123456, { name: offer.obj.name, content: updatedOfferContent });

При использовании целевых API пользовательский интерфейс часто обновляется медленно. Так что, если бы я пошел проверять пользовательский интерфейс предложений, я бы все равно увидел «n» вместо «700». Однако API доставки использует новейшую версию при обслуживании действий, поэтому изменение вступает в силу почти сразу.

Собираем все вместе…

Итак, теперь давайте соберем все вместе. Позвоните в Adobe Analytics для получения вчерашних данных, извлеките этот номер и обновите целевое предложение, указав этот новый номер:

const auth = require('@adobe/jwt-auth');
const dotenv = require('dotenv');
const targetSDK = require('@adobe/aio-lib-target');
const aaSDK = require('@adobe/aio-lib-analytics');

// get my env variables
dotenv.config();

// Adobe API Config vars
const config = {
  clientId: process.env.clientId,
  clientSecret: process.env.clientSecret,
  technicalAccountId: process.env.technicalAccountId,
  orgId: process.env.orgId,
  metaScopes: ['ent_analytics_bulk_ingest_sdk', 'ent_marketing_sdk'],
  privateKey: process.env.privateKey.replace(/\\n/g, '\n'), // make the PK readable as a string
};
const today = new Date();
const todayDate = today.toISOString().split('T')[0];
const yesterday = today.setDate(today.getDate() - 1);
const yesterdayDate = new Date(yesterday).toISOString().split('T')[0];

// the query is truncated here, it may be longer in actuality
const query = {
  rsid: process.env.rsid,
  globalFilters: [
    {
      type: 'dateRange',
      dateRange: `${yesterdayDate}T00:00:00.000/${todayDate}T00:00:00.000`,
    },
  ],
  metricContainer: {
    metrics: [
      {
        columnId: '0',
        id: 'metrics/visitors',
        sort: 'desc',
      },
    ],
  },
  dimension: 'variables/page',
// etc 
// etc
}

async function getAAData() {
  // get bearer token
  let { access_token } = await auth(config);

  //initialize sdk
  const aaClient = await aaSDK.init(process.env.companyId, config.clientId, access_token);

  // Query the data
  const myReport = await aaClient.getReport(query);
  let { rows } = myReport.obj;

  // the first number in the first data row
  let number = rows[0].data.pop();
  return number;
}

async function updateTargetOffer() {
  let { access_token } = await auth(config);
  const targetClient = await targetSDK.init(process.env.tenant, config.clientId, access_token);
  const offer = await targetClient.getOfferById(process.env.offerID); // not the real offer ID
  const number = await getAAData();
  let currentOfferContent = offer.obj.content;
  let updatedOfferContent = currentOfferContent.replace(
    /let message=".+ people viewed this product yesterday!"/g,
    `let message="${number.toLocaleString('en-us')} people viewed this product yesterday!";`
  );
  const updateOffer = await targetClient.updateOffer(process.env.offerID, { name: offer.obj.name, content: updatedOfferContent });
  return updateOffer;
}

updateTargetOffer();

Несколько вещей, которые нужно вызвать в приведенном выше коде:

  • Метаскопы в комплектации; Помимо настройки API в консоли разработчика, вы должны убедиться, что используете правильные метаскопы в вызовах API. Подробнее о метаскопах здесь.
  • Замена регулярного выражения для содержимого сообщения намеренно расплывчата, чтобы учесть пунктуацию в форматировании чисел. Кроме того, теперь это настоящее регулярное выражение, потому что после первого запуска сообщение будет содержать число вместо «.
  • В числовой строке используется форматирование в стиле США с использованием метода toLocaleString. Было бы здорово как-то динамически установить это с помощью целевого геопрофилирования…
  • Для вызова updateOffer по какой-то причине требуется имя предложения, даже если вы уже отправляете идентификатор…
  • Я переместил идентификатор предложения в переменную среды, чтобы сделать этот код более удобным для повторного использования. Вы также можете переместить его в аргумент командной строки.

Автоматизация и заключение

Чтобы эта настройка была действительно полезной, ее необходимо автоматизировать для ежедневного запуска, чтобы в предложении всегда отображались данные за предыдущий день. Поскольку действие просто служит предложением, нет необходимости трогать действие после его активации. Он просто будет обслуживать последнюю версию предложения, какой бы она ни была.

Существует несколько вариантов ежедневного автоматического обновления кода предложения. Задание cron на виртуальной машине Linux или через AWS Lambda с AWS Event Bridge — два наиболее очевидных варианта. Существует множество ресурсов, которые помогут вам узнать об этом!

Выполнение всех описанных выше шагов и включение кода в процесс автоматизации позволит вам запустить Target Experience, который показывает предложение, которое показывает реальные данные!