| // Copyright 2018 The Fuchsia 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 "peridot/bin/suggestion_engine/ranking_features/affinity_ranking_feature.h" |
| |
| #include <lib/fsl/types/type_converters.h> |
| #include <lib/fxl/logging.h> |
| #include <lib/fxl/strings/join_strings.h> |
| #include <lib/fxl/type_converter.h> |
| |
| namespace modular { |
| |
| namespace { |
| |
| bool MatchesMod(const fuchsia::modular::ModuleAffinity& affinity, |
| const fidl::VectorPtr<fidl::StringPtr>& mod_path, |
| const std::string& affinity_story) { |
| // If the stories are not the same return early. |
| if (affinity.story_name != affinity_story) { |
| return false; |
| } |
| |
| // Since module_paths are composed using the name of the parent view (note |
| // this is how it's working today, but not how it should be expected to work. |
| // Module names are opaque identifiers we shouldn't rely on for this kind of |
| // logic), we compare the one in focused with the affinity we are interested. |
| // Exmaple: |
| // - In focus: a/b/c |
| // - Affinity: a/b |
| // Since the name is the same up to a/b, then we have higher confidence. |
| // TODO(miguelfrde): rather than using this in a boolean fashion, return a |
| // meaningful confidence that represents this behavior and supports cases such |
| // as a/b/c vs a/d. For the case above it could be 0.66 and for the case of |
| // a/d 0.33. |
| for (uint32_t i = 0; i < mod_path->size(); i++) { |
| if (i >= affinity.module_name->size()) { |
| break; |
| } |
| if (mod_path->at(i) != affinity.module_name->at(i)) { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| } // namespace |
| |
| AffinityRankingFeature::AffinityRankingFeature() {} |
| |
| AffinityRankingFeature::~AffinityRankingFeature() = default; |
| |
| double AffinityRankingFeature::ComputeFeatureInternal( |
| const fuchsia::modular::UserInput& query, |
| const RankedSuggestion& suggestion) { |
| const auto& proposal = suggestion.prototype->proposal; |
| if (proposal.affinity->empty()) { |
| return kMaxConfidence; |
| } |
| for (const auto& context_value : *ContextValues()) { |
| const auto& affinity_story_id = context_value.meta.story->id; |
| for (const auto& affinity : *proposal.affinity) { |
| if (affinity.is_story_affinity() && |
| affinity_story_id == affinity.story_affinity().story_name) { |
| return kMaxConfidence; |
| } |
| if (affinity.is_module_affinity() && |
| MatchesMod(affinity.module_affinity(), context_value.meta.mod->path, |
| affinity_story_id)) { |
| return kMaxConfidence; |
| } |
| } |
| } |
| return kMinConfidence; |
| } |
| |
| fuchsia::modular::ContextSelectorPtr |
| AffinityRankingFeature::CreateContextSelectorInternal() { |
| // Get currently focused mod and in focused story. |
| auto selector = fuchsia::modular::ContextSelector::New(); |
| selector->type = fuchsia::modular::ContextValueType::MODULE; |
| selector->meta = fuchsia::modular::ContextMetadata::New(); |
| selector->meta->story = fuchsia::modular::StoryMetadata::New(); |
| selector->meta->story->focused = fuchsia::modular::FocusedState::New(); |
| selector->meta->story->focused->state = |
| fuchsia::modular::FocusedStateState::FOCUSED; |
| selector->meta->mod = fuchsia::modular::ModuleMetadata::New(); |
| selector->meta->mod->focused = fuchsia::modular::FocusedState::New(); |
| selector->meta->mod->focused->state = |
| fuchsia::modular::FocusedStateState::FOCUSED; |
| return selector; |
| } |
| |
| } // namespace modular |