Skip to content

Commit 13ae2f8

Browse files
author
Moritz Haller
committed
Add keyword-segment lists, funnel-site lists and classifier for Brave Ads purchase intent
1 parent a1ec4e5 commit 13ae2f8

35 files changed

+4988
-19
lines changed

test/BUILD.gn

+3
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,9 @@ test("brave_unit_tests") {
280280
"//brave/vendor/bat-native-ads/src/bat/ads/internal/frequency_capping/permission_rules/ads_per_day_frequency_cap_unittest.cc",
281281
"//brave/vendor/bat-native-ads/src/bat/ads/internal/frequency_capping/permission_rules/ads_per_hour_frequency_cap_unittest.cc",
282282
"//brave/vendor/bat-native-ads/src/bat/ads/internal/sorts/ads_history_sort_unittest.cc",
283+
"//brave/vendor/bat-native-ads/src/bat/ads/internal/purchase_intent/funnel_sites_unittest.cc",
284+
"//brave/vendor/bat-native-ads/src/bat/ads/internal/purchase_intent/keywords_unittest.cc",
285+
"//brave/vendor/bat-native-ads/src/bat/ads/internal/purchase_intent/purchase_intent_classifier_unittest.cc",
283286
]
284287
}
285288

vendor/bat-native-ads/BUILD.gn

