Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(50)

Side by Side Diff: components/ntp_snippets/remote/remote_suggestions_fetcher.cc

Issue 2686063003: [remote suggestions] Attach the fetch time to RemoteSnippets, ContentSnippets and SnippetArticle (Closed)
Patch Set: rebase Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "components/ntp_snippets/remote/remote_suggestions_fetcher.h" 5 #include "components/ntp_snippets/remote/remote_suggestions_fetcher.h"
6 6
7 #include <cstdlib> 7 #include <cstdlib>
8 #include <utility> 8 #include <utility>
9 9
10 #include "base/files/file_path.h" 10 #include "base/files/file_path.h"
11 #include "base/files/file_util.h" 11 #include "base/files/file_util.h"
12 #include "base/memory/ptr_util.h" 12 #include "base/memory/ptr_util.h"
13 #include "base/metrics/histogram_macros.h" 13 #include "base/metrics/histogram_macros.h"
14 #include "base/metrics/sparse_histogram.h" 14 #include "base/metrics/sparse_histogram.h"
15 #include "base/path_service.h" 15 #include "base/path_service.h"
16 #include "base/strings/stringprintf.h" 16 #include "base/strings/stringprintf.h"
17 #include "base/strings/utf_string_conversions.h" 17 #include "base/strings/utf_string_conversions.h"
18 #include "base/time/default_tick_clock.h" 18 #include "base/time/default_clock.h"
19 #include "base/time/time.h" 19 #include "base/time/time.h"
20 #include "base/values.h" 20 #include "base/values.h"
21 #include "components/data_use_measurement/core/data_use_user_data.h" 21 #include "components/data_use_measurement/core/data_use_user_data.h"
22 #include "components/ntp_snippets/category.h" 22 #include "components/ntp_snippets/category.h"
23 #include "components/ntp_snippets/features.h" 23 #include "components/ntp_snippets/features.h"
24 #include "components/ntp_snippets/ntp_snippets_constants.h" 24 #include "components/ntp_snippets/ntp_snippets_constants.h"
25 #include "components/ntp_snippets/remote/request_params.h" 25 #include "components/ntp_snippets/remote/request_params.h"
26 #include "components/ntp_snippets/user_classifier.h" 26 #include "components/ntp_snippets/user_classifier.h"
27 #include "components/signin/core/browser/signin_manager.h" 27 #include "components/signin/core/browser/signin_manager.h"
28 #include "components/signin/core/browser/signin_manager_base.h" 28 #include "components/signin/core/browser/signin_manager_base.h"
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after
129 } 129 }
130 return true; 130 return true;
131 } 131 }
132 132
133 // Creates suggestions from dictionary values in |list| and adds them to 133 // Creates suggestions from dictionary values in |list| and adds them to
134 // |suggestions|. Returns true on success, false if anything went wrong. 134 // |suggestions|. Returns true on success, false if anything went wrong.
135 // |remote_category_id| is only used if |content_suggestions_api| is true. 135 // |remote_category_id| is only used if |content_suggestions_api| is true.
136 bool AddSuggestionsFromListValue(bool content_suggestions_api, 136 bool AddSuggestionsFromListValue(bool content_suggestions_api,
137 int remote_category_id, 137 int remote_category_id,
138 const base::ListValue& list, 138 const base::ListValue& list,
139 RemoteSuggestion::PtrVector* suggestions) { 139 RemoteSuggestion::PtrVector* suggestions,
140 const base::Time& fetch_time) {
140 for (const auto& value : list) { 141 for (const auto& value : list) {
141 const base::DictionaryValue* dict = nullptr; 142 const base::DictionaryValue* dict = nullptr;
142 if (!value->GetAsDictionary(&dict)) { 143 if (!value->GetAsDictionary(&dict)) {
143 return false; 144 return false;
144 } 145 }
145 146
146 std::unique_ptr<RemoteSuggestion> suggestion; 147 std::unique_ptr<RemoteSuggestion> suggestion;
147 if (content_suggestions_api) { 148 if (content_suggestions_api) {
148 suggestion = RemoteSuggestion::CreateFromContentSuggestionsDictionary( 149 suggestion = RemoteSuggestion::CreateFromContentSuggestionsDictionary(
149 *dict, remote_category_id); 150 *dict, remote_category_id, fetch_time);
150 } else { 151 } else {
151 suggestion = RemoteSuggestion::CreateFromChromeReaderDictionary(*dict); 152 suggestion =
153 RemoteSuggestion::CreateFromChromeReaderDictionary(*dict, fetch_time);
152 } 154 }
153 if (!suggestion) { 155 if (!suggestion) {
154 return false; 156 return false;
155 } 157 }
156 158
157 suggestions->push_back(std::move(suggestion)); 159 suggestions->push_back(std::move(suggestion));
158 } 160 }
159 return true; 161 return true;
160 } 162 }
161 163
162 int GetMinuteOfTheDay(bool local_time, bool reduced_resolution) { 164 int GetMinuteOfTheDay(bool local_time,
163 base::Time now(base::Time::Now()); 165 bool reduced_resolution,
166 base::Clock* clock) {
167 base::Time now(clock->Now());
164 base::Time::Exploded now_exploded{}; 168 base::Time::Exploded now_exploded{};
165 local_time ? now.LocalExplode(&now_exploded) : now.UTCExplode(&now_exploded); 169 local_time ? now.LocalExplode(&now_exploded) : now.UTCExplode(&now_exploded);
166 int now_minute = reduced_resolution 170 int now_minute = reduced_resolution
167 ? now_exploded.minute / kFetchTimeHistogramResolution * 171 ? now_exploded.minute / kFetchTimeHistogramResolution *
168 kFetchTimeHistogramResolution 172 kFetchTimeHistogramResolution
169 : now_exploded.minute; 173 : now_exploded.minute;
170 return now_exploded.hour * 60 + now_minute; 174 return now_exploded.hour * 60 + now_minute;
171 } 175 }
172 176
173 // The response from the backend might include suggestions from multiple 177 // The response from the backend might include suggestions from multiple
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after
248 signin_manager_(signin_manager), 252 signin_manager_(signin_manager),
249 token_service_(token_service), 253 token_service_(token_service),
250 url_request_context_getter_(std::move(url_request_context_getter)), 254 url_request_context_getter_(std::move(url_request_context_getter)),
251 language_model_(language_model), 255 language_model_(language_model),
252 parse_json_callback_(parse_json_callback), 256 parse_json_callback_(parse_json_callback),
253 fetch_url_(GetFetchEndpoint()), 257 fetch_url_(GetFetchEndpoint()),
254 fetch_api_(UsesChromeContentSuggestionsAPI(fetch_url_) 258 fetch_api_(UsesChromeContentSuggestionsAPI(fetch_url_)
255 ? FetchAPI::CHROME_CONTENT_SUGGESTIONS_API 259 ? FetchAPI::CHROME_CONTENT_SUGGESTIONS_API
256 : FetchAPI::CHROME_READER_API), 260 : FetchAPI::CHROME_READER_API),
257 api_key_(api_key), 261 api_key_(api_key),
258 tick_clock_(new base::DefaultTickClock()), 262 clock_(new base::DefaultClock()),
259 user_classifier_(user_classifier), 263 user_classifier_(user_classifier),
260 request_throttler_rare_ntp_user_( 264 request_throttler_rare_ntp_user_(
261 pref_service, 265 pref_service,
262 RequestThrottler::RequestType:: 266 RequestThrottler::RequestType::
263 CONTENT_SUGGESTION_FETCHER_RARE_NTP_USER), 267 CONTENT_SUGGESTION_FETCHER_RARE_NTP_USER),
264 request_throttler_active_ntp_user_( 268 request_throttler_active_ntp_user_(
265 pref_service, 269 pref_service,
266 RequestThrottler::RequestType:: 270 RequestThrottler::RequestType::
267 CONTENT_SUGGESTION_FETCHER_ACTIVE_NTP_USER), 271 CONTENT_SUGGESTION_FETCHER_ACTIVE_NTP_USER),
268 request_throttler_active_suggestions_consumer_( 272 request_throttler_active_suggestions_consumer_(
(...skipping 14 matching lines...) Expand all
283 if (!DemandQuotaForRequest(params.interactive_request)) { 287 if (!DemandQuotaForRequest(params.interactive_request)) {
284 FetchFinished(OptionalFetchedCategories(), std::move(callback), 288 FetchFinished(OptionalFetchedCategories(), std::move(callback),
285 params.interactive_request 289 params.interactive_request
286 ? FetchResult::INTERACTIVE_QUOTA_ERROR 290 ? FetchResult::INTERACTIVE_QUOTA_ERROR
287 : FetchResult::NON_INTERACTIVE_QUOTA_ERROR, 291 : FetchResult::NON_INTERACTIVE_QUOTA_ERROR,
288 /*error_details=*/std::string()); 292 /*error_details=*/std::string());
289 return; 293 return;
290 } 294 }
291 295
292 if (!params.interactive_request) { 296 if (!params.interactive_request) {
293 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.FetchTimeLocal", 297 UMA_HISTOGRAM_SPARSE_SLOWLY(
294 GetMinuteOfTheDay(/*local_time=*/true, 298 "NewTabPage.Snippets.FetchTimeLocal",
295 /*reduced_resolution=*/true)); 299 GetMinuteOfTheDay(/*local_time=*/true,
296 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.FetchTimeUTC", 300 /*reduced_resolution=*/true, clock_.get()));
297 GetMinuteOfTheDay(/*local_time=*/false, 301 UMA_HISTOGRAM_SPARSE_SLOWLY(
298 /*reduced_resolution=*/true)); 302 "NewTabPage.Snippets.FetchTimeUTC",
303 GetMinuteOfTheDay(/*local_time=*/false,
304 /*reduced_resolution=*/true, clock_.get()));
299 } 305 }
300 306
301 JsonRequest::Builder builder; 307 JsonRequest::Builder builder;
302 builder.SetFetchAPI(fetch_api_) 308 builder.SetFetchAPI(fetch_api_)
303 .SetFetchAPI(fetch_api_) 309 .SetFetchAPI(fetch_api_)
304 .SetLanguageModel(language_model_) 310 .SetLanguageModel(language_model_)
305 .SetParams(params) 311 .SetParams(params)
306 .SetParseJsonCallback(parse_json_callback_) 312 .SetParseJsonCallback(parse_json_callback_)
307 .SetTickClock(tick_clock_.get()) 313 .SetClock(clock_.get())
308 .SetUrlRequestContextGetter(url_request_context_getter_) 314 .SetUrlRequestContextGetter(url_request_context_getter_)
309 .SetUserClassifier(*user_classifier_); 315 .SetUserClassifier(*user_classifier_);
310 316
311 if (signin_manager_->IsAuthenticated()) { 317 if (signin_manager_->IsAuthenticated()) {
312 // Signed-in: get OAuth token --> fetch suggestions. 318 // Signed-in: get OAuth token --> fetch suggestions.
313 oauth_token_retried_ = false; 319 oauth_token_retried_ = false;
314 pending_requests_.emplace(std::move(builder), std::move(callback)); 320 pending_requests_.emplace(std::move(builder), std::move(callback));
315 StartTokenRequest(); 321 StartTokenRequest();
316 } else if (signin_manager_->AuthInProgress()) { 322 } else if (signin_manager_->AuthInProgress()) {
317 // Currently signing in: wait for auth to finish (the refresh token) --> 323 // Currently signing in: wait for auth to finish (the refresh token) -->
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after
435 StartTokenRequest(); 441 StartTokenRequest();
436 } 442 }
437 443
438 void RemoteSuggestionsFetcher::JsonRequestDone( 444 void RemoteSuggestionsFetcher::JsonRequestDone(
439 std::unique_ptr<JsonRequest> request, 445 std::unique_ptr<JsonRequest> request,
440 SnippetsAvailableCallback callback, 446 SnippetsAvailableCallback callback,
441 std::unique_ptr<base::Value> result, 447 std::unique_ptr<base::Value> result,
442 FetchResult status_code, 448 FetchResult status_code,
443 const std::string& error_details) { 449 const std::string& error_details) {
444 DCHECK(request); 450 DCHECK(request);
451 // Record the time when request for fetching remote content snippets finished.
452 const base::Time fetch_time = clock_->Now();
453
445 last_fetch_json_ = request->GetResponseString(); 454 last_fetch_json_ = request->GetResponseString();
446 455
447 UMA_HISTOGRAM_TIMES("NewTabPage.Snippets.FetchTime", 456 UMA_HISTOGRAM_TIMES("NewTabPage.Snippets.FetchTime",
448 request->GetFetchDuration()); 457 request->GetFetchDuration());
449 458
450 if (!result) { 459 if (!result) {
451 FetchFinished(OptionalFetchedCategories(), std::move(callback), status_code, 460 FetchFinished(OptionalFetchedCategories(), std::move(callback), status_code,
452 error_details); 461 error_details);
453 return; 462 return;
454 } 463 }
455 FetchedCategoriesVector categories; 464 FetchedCategoriesVector categories;
456 if (!JsonToSnippets(*result, &categories)) { 465
466 if (!JsonToSnippets(*result, &categories, fetch_time)) {
457 LOG(WARNING) << "Received invalid snippets: " << last_fetch_json_; 467 LOG(WARNING) << "Received invalid snippets: " << last_fetch_json_;
458 FetchFinished(OptionalFetchedCategories(), std::move(callback), 468 FetchFinished(OptionalFetchedCategories(), std::move(callback),
459 FetchResult::INVALID_SNIPPET_CONTENT_ERROR, std::string()); 469 FetchResult::INVALID_SNIPPET_CONTENT_ERROR, std::string());
460 return; 470 return;
461 } 471 }
462 // Filter out unwanted categories if necessary. 472 // Filter out unwanted categories if necessary.
463 // TODO(fhorschig): As soon as the server supports filtering by category, 473 // TODO(fhorschig): As soon as the server supports filtering by category,
464 // adjust the request instead of over-fetching and filtering here. 474 // adjust the request instead of over-fetching and filtering here.
465 FilterCategories(&categories, request->exclusive_category()); 475 FilterCategories(&categories, request->exclusive_category());
466 476
(...skipping 15 matching lines...) Expand all
482 static_cast<int>(FetchResult::RESULT_MAX)); 492 static_cast<int>(FetchResult::RESULT_MAX));
483 493
484 DVLOG(1) << "Fetch finished: " << last_status_; 494 DVLOG(1) << "Fetch finished: " << last_status_;
485 495
486 std::move(callback).Run(FetchResultToStatus(fetch_result), 496 std::move(callback).Run(FetchResultToStatus(fetch_result),
487 std::move(categories)); 497 std::move(categories));
488 } 498 }
489 499
490 bool RemoteSuggestionsFetcher::JsonToSnippets( 500 bool RemoteSuggestionsFetcher::JsonToSnippets(
491 const base::Value& parsed, 501 const base::Value& parsed,
492 FetchedCategoriesVector* categories) { 502 FetchedCategoriesVector* categories,
503 const base::Time& fetch_time) {
493 const base::DictionaryValue* top_dict = nullptr; 504 const base::DictionaryValue* top_dict = nullptr;
494 if (!parsed.GetAsDictionary(&top_dict)) { 505 if (!parsed.GetAsDictionary(&top_dict)) {
495 return false; 506 return false;
496 } 507 }
497 508
498 switch (fetch_api_) { 509 switch (fetch_api_) {
499 case FetchAPI::CHROME_READER_API: { 510 case FetchAPI::CHROME_READER_API: {
500 const int kUnusedRemoteCategoryId = -1; 511 const int kUnusedRemoteCategoryId = -1;
501 categories->push_back(FetchedCategory( 512 categories->push_back(FetchedCategory(
502 Category::FromKnownCategory(KnownCategories::ARTICLES), 513 Category::FromKnownCategory(KnownCategories::ARTICLES),
503 BuildArticleCategoryInfo(base::nullopt))); 514 BuildArticleCategoryInfo(base::nullopt)));
504 515
505 const base::ListValue* recos = nullptr; 516 const base::ListValue* recos = nullptr;
506 return top_dict->GetList("recos", &recos) && 517 return top_dict->GetList("recos", &recos) &&
507 AddSuggestionsFromListValue(/*content_suggestions_api=*/false, 518 AddSuggestionsFromListValue(
508 kUnusedRemoteCategoryId, *recos, 519 /*content_suggestions_api=*/false, kUnusedRemoteCategoryId,
509 &categories->back().suggestions); 520 *recos, &categories->back().suggestions, fetch_time);
510 } 521 }
511 522
512 case FetchAPI::CHROME_CONTENT_SUGGESTIONS_API: { 523 case FetchAPI::CHROME_CONTENT_SUGGESTIONS_API: {
513 const base::ListValue* categories_value = nullptr; 524 const base::ListValue* categories_value = nullptr;
514 if (!top_dict->GetList("categories", &categories_value)) { 525 if (!top_dict->GetList("categories", &categories_value)) {
515 return false; 526 return false;
516 } 527 }
517 528
518 for (const auto& v : *categories_value) { 529 for (const auto& v : *categories_value) {
519 std::string utf8_title; 530 std::string utf8_title;
520 int remote_category_id = -1; 531 int remote_category_id = -1;
521 const base::DictionaryValue* category_value = nullptr; 532 const base::DictionaryValue* category_value = nullptr;
522 if (!(v->GetAsDictionary(&category_value) && 533 if (!(v->GetAsDictionary(&category_value) &&
523 category_value->GetString("localizedTitle", &utf8_title) && 534 category_value->GetString("localizedTitle", &utf8_title) &&
524 category_value->GetInteger("id", &remote_category_id) && 535 category_value->GetInteger("id", &remote_category_id) &&
525 (remote_category_id > 0))) { 536 (remote_category_id > 0))) {
526 return false; 537 return false;
527 } 538 }
528 539
529 RemoteSuggestion::PtrVector suggestions; 540 RemoteSuggestion::PtrVector suggestions;
530 const base::ListValue* suggestions_list = nullptr; 541 const base::ListValue* suggestions_list = nullptr;
531 // Absence of a list of suggestions is treated as an empty list, which 542 // Absence of a list of suggestions is treated as an empty list, which
532 // is permissible. 543 // is permissible.
533 if (category_value->GetList("suggestions", &suggestions_list)) { 544 if (category_value->GetList("suggestions", &suggestions_list)) {
534 if (!AddSuggestionsFromListValue( 545 if (!AddSuggestionsFromListValue(
535 /*content_suggestions_api=*/true, remote_category_id, 546 /*content_suggestions_api=*/true, remote_category_id,
536 *suggestions_list, &suggestions)) { 547 *suggestions_list, &suggestions, fetch_time)) {
537 return false; 548 return false;
538 } 549 }
539 } 550 }
540 Category category = Category::FromRemoteCategory(remote_category_id); 551 Category category = Category::FromRemoteCategory(remote_category_id);
541 if (category.IsKnownCategory(KnownCategories::ARTICLES)) { 552 if (category.IsKnownCategory(KnownCategories::ARTICLES)) {
542 categories->push_back(FetchedCategory( 553 categories->push_back(FetchedCategory(
543 category, 554 category,
544 BuildArticleCategoryInfo(base::UTF8ToUTF16(utf8_title)))); 555 BuildArticleCategoryInfo(base::UTF8ToUTF16(utf8_title))));
545 } else { 556 } else {
546 // TODO(tschumann): Right now, the backend does not yet populate this 557 // TODO(tschumann): Right now, the backend does not yet populate this
(...skipping 24 matching lines...) Expand all
571 interactive_request); 582 interactive_request);
572 case UserClassifier::UserClass::ACTIVE_SUGGESTIONS_CONSUMER: 583 case UserClassifier::UserClass::ACTIVE_SUGGESTIONS_CONSUMER:
573 return request_throttler_active_suggestions_consumer_ 584 return request_throttler_active_suggestions_consumer_
574 .DemandQuotaForRequest(interactive_request); 585 .DemandQuotaForRequest(interactive_request);
575 } 586 }
576 NOTREACHED(); 587 NOTREACHED();
577 return false; 588 return false;
578 } 589 }
579 590
580 } // namespace ntp_snippets 591 } // namespace ntp_snippets
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698