Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Round-robin Brave Ads campaign advertisers to reduce the frequency of showing the same ad #4459

Merged
merged 1 commit into from
Feb 6, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 32 additions & 10 deletions components/brave_ads/browser/bundle_state_database.cc
Original file line number Diff line number Diff line change
@@ -22,8 +22,8 @@ namespace brave_ads {

namespace {

const int kCurrentVersionNumber = 3;
const int kCompatibleVersionNumber = 3;
const int kCurrentVersionNumber = 4;
const int kCompatibleVersionNumber = 4;

} // namespace

@@ -171,6 +171,7 @@ bool BundleStateDatabase::CreateAdInfoTable() {
"region VARCHAR,"
"campaign_id LONGVARCHAR,"
"daily_cap INTEGER DEFAULT 0 NOT NULL,"
"advertiser_id LONGVARCHAR,"
"per_day INTEGER DEFAULT 0 NOT NULL,"
"total_max INTEGER DEFAULT 0 NOT NULL,"
"PRIMARY KEY(region, uuid))");
@@ -330,8 +331,9 @@ bool BundleStateDatabase::InsertOrUpdateAdInfo(const ads::AdInfo& info) {
"INSERT OR REPLACE INTO ad_info "
"(creative_set_id, advertiser, notification_text, "
"notification_url, start_timestamp, end_timestamp, uuid, "
"campaign_id, daily_cap, per_day, total_max, region) "
"VALUES (?, ?, ?, ?, datetime(?), datetime(?), ?, ?, ?, ?, ?, ?)"));
"campaign_id, daily_cap, advertiser_id, per_day, total_max, "
"region) VALUES (?, ?, ?, ?, datetime(?), datetime(?), ?, ?, ?, "
"?, ?, ?, ?)"));