+16
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ source_set("headers") {
7474
"include/bat/ads/ads_history.h",
7575
"include/bat/ads/bundle_state.h",
7676
"include/bat/ads/category_content.h",
77+
"include/bat/ads/purchase_intent_signal_history.h",
7778
"include/bat/ads/client_info_platform_type.h",
7879
"include/bat/ads/client_info.h",
7980
"include/bat/ads/confirmation_type.h",
@@ -104,6 +105,7 @@ source_set("ads") {
104105
"src/bat/ads/ads_history.cc",
105106
"src/bat/ads/bundle_state.cc",
106107
"src/bat/ads/category_content.cc",
108+
"src/bat/ads/purchase_intent_signal_history.cc",
107109
"src/bat/ads/client_info.cc",
108110
"src/bat/ads/confirmation_type.cc",
109111
"src/bat/ads/creative_ad_notification_info.cc",
@@ -219,6 +221,20 @@ source_set("ads") {
219221
"src/bat/ads/internal/time.h",
220222
"src/bat/ads/internal/uri_helper.cc",
221223
"src/bat/ads/internal/uri_helper.h",
224+
"src/bat/ads/internal/purchase_intent/funnel_site_info.cc",
225+
"src/bat/ads/internal/purchase_intent/funnel_site_info.h",
226+
"src/bat/ads/internal/purchase_intent/funnel_sites.cc",
227+
"src/bat/ads/internal/purchase_intent/funnel_sites.h",
228+
"src/bat/ads/internal/purchase_intent/funnel_keyword_info.cc",
229+
"src/bat/ads/internal/purchase_intent/funnel_keyword_info.h",
230+
"src/bat/ads/internal/purchase_intent/segment_keyword_info.cc",
231+
"src/bat/ads/internal/purchase_intent/segment_keyword_info.h",
232+
"src/bat/ads/internal/purchase_intent/keywords.cc",
233+
"src/bat/ads/internal/purchase_intent/keywords.h",
234+
"src/bat/ads/internal/purchase_intent/purchase_intent_classifier.cc",
235+
"src/bat/ads/internal/purchase_intent/purchase_intent_classifier.h",
236+
"src/bat/ads/internal/purchase_intent/purchase_intent_signal_info.cc",
237+
"src/bat/ads/internal/purchase_intent/purchase_intent_signal_info.h",
222238
]
223239

224240
deps = [
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/* Copyright (c) 2020 The Brave Authors. All rights reserved.
2+
* This Source Code Form is subject to the terms of the Mozilla Public
3+
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
4+
* You can obtain one at http://mozilla.org/MPL/2.0/. */
5+
6+
#ifndef BAT_ADS_PURCHASE_INTENT_SIGNAL_HISTORY_H_
7+
#define BAT_ADS_PURCHASE_INTENT_SIGNAL_HISTORY_H_
8+
9+
#include <stdint.h>
10+
#include <string>
11+
#include <map>
12+
#include <deque>
13+
14+
#include "bat/ads/export.h"
15+
#include "bat/ads/result.h"
16+
17+
namespace ads {
18+
19+
struct ADS_EXPORT PurchaseIntentSignalHistory {
20+
PurchaseIntentSignalHistory();
21+
PurchaseIntentSignalHistory(
22+
const PurchaseIntentSignalHistory& properties);
23+
~PurchaseIntentSignalHistory();
24+
25+
bool operator==(
26+
const PurchaseIntentSignalHistory& rhs) const;
27+
28+
bool operator!=(
29+
const PurchaseIntentSignalHistory& rhs) const;
30+
31+
std::string ToJson() const;
32+
Result FromJson(
33+
const std::string& json,
34+
std::string* error_description = nullptr);
35+
36+
uint64_t timestamp_in_seconds;
37+
uint16_t weight = 0;
38+
};
39+
40+
using PurchaseIntentSignalSegmentHistoryList =
41+
std::deque<PurchaseIntentSignalHistory>;
42+
using PurchaseIntentSignalSegmentHistoryMap =
43+
std::map<std::string, PurchaseIntentSignalSegmentHistoryList>;
44+
45+
} // namespace ads
46+
47+
#endif // BAT_ADS_PURCHASE_INTENT_SIGNAL_HISTORY_H_

vendor/bat-native-ads/src/bat/ads/internal/ads_impl.cc

+78-8
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
#include "bat/ads/ads_history.h"
1515
#include "bat/ads/confirmation_type.h"
1616
#include "bat/ads/ad_notification_info.h"
17-
17+
#include "bat/ads/purchase_intent_signal_history.h"
1818
#include "bat/ads/internal/ads_impl.h"
1919
#include "bat/ads/internal/classification_helper.h"
2020
#include "bat/ads/internal/locale_helper.h"
@@ -41,6 +41,8 @@
4141
#include "bat/ads/internal/frequency_capping/permission_rules/ads_per_day_frequency_cap.h"
4242
#include "bat/ads/internal/frequency_capping/permission_rules/ads_per_hour_frequency_cap.h"
4343
#include "bat/ads/internal/sorts/ads_history_sort_factory.h"
44+
#include "bat/ads/internal/purchase_intent/purchase_intent_signal_info.h"
45+
#include "bat/ads/internal/purchase_intent/purchase_intent_classifier.h"
4446

4547
#include "base/guid.h"
4648
#include "base/rand_util.h"
@@ -89,6 +91,9 @@ AdsImpl::AdsImpl(AdsClient* ads_client)
8991
ad_conversions_(std::make_unique<AdConversions>(
9092
this, ads_client, client_.get())),
9193
user_model_(nullptr),
94+
purchase_intent_classifier_(std::make_unique<PurchaseIntentClassifier>(
95+
kPurchaseIntentSignalLevel, kPurchaseIntentClassificationThreshold,
96+
kPurchaseIntentSignalDecayTimeWindow)),
9297
is_initialized_(false),
9398
is_confirmations_ready_(false),
9499
sustained_ad_notification_interaction_timer_id_(0),
@@ -658,6 +663,8 @@ void AdsImpl::OnPageLoaded(
658663

659664
CheckAdConversion(url);
660665

666+
ExtractPurchaseIntentSignal(url);
667+
661668
if (helper::Uri::MatchesDomainOrHost(url,
662669
last_shown_ad_notification_.target_url)) {
663670
BLOG(INFO) << "Site visited " << url
@@ -704,6 +711,39 @@ void AdsImpl::OnPageLoaded(
704711
<< previous_tab_url_;
705712
}
706713

714+
void AdsImpl::ExtractPurchaseIntentSignal(
715+
const std::string& url) {
716+
if (!ShouldClassifyPagesIfTargeted()) {
717+
return;
718+
}
719+
720+
if (!TestSearchState(url) &&
721+
helper::Uri::MatchesDomainOrHost(url, previous_tab_url_)) {
722+
return;
723+
}
724+
725+
PurchaseIntentSignalInfo purchase_intent_signal =
726+
purchase_intent_classifier_->ExtractIntentSignal(url);
727+
728+
if (purchase_intent_signal.segments.empty() &&
729+
purchase_intent_signal.timestamp_in_seconds == 0) {
730+
return;
731+
}
732+
733+
GeneratePurchaseIntentSignalHistoryEntry(purchase_intent_signal);
734+
BLOG(INFO) << "Purchase intent signal extracted for " << url;
735+
}
736+
737+
void AdsImpl::GeneratePurchaseIntentSignalHistoryEntry(
738+
const PurchaseIntentSignalInfo& purchase_intent_signal) {
739+
for (const auto& segment : purchase_intent_signal.segments) {
740+
PurchaseIntentSignalHistory history;
741+
history.timestamp_in_seconds = purchase_intent_signal.timestamp_in_seconds;
742+
history.weight = purchase_intent_signal.weight;
743+
client_->AppendToPurchaseIntentSignalHistoryForSegment(segment, history);
744+
}
745+
}
746+
707747
void AdsImpl::CheckAdConversion(
708748
const std::string& url) {
709749
DCHECK(!url.empty());
@@ -838,8 +878,8 @@ std::string AdsImpl::ClassifyPage(
838878
return winning_category;
839879
}
840880

841-
std::vector<std::string> AdsImpl::GetWinningCategories() {
842-
std::vector<std::string> winning_categories;
881+
WinningCategoryList AdsImpl::GetWinningCategories() {
882+
WinningCategoryList winning_categories;
843883

844884
if (!ShouldClassifyPagesIfTargeted()) {
845885
return winning_categories;
@@ -929,6 +969,22 @@ AdsImpl::GetPageScoreCache() const {
929969
return page_score_cache_;
930970
}
931971

972+
PurchaseIntentWinningCategoryList
973+
AdsImpl::GetWinningPurchaseIntentCategories() {
974+
PurchaseIntentWinningCategoryList winning_categories;
975+
976+
PurchaseIntentSignalSegmentHistoryMap purchase_intent_signal_history =
977+
client_->GetPurchaseIntentSignalHistory();
978+
if (purchase_intent_signal_history.empty()) {
979+
return winning_categories;
980+
}
981+
982+
winning_categories = purchase_intent_classifier_->GetWinningCategories(
983+
purchase_intent_signal_history, kPurchaseIntentMaxSegments);
984+
985+
return winning_categories;
986+
}
987+
932988
void AdsImpl::TestShoppingData(
933989
const std::string& url) {
934990
if (!IsInitialized()) {
@@ -1088,12 +1144,24 @@ void AdsImpl::ServeAdNotificationIfReady(
10881144
}
10891145
}
10901146

1091-
auto categories = GetWinningCategories();
1147+
CategoryList categories = GetCategoriesToServeAd();
10921148
ServeAdNotificationFromCategories(categories);
10931149
}
10941150

1151+
CategoryList AdsImpl::GetCategoriesToServeAd() {
1152+
CategoryList categories;
1153+
WinningCategoryList contextual_categories = GetWinningCategories();
1154+
categories.insert(categories.end(),
1155+
contextual_categories.begin(), contextual_categories.end());
1156+
PurchaseIntentWinningCategoryList purchase_intent_categories =
1157+
GetWinningPurchaseIntentCategories();
1158+
categories.insert(categories.end(),
1159+
purchase_intent_categories.begin(), purchase_intent_categories.end());
1160+
return categories;
1161+
}
1162+
10951163
void AdsImpl::ServeAdNotificationFromCategories(
1096-
const std::vector<std::string>& categories) {
1164+
const CategoryList& categories) {
10971165
std::string catalog_id = bundle_->GetCatalogId();
10981166
if (catalog_id.empty()) {
10991167
FailedToServeAdNotification("No ad catalog");
@@ -1118,7 +1186,7 @@ void AdsImpl::ServeAdNotificationFromCategories(
11181186

11191187
void AdsImpl::OnServeAdNotificationFromCategories(
11201188
const Result result,
1121-
const std::vector<std::string>& categories,
1189+
const CategoryList& categories,
11221190
const CreativeAdNotificationList& ads) {
11231191
auto eligible_ads = GetEligibleAds(ads);
11241192
if (!eligible_ads.empty()) {
@@ -1132,6 +1200,8 @@ void AdsImpl::OnServeAdNotificationFromCategories(
11321200
BLOG(INFO) << " " << category;
11331201
}
11341202

1203+
// TODO(https://github.com/brave/brave-browser/issues/8486): Brave Ads
1204+
// Purchase Intent segments should not fall back to parent segments
11351205
if (ServeAdNotificationFromParentCategories(categories)) {
11361206
return;
11371207
}
@@ -1140,8 +1210,8 @@ void AdsImpl::OnServeAdNotificationFromCategories(
11401210
}
11411211

11421212
bool AdsImpl::ServeAdNotificationFromParentCategories(
1143-
const std::vector<std::string>& categories) {
1144-
std::vector<std::string> parent_categories;
1213+
const CategoryList& categories) {
1214+
CategoryList parent_categories;
11451215
for (const auto& category : categories) {
11461216
auto pos = category.find_last_of(kCategoryDelimiter);
11471217
if (pos == std::string::npos) {

vendor/bat-native-ads/src/bat/ads/internal/ads_impl.h

+15-2
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,14 @@
2424
#include "bat/ads/internal/ad_conversions.h"
2525
#include "bat/ads/internal/ad_notification_result_type.h"
2626
#include "bat/ads/internal/ad_notifications.h"
27-
2827
#include "bat/usermodel/user_model.h"
28+
#include "bat/ads/internal/purchase_intent/purchase_intent_classifier.h"
2929

3030
namespace ads {
3131

32+
using WinningCategoryList = std::vector<std::string>;
33+
using CategoryList = std::vector<std::string>;
34+
3235
class Client;
3336
class Bundle;
3437
class AdsServe;
@@ -37,6 +40,8 @@ class AdConversions;
3740
class FrequencyCapping;
3841
class ExclusionRule;
3942
class PermissionRule;
43+
class PurchaseIntentClassifier;
44+
struct PurchaseIntentSignalInfo;
4045

4146
class AdsImpl : public Ads {
4247
public:
@@ -153,6 +158,11 @@ class AdsImpl : public Ads {
153158
const std::string& url,
154159
const std::string& content) override;
155160

161+
void ExtractPurchaseIntentSignal(
162+
const std::string& url);
163+
void GeneratePurchaseIntentSignalHistoryEntry(
164+
const PurchaseIntentSignalInfo& purchase_intent_signal);
165+
156166
void MaybeClassifyPage(
157167
const std::string& url,
158168
const std::string& content);
@@ -161,7 +171,8 @@ class AdsImpl : public Ads {
161171
const std::string& url,
162172
const std::string& content);
163173

164-
std::vector<std::string> GetWinningCategories();
174+
WinningCategoryList GetWinningCategories();
175+
PurchaseIntentWinningCategoryList GetWinningPurchaseIntentCategories();
165176
std::string GetWinningCategory(
166177
const std::vector<double>& page_score);
167178

@@ -208,6 +219,7 @@ class AdsImpl : public Ads {
208219
const Result result,
209220
const std::vector<std::string>& categories,
210221
const CreativeAdNotificationList& ads);
222+
CategoryList GetCategoriesToServeAd();
211223
void ServeAdNotification(
212224
const CreativeAdNotificationList& ads);
213225
void OnServeAdNotification(
@@ -293,6 +305,7 @@ class AdsImpl : public Ads {
293305
std::unique_ptr<FrequencyCapping> frequency_capping_;
294306
std::unique_ptr<AdConversions> ad_conversions_;
295307
std::unique_ptr<usermodel::UserModel> user_model_;
308+
std::unique_ptr<PurchaseIntentClassifier> purchase_intent_classifier_;
296309

297310
private:
298311
bool is_initialized_;

vendor/bat-native-ads/src/bat/ads/internal/client.cc

+25
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "bat/ads/internal/client.h"
77

88
#include "bat/ads/ad_history.h"
9+
#include "bat/ads/purchase_intent_signal_history.h"
910
#include "bat/ads/internal/classification_helper.h"
1011
#include "bat/ads/internal/filtered_ad.h"
1112
#include "bat/ads/internal/filtered_category.h"
@@ -80,6 +81,30 @@ std::deque<AdHistory> Client::GetAdsShownHistory() const {
8081
return client_state_->ads_shown_history;
8182
}
8283

84+
void Client::AppendToPurchaseIntentSignalHistoryForSegment(
85+
const std::string& segment,
86+
const PurchaseIntentSignalHistory& history) {
87+
if (client_state_->purchase_intent_signal_history.find(segment) ==
88+
client_state_->purchase_intent_signal_history.end()) {
89+
client_state_->purchase_intent_signal_history.insert({segment, {}});
90+
}
91+
92+
client_state_->purchase_intent_signal_history.at(
93+
segment).push_back(history);
94+
95+
if (client_state_->purchase_intent_signal_history.at(segment).size() >
96+
kMaximumEntriesPerSegmentInPurchaseIntentSignalHistory) {
97+
client_state_->purchase_intent_signal_history.at(segment).pop_back();
98+
}
99+
100+
SaveState();
101+
}
102+
103+
const PurchaseIntentSignalSegmentHistoryMap&
104+
Client::GetPurchaseIntentSignalHistory() const {
105+
return client_state_->purchase_intent_signal_history;
106+
}
107+
83108
AdContent::LikeAction Client::ToggleAdThumbUp(
84109
const std::string& creative_instance_id,
85110
const std::string& creative_set_id,

vendor/bat-native-ads/src/bat/ads/internal/client.h

+5
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ class Client {
3131
void AppendAdHistoryToAdsShownHistory(
3232
const AdHistory& ad_history);
3333
std::deque<AdHistory> GetAdsShownHistory() const;
34+
void AppendToPurchaseIntentSignalHistoryForSegment(
35+
const std::string& segment,
36+
const PurchaseIntentSignalHistory& history);
37+
const PurchaseIntentSignalSegmentHistoryMap&
38+
GetPurchaseIntentSignalHistory() const;
3439
AdContent::LikeAction ToggleAdThumbUp(
3540
const std::string& creative_instance_id,
3641
const std::string& creative_set_id,

0 commit comments

Comments
 (0)