| Index: components/contextual_search/browser/ctr_aggregator.cc
|
| diff --git a/components/contextual_search/browser/ctr_aggregator.cc b/components/contextual_search/browser/ctr_aggregator.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..2cd4b70e40f80e879bdb9ad3fadba7536e9da3e0
|
| --- /dev/null
|
| +++ b/components/contextual_search/browser/ctr_aggregator.cc
|
| @@ -0,0 +1,198 @@
|
| +// Copyright 2016 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "components/contextual_search/browser/ctr_aggregator.h"
|
| +
|
| +#include <unordered_map>
|
| +
|
| +#include "base/lazy_instance.h"
|
| +#include "base/time/time.h"
|
| +
|
| +namespace {
|
| +
|
| +// Keys for ChromePreferenceManager storage of first and last week written.
|
| +const char kFirstWeekWrittenKey[] = "contextual_search_first_week";
|
| +const char kLastWeekWrittenKey[] = "contextual_search_last_week";
|
| +// Number of weeks of data to record.
|
| +const int kNumWeeksNeededFor28DayData = 4;
|
| +const int kNumWeeksToRecord = kNumWeeksNeededFor28DayData + 1;
|
| +// Prefixes for ChromePreferenceManager storage keyed by week.
|
| +const char kClicksWeekKeyPrefix[] = "contextual_search_clicks_week_";
|
| +const char kImpressionsWeekKeyPrefix[] = "contextual_search_impressions_week_";
|
| +
|
| +const double kSecondsPerWeek =
|
| + base::Time::kMicrosecondsPerWeek / base::Time::kMicrosecondsPerSecond;
|
| +// Used for validation in debug build. Week numbers are > 2300 as of year 2016.
|
| +const int kReasonableMinWeek = 2000;
|
| +
|
| +} // namespace
|
| +
|
| +namespace contextual_search {
|
| +
|
| +CTRAggregator::CTRAggregator(DeviceIntStorage& storage) : storage_(storage) {
|
| + base::Time now = base::Time::NowFromSystemTime();
|
| + double now_in_seconds = now.ToDoubleT();
|
| + week_number_ = now_in_seconds / kSecondsPerWeek;
|
| + DCHECK(week_number_ >= kReasonableMinWeek);
|
| + // It would be convenient to call EnsureFirstWeekRecorded here, but that could
|
| + // be problematic because recording the data will callback into the storage
|
| + // system which might not be fully initialized yet.
|
| +}
|
| +
|
| +CTRAggregator::CTRAggregator(DeviceIntStorage& storage, int week_number)
|
| + : storage_(storage), week_number_(week_number) {
|
| +}
|
| +
|
| +CTRAggregator::~CTRAggregator() {}
|
| +
|
| +void CTRAggregator::RecordImpression(bool did_click) {
|
| + UpdateStorageToCurrentWeek();
|
| + Increment(GetWeekImpressionsKey(week_number_));
|
| + if (did_click)
|
| + Increment(GetWeekClicksKey(week_number_));
|
| +}
|
| +
|
| +bool CTRAggregator::HasPreviousWeekData() {
|
| + EnsureFirstWeekRecorded();
|
| + int first_week_written = ReadInt(kFirstWeekWrittenKey);
|
| + return (week_number_ - first_week_written >= 1);
|
| +}
|
| +
|
| +int CTRAggregator::GetPreviousWeekImpressions() {
|
| + UpdateStorageToCurrentWeek();
|
| + std::string key_impressions = GetWeekImpressionsKey(week_number_ - 1);
|
| + return ReadInt(key_impressions);
|
| +}
|
| +
|
| +float CTRAggregator::GetPreviousWeekCTR() {
|
| + if (!HasPreviousWeekData())
|
| + return NAN;
|
| +
|
| + UpdateStorageToCurrentWeek();
|
| + int clicks = GetPreviousWeekClicks();
|
| + int impressions = GetPreviousWeekImpressions();
|
| + if (impressions == 0)
|
| + return 0.0;
|
| + return (float)clicks / impressions;
|
| +}
|
| +
|
| +bool CTRAggregator::HasPrevious28DayData() {
|
| + EnsureFirstWeekRecorded();
|
| + int first_week_written = ReadInt(kFirstWeekWrittenKey);
|
| + return (week_number_ - first_week_written >= kNumWeeksNeededFor28DayData);
|
| +}
|
| +
|
| +float CTRAggregator::GetPrevious28DayCTR() {
|
| + if (!HasPrevious28DayData())
|
| + return NAN;
|
| +
|
| + UpdateStorageToCurrentWeek();
|
| + int clicks = GetPrevious28DayClicks();
|
| + int impressions = GetPrevious28DayImpressions();
|
| + if (impressions == 0)
|
| + return 0.0;
|
| + return (float)clicks / impressions;
|
| +}
|
| +
|
| +int CTRAggregator::GetPrevious28DayImpressions() {
|
| + UpdateStorageToCurrentWeek();
|
| + int impressions = 0;
|
| + for (int week = 1; week <= kNumWeeksNeededFor28DayData; week++) {
|
| + std::string key_impressions = GetWeekImpressionsKey(week_number_ - week);
|
| + impressions += ReadInt(key_impressions);
|
| + }
|
| + return impressions;
|
| +}
|
| +
|
| +// private
|
| +
|
| +std::string CTRAggregator::GetWeekKey(int which_week) {
|
| + DCHECK(which_week > kReasonableMinWeek);
|
| + return std::to_string(which_week % kNumWeeksToRecord);
|
| +}
|
| +
|
| +std::string CTRAggregator::GetWeekImpressionsKey(int which_week) {
|
| + return kImpressionsWeekKeyPrefix + GetWeekKey(which_week);
|
| +}
|
| +
|
| +std::string CTRAggregator::GetWeekClicksKey(int which_week) {
|
| + return kClicksWeekKeyPrefix + GetWeekKey(which_week);
|
| +}
|
| +
|
| +int CTRAggregator::GetPreviousWeekClicks() {
|
| + std::string key_clicks = GetWeekClicksKey(week_number_ - 1);
|
| + return ReadInt(key_clicks);
|
| +}
|
| +
|
| +int CTRAggregator::GetPrevious28DayClicks() {
|
| + int clicks = 0;
|
| + for (int week = 1; week <= 4; week++) {
|
| + std::string key_clicks = GetWeekClicksKey(week_number_ - week);
|
| + clicks += ReadInt(key_clicks);
|
| + }
|
| + return clicks;
|
| +}
|
| +
|
| +void CTRAggregator::Increment(std::string key) {
|
| + int cur_value = ReadInt(key);
|
| + cur_value++;
|
| + WriteInt(key, cur_value);
|
| +}
|
| +
|
| +void CTRAggregator::EnsureFirstWeekRecorded() {
|
| + int first_week_written = ReadInt(kFirstWeekWrittenKey);
|
| + if (first_week_written == 0)
|
| + WriteInt(kFirstWeekWrittenKey, week_number_);
|
| +}
|
| +
|
| +void CTRAggregator::UpdateStorageToCurrentWeek() {
|
| + // If still on the same week we're done!
|
| + int last_week_written = ReadInt(kLastWeekWrittenKey);
|
| + if (last_week_written == week_number_)
|
| + return;
|
| +
|
| + EnsureFirstWeekRecorded();
|
| + WriteInt(kLastWeekWrittenKey, week_number_);
|
| + if (last_week_written == 0)
|
| + return;
|
| +
|
| + // Moved to a new week. Since we recycle storage we must clear the new week
|
| + // and all that we may have skipped since our last write.
|
| + int weeks_to_clear =
|
| + std::min(week_number_ - last_week_written, kNumWeeksToRecord);
|
| + int week = week_number_;
|
| + while (weeks_to_clear > 0) {
|
| + WriteInt(GetWeekImpressionsKey(week), 0);
|
| + WriteInt(GetWeekClicksKey(week), 0);
|
| + week--;
|
| + weeks_to_clear--;
|
| + }
|
| +}
|
| +
|
| +// Storage
|
| +
|
| +typedef std::unordered_map<std::string, int> IntMap;
|
| +static base::LazyInstance<IntMap> s_storage_cache_ = LAZY_INSTANCE_INITIALIZER;
|
| +
|
| +int CTRAggregator::ReadInt(std::string storage_key) {
|
| + if (s_storage_cache_.Get().count(storage_key) != 0)
|
| + return s_storage_cache_.Get()[storage_key];
|
| +
|
| + int value = storage_.ReadInt(storage_key);
|
| + s_storage_cache_.Get()[storage_key] = value;
|
| + return value;
|
| +}
|
| +
|
| +void CTRAggregator::WriteInt(std::string storage_key, int value) {
|
| + storage_.WriteInt(storage_key, value);
|
| + s_storage_cache_.Get()[storage_key] = value;
|
| +}
|
| +
|
| +// Testing only
|
| +
|
| +void CTRAggregator::IncrementWeek(int weeks) {
|
| + week_number_ += weeks;
|
| +}
|
| +
|
| +} // namespace contextual_search
|
|
|