ad_info_statement.BindString(0, info.creative_set_id);
ad_info_statement.BindString(1, info.advertiser);
@@ -342,9 +344,10 @@ bool BundleStateDatabase::InsertOrUpdateAdInfo(const ads::AdInfo& info) {
ad_info_statement.BindString(6, info.uuid);
ad_info_statement.BindString(7, info.campaign_id);
ad_info_statement.BindInt(8, info.daily_cap);
ad_info_statement.BindInt(9, info.per_day);
ad_info_statement.BindInt(10, info.total_max);
ad_info_statement.BindString(11, *it);
ad_info_statement.BindString(9, info.advertiser_id);
ad_info_statement.BindInt(10, info.per_day);
ad_info_statement.BindInt(11, info.total_max);
ad_info_statement.BindString(12, *it);
if (!ad_info_statement.Run()) {
return false;
}
@@ -418,7 +421,7 @@ bool BundleStateDatabase::GetAdsForCategory(
"ai.notification_text, ai.notification_url, "
"ai.start_timestamp, ai.end_timestamp, "
"ai.uuid, ai.region, ai.campaign_id, ai.daily_cap, "
"ai.per_day, ai.total_max FROM ad_info AS ai "
"ai.advertiser_id, ai.per_day, ai.total_max FROM ad_info AS ai "
"INNER JOIN ad_info_category AS aic "
"ON aic.ad_info_uuid = ai.uuid "
"WHERE aic.category_name = ? and "
@@ -440,8 +443,9 @@ bool BundleStateDatabase::GetAdsForCategory(
info.uuid = info_sql.ColumnString(6);
info.campaign_id = info_sql.ColumnString(8);
info.daily_cap = info_sql.ColumnInt(9);
info.per_day = info_sql.ColumnInt(10);
info.total_max = info_sql.ColumnInt(11);
info.advertiser_id = info_sql.ColumnString(10);
info.per_day = info_sql.ColumnInt(11);
info.total_max = info_sql.ColumnInt(12);
ads->emplace_back(info);
}

@@ -558,6 +562,15 @@ bool BundleStateDatabase::Migrate() {
break;
}

case 3: {
if (!MigrateV3toV4()) {
LOG(ERROR) << "DB: Error migrating database from v3 to v4";
return false;
}

break;
}

default: {
NOTREACHED();
return false;
@@ -620,4 +633,13 @@ bool BundleStateDatabase::MigrateV2toV3() {
return GetDB().Execute(sql.c_str());
}

bool BundleStateDatabase::MigrateV3toV4() {
std::string sql = "ALTER TABLE ad_info ADD advertiser_id LONGVARCHAR;";
if (!GetDB().Execute(sql.c_str())) {
return false;
}

return false;
}

} // namespace brave_ads
1 change: 1 addition & 0 deletions components/brave_ads/browser/bundle_state_database.h
Original file line number Diff line number Diff line change
@@ -87,6 +87,7 @@ class BundleStateDatabase {
bool Migrate();
bool MigrateV1toV2();
bool MigrateV2toV3();
bool MigrateV3toV4();

sql::Database db_;
sql::MetaTable meta_table_;
1 change: 1 addition & 0 deletions vendor/bat-native-ads/include/bat/ads/ad_info.h
Original file line number Diff line number Diff line change
@@ -29,6 +29,7 @@ struct ADS_EXPORT AdInfo {
std::string start_timestamp;
std::string end_timestamp;
unsigned int daily_cap;
std::string advertiser_id;
unsigned int per_day;
unsigned int total_max;
std::vector<std::string> regions;
3 changes: 3 additions & 0 deletions vendor/bat-native-ads/resources/bundle-schema.json
Original file line number Diff line number Diff line change
@@ -32,6 +32,9 @@
"dailyCap": {
"type": "number"
},
"advertiserId": {
"type": "string"
},
"perDay": {
"type": "number"
},
8 changes: 8 additions & 0 deletions vendor/bat-native-ads/src/bat/ads/ad_info.cc
Original file line number Diff line number Diff line change
@@ -20,6 +20,7 @@ AdInfo::AdInfo(const AdInfo& info) :
start_timestamp(info.start_timestamp),
end_timestamp(info.end_timestamp),
daily_cap(info.daily_cap),
advertiser_id(info.advertiser_id),
per_day(info.per_day),
total_max(info.total_max),
regions(info.regions),
@@ -71,6 +72,10 @@ Result AdInfo::FromJson(
daily_cap = document["daily_cap"].GetUint();
}

if (document.HasMember("advertiser_id")) {
advertiser_id = document["advertiser_id"].GetString();
}

if (document.HasMember("per_day")) {
per_day = document["per_day"].GetUint();
}
@@ -128,6 +133,9 @@ void SaveToJson(JsonWriter* writer, const AdInfo& info) {
writer->String("daily_cap");
writer->Uint(info.daily_cap);

writer->String("advertiser_id");
writer->String(info.advertiser_id.c_str());

writer->String("per_day");
writer->Uint(info.per_day);

7 changes: 7 additions & 0 deletions vendor/bat-native-ads/src/bat/ads/bundle_state.cc
Original file line number Diff line number Diff line change
@@ -75,6 +75,10 @@ Result BundleState::FromJson(
ad_info.daily_cap = info["dailyCap"].GetUint();
}

if (info.HasMember("advertiserId")) {
ad_info.advertiser_id = info["advertiserId"].GetString();
}

if (info.HasMember("perDay")) {
ad_info.per_day = info["perDay"].GetUint();
}
@@ -171,6 +175,9 @@ void SaveToJson(JsonWriter* writer, const BundleState& state) {
writer->String("dailyCap");
writer->Uint(ad.daily_cap);

writer->String("advertiserId");
writer->String(ad.advertiser_id.c_str());

writer->String("perDay");
writer->Uint(ad.per_day);

58 changes: 56 additions & 2 deletions vendor/bat-native-ads/src/bat/ads/internal/ads_impl.cc
Original file line number Diff line number Diff line change
@@ -1317,13 +1317,47 @@ std::vector<AdInfo> AdsImpl::GetUnseenAdsAndRoundRobinIfNeeded(
return ads;
}

auto unseen_ads = GetUnseenAds(ads);
std::vector<AdInfo> ads_for_unseen_advertisers =
GetAdsForUnseenAdvertisers(ads);
if (ads_for_unseen_advertisers.empty()) {
BLOG(INFO) << "All advertisers have been shown, so round robin";

const bool should_not_show_last_advertiser =
client_->GetAdvertisersUUIDSeen().size() > 1 ? true : false;

client_->ResetAdvertisersUUIDSeen(ads);

ads_for_unseen_advertisers = GetAdsForUnseenAdvertisers(ads);

if (should_not_show_last_advertiser) {
const auto it = std::remove_if(ads_for_unseen_advertisers.begin(),
ads_for_unseen_advertisers.end(), [&](AdInfo& ad) {
return ad.advertiser_id == last_shown_ad_info_.advertiser_id;
});

ads_for_unseen_advertisers.erase(it, ads_for_unseen_advertisers.end());
}
}

std::vector<AdInfo> unseen_ads = GetUnseenAds(ads_for_unseen_advertisers);
if (unseen_ads.empty()) {
BLOG(INFO) << "All ads have been shown, so round robin";

const bool should_not_show_last_ad =
client_->GetAdsUUIDSeen().size() > 1 ? true : false;

client_->ResetAdsUUIDSeen(ads);

unseen_ads = GetUnseenAds(ads);

if (should_not_show_last_ad) {
const auto it = std::remove_if(ads_for_unseen_advertisers.begin(),
ads_for_unseen_advertisers.end(), [&](AdInfo& ad) {
return ad.uuid == last_shown_ad_info_.uuid;
});

ads_for_unseen_advertisers.erase(it, ads_for_unseen_advertisers.end());
}
}

return unseen_ads;
@@ -1333,10 +1367,27 @@ std::vector<AdInfo> AdsImpl::GetUnseenAds(
const std::vector<AdInfo>& ads) const {
auto unseen_ads = ads;
const auto seen_ads = client_->GetAdsUUIDSeen();
const auto seen_advertisers = client_->GetAdvertisersUUIDSeen();

const auto it = std::remove_if(unseen_ads.begin(), unseen_ads.end(),
[&](AdInfo& ad) {
return seen_ads.find(ad.uuid) != seen_ads.end();
return seen_ads.find(ad.uuid) != seen_ads.end() &&
seen_ads.find(ad.advertiser_id) != seen_advertisers.end();
});

unseen_ads.erase(it, unseen_ads.end());

return unseen_ads;
}

std::vector<AdInfo> AdsImpl::GetAdsForUnseenAdvertisers(
const std::vector<AdInfo>& ads) const {
auto unseen_ads = ads;
const auto seen_ads = client_->GetAdvertisersUUIDSeen();

const auto it = std::remove_if(unseen_ads.begin(), unseen_ads.end(),
[&](AdInfo& ad) {
return seen_ads.find(ad.advertiser_id) != seen_ads.end();
});

unseen_ads.erase(it, unseen_ads.end());
@@ -1381,6 +1432,9 @@ bool AdsImpl::ShowAd(
now_in_seconds);

client_->UpdateAdsUUIDSeen(ad.uuid, 1);
client_->UpdateAdvertisersUUIDSeen(ad.advertiser_id, 1);

last_shown_ad_info_ = ad;

auto notification_info = std::make_unique<NotificationInfo>();
notification_info->id = base::GenerateGUID();
3 changes: 3 additions & 0 deletions vendor/bat-native-ads/src/bat/ads/internal/ads_impl.h
Original file line number Diff line number Diff line change
@@ -232,10 +232,13 @@ class AdsImpl : public Ads {
const std::vector<AdInfo>& ads) const;
std::vector<AdInfo> GetUnseenAds(
const std::vector<AdInfo>& ads) const;
std::vector<AdInfo> GetAdsForUnseenAdvertisers(
const std::vector<AdInfo>& ads) const;

bool IsAdValid(
const AdInfo& ad_info);
NotificationInfo last_shown_notification_info_;
AdInfo last_shown_ad_info_;
bool ShowAd(
const AdInfo& ad_info);
bool IsAllowedToServeAds();
1 change: 1 addition & 0 deletions vendor/bat-native-ads/src/bat/ads/internal/bundle.cc
Original file line number Diff line number Diff line change
@@ -129,6 +129,7 @@ std::unique_ptr<BundleState> Bundle::GenerateFromCatalog(
ad_info.start_timestamp = campaign.start_at;
ad_info.end_timestamp = campaign.end_at;
ad_info.daily_cap = campaign.daily_cap;
ad_info.advertiser_id = campaign.advertiser_id;
ad_info.per_day = creative_set.per_day;
ad_info.total_max = creative_set.total_max;
ad_info.regions = regions;
Original file line number Diff line number Diff line change
@@ -66,6 +66,7 @@ Result CatalogState::FromJson(
campaign_info.start_at = campaign["startAt"].GetString();
campaign_info.end_at = campaign["endAt"].GetString();
campaign_info.daily_cap = campaign["dailyCap"].GetUint();
campaign_info.advertiser_id = campaign["advertiserId"].GetString();

// Geo targets
for (const auto& geo_target : campaign["geoTargets"].GetArray()) {
27 changes: 27 additions & 0 deletions vendor/bat-native-ads/src/bat/ads/internal/client.cc
Original file line number Diff line number Diff line change
@@ -364,6 +364,33 @@ void Client::ResetAdsUUIDSeen(
SaveState();
}

void Client::UpdateAdvertisersUUIDSeen(
const std::string& uuid,
const uint64_t value) {
client_state_->advertisers_uuid_seen.insert({uuid, value});

SaveState();
}

const std::map<std::string, uint64_t> Client::GetAdvertisersUUIDSeen() {
return client_state_->advertisers_uuid_seen;
}

void Client::ResetAdvertisersUUIDSeen(
const std::vector<AdInfo>& ads) {
BLOG(INFO) << "Resetting seen advertisers";

for (const auto& ad : ads) {
auto advertiser_uuid_seen =
client_state_->advertisers_uuid_seen.find(ad.advertiser_id);
if (advertiser_uuid_seen != client_state_->advertisers_uuid_seen.end()) {
client_state_->advertisers_uuid_seen.erase(advertiser_uuid_seen);
}
}

SaveState();
}

void Client::SetNextCheckServeAdTimestampInSeconds(
const uint64_t timestamp_in_seconds) {
client_state_->next_check_serve_ad_timestamp_in_seconds
6 changes: 6 additions & 0 deletions vendor/bat-native-ads/src/bat/ads/internal/client.h
Original file line number Diff line number Diff line change
@@ -55,6 +55,12 @@ class Client {
void UpdateAdsUUIDSeen(const std::string& uuid, uint64_t value);
const std::map<std::string, uint64_t> GetAdsUUIDSeen();
void ResetAdsUUIDSeen(const std::vector<AdInfo>& ads);
void UpdateAdvertisersUUIDSeen(
const std::string& uuid,
const uint64_t value);
const std::map<std::string, uint64_t> GetAdvertisersUUIDSeen();
void ResetAdvertisersUUIDSeen(
const std::vector<AdInfo>& ads);
void SetNextCheckServeAdTimestampInSeconds(
const uint64_t timestamp_in_seconds);
uint64_t GetNextCheckServeAdTimestampInSeconds();
17 changes: 17 additions & 0 deletions vendor/bat-native-ads/src/bat/ads/internal/client_state.cc
Original file line number Diff line number Diff line change
@@ -31,6 +31,7 @@ ClientState::ClientState(const ClientState& state)
ads_shown_history(state.ads_shown_history),
ad_uuid(state.ad_uuid),
ads_uuid_seen(state.ads_uuid_seen),
advertisers_uuid_seen(state.advertisers_uuid_seen),
next_check_serve_ad_timestamp_in_seconds(
state.next_check_serve_ad_timestamp_in_seconds),
available(state.available),
@@ -112,6 +113,14 @@ Result ClientState::FromJson(
}
}

if (client.HasMember("advertisersUUIDSeen")) {
for (const auto& advertiser_uuid_seen :
client["advertisersUUIDSeen"].GetObject()) {
advertisers_uuid_seen.insert({advertiser_uuid_seen.name.GetString(),
advertiser_uuid_seen.value.GetInt64()});
}
}

if (client.HasMember("nextCheckServeAd")) {
next_check_serve_ad_timestamp_in_seconds =
client["nextCheckServeAd"].GetUint64();
@@ -265,6 +274,14 @@ void SaveToJson(JsonWriter* writer, const ClientState& state) {
}
writer->EndObject();

writer->String("advertisersUUIDSeen");
writer->StartObject();
for (const auto& advertiser_uuid_seen : state.advertisers_uuid_seen) {
writer->String(advertiser_uuid_seen.first.c_str());
writer->Uint64(advertiser_uuid_seen.second);
}
writer->EndObject();

writer->String("nextCheckServeAd");
writer->Uint64(state.next_check_serve_ad_timestamp_in_seconds);

1 change: 1 addition & 0 deletions vendor/bat-native-ads/src/bat/ads/internal/client_state.h
Original file line number Diff line number Diff line change
@@ -33,6 +33,7 @@ struct ClientState {
std::deque<AdHistory> ads_shown_history;
std::string ad_uuid;
std::map<std::string, uint64_t> ads_uuid_seen;
std::map<std::string, uint64_t> advertisers_uuid_seen;
uint64_t next_check_serve_ad_timestamp_in_seconds;
bool available;
uint64_t last_search_time;