121 lines
4.1 KiB
Python
121 lines
4.1 KiB
Python
# -*- coding: utf-8 -*- #
|
|
# Copyright 2018 Google LLC. All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
"""Contains a class to rate commands based on relevance."""
|
|
|
|
from __future__ import absolute_import
|
|
from __future__ import division
|
|
from __future__ import unicode_literals
|
|
|
|
from googlecloudsdk.command_lib.help_search import lookup
|
|
|
|
|
|
class CommandRater(object):
|
|
"""A class to rate the results of searching a command."""
|
|
|
|
# The below multipliers reflect heuristics for how "important" a term is
|
|
# in a command based on where it's found.
|
|
_COMMAND_NAME_MULTIPLIER = 1.0 # command name
|
|
_ARG_NAME_MULTIPLIER = 0.5 # arg name (positional or flag)
|
|
_PATH_MULTIPLIER = 0.5 # the command path
|
|
_DEFAULT_MULTIPLIER = 0.25 # anything not controlled by other multipliers.
|
|
|
|
# This multiplier controls how much a command is penalized for not containing
|
|
# a search term.
|
|
_NOT_FOUND_MULTIPLIER = 0.1
|
|
|
|
def __init__(self, results, command):
|
|
"""Create a CommandRater.
|
|
|
|
Args:
|
|
results: googlecloudsdk.command_lib.search_help.search_util
|
|
.CommandSearchResult, class that holds results.
|
|
command: dict, a json representation of a command.
|
|
"""
|
|
self._command = command
|
|
self._terms = results.AllTerms()
|
|
self._results = results
|
|
|
|
def Rate(self):
|
|
"""Produce a simple relevance rating for a set of command search results.
|
|
|
|
Returns a float in the range (0, 1]. For each term that's found, the rating
|
|
is multiplied by a number reflecting how "important" its location is, with
|
|
command name being the most and flag or positional names being the second
|
|
most important, as well as by how many of the search terms were found.
|
|
|
|
Commands are also penalized if duplicate results in a higher release track
|
|
were found.
|
|
|
|
Returns:
|
|
rating: float, the rating of the results.
|
|
"""
|
|
rating = 1.0
|
|
rating *= self._RateForLocation()
|
|
rating *= self._RateForTermsFound()
|
|
return rating
|
|
|
|
def _RateForLocation(self):
|
|
"""Get a rating based on locations of results."""
|
|
rating = 1.0
|
|
locations = self._results.FoundTermsMap().values()
|
|
for location in locations:
|
|
if location == lookup.NAME:
|
|
rating *= self._COMMAND_NAME_MULTIPLIER
|
|
elif location == lookup.PATH:
|
|
rating *= self._PATH_MULTIPLIER
|
|
elif (location.split(lookup.DOT)[0] in [lookup.FLAGS, lookup.POSITIONALS]
|
|
and location.split(lookup.DOT)[-1] == lookup.NAME):
|
|
rating *= self._ARG_NAME_MULTIPLIER
|
|
else:
|
|
rating *= self._DEFAULT_MULTIPLIER
|
|
return rating
|
|
|
|
def _RateForTermsFound(self):
|
|
"""Get a rating based on how many of the searched terms were found."""
|
|
rating = 1.0
|
|
results = self._results.FoundTermsMap()
|
|
for term in self._terms:
|
|
if term not in results:
|
|
rating *= self._NOT_FOUND_MULTIPLIER
|
|
return rating
|
|
|
|
|
|
class CumulativeRater(object):
|
|
"""Rates all found commands for relevance."""
|
|
|
|
def __init__(self):
|
|
"""Creates a cumulative rater.
|
|
"""
|
|
self._found_commands_and_results = []
|
|
|
|
def AddFoundCommand(self, command, result):
|
|
"""Add a command that is a result.
|
|
|
|
Args:
|
|
command: dict, a json representation of a command. MUST already be updated
|
|
with the search results.
|
|
result: search_util.CommandSearchResults, the results object that goes
|
|
with this command.
|
|
"""
|
|
self._found_commands_and_results.append((command, result))
|
|
|
|
def RateAll(self):
|
|
"""Adds rating to every command found."""
|
|
for command, results in self._found_commands_and_results:
|
|
rating = CommandRater(results, command).Rate()
|
|
command[lookup.RELEVANCE] = rating
|
|
|