123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234 |
- import collections
- from collections import Counter
- import six
- from jinja2 import Template
- from kalliope.core.NeuronParameterLoader import NeuronParameterLoader
- from kalliope.core.Models.MatchedSynapse import MatchedSynapse
- from kalliope.core.Utils.Utils import Utils
- from kalliope.core.ConfigurationManager import SettingLoader
- import logging
- logging.basicConfig()
- logger = logging.getLogger("kalliope")
- class OrderAnalyser:
- """
- This Class is used to get a list of synapses that match a given Spoken order
- """
- brain = None
- settings = None
- @classmethod
- def __init__(cls):
- cls.settings = SettingLoader().settings
- @classmethod
- def get_matching_synapse(cls, order, brain=None):
- """
- Return the list of matching synapses from the given order
- :param order: The user order
- :param brain: The loaded brain
- :return: The List of synapses matching the given order
- """
- cls.brain = brain
- logger.debug("[OrderAnalyser] Received order: %s" % order)
- if isinstance(order, six.binary_type):
- order = order.decode('utf-8')
-
- synapse_order_tuple = collections.namedtuple('tuple_synapse_matchingOrder',
- ['synapse', 'order'])
- list_match_synapse = list()
-
- if order is None:
- return list_match_synapse
-
- for synapse in cls.brain.synapses:
- for signal in synapse.signals:
-
- if signal.name == "order":
-
- expected_matching_type = "normal"
- signal_order = None
- if isinstance(signal.parameters, str):
- signal_order = signal.parameters
- if isinstance(signal.parameters, dict):
- try:
- signal_order = signal.parameters["text"]
- except KeyError:
- logger.debug("[OrderAnalyser] Warning, missing parameter 'text' in order. "
- "Order will be skipped")
- continue
- try:
- expected_matching_type = signal.parameters["matching-type"]
- except KeyError:
- logger.debug("[OrderAnalyser] Warning, missing parameter 'matching-type' in order. "
- "Fallback to 'normal'")
- if cls.is_order_matching(user_order=order,
- signal_order=signal_order,
- expected_order_type=expected_matching_type):
-
- logger.debug("Order found! Run synapse name: %s" % synapse.name)
- Utils.print_success("Order matched in the brain. Running synapse \"%s\"" % synapse.name)
- list_match_synapse.append(synapse_order_tuple(synapse=synapse, order=signal_order))
-
- list_synapse_to_process = list()
- for tuple_el in list_match_synapse:
- new_matching_synapse = MatchedSynapse(matched_synapse=tuple_el.synapse,
- matched_order=tuple_el.order,
- user_order=order)
- list_synapse_to_process.append(new_matching_synapse)
- return list_synapse_to_process
- @staticmethod
- def _get_split_order_without_bracket(order):
- """
- Get an order with bracket inside like: "hello my name is {{ name }}.
- return a list of string without bracket like ["hello", "my", "name", "is"]
- :param order: sentence to split
- :return: list of string without bracket
- """
- matches = Utils.find_all_matching_brackets(order)
- for match in matches:
- order = order.replace(match, "")
-
- split_order = order.split()
- return split_order
- @classmethod
- def is_normal_matching(cls, user_order, signal_order):
- """
- True if :
- - all word in the user_order are present in the signal_order
- :param user_order: order from the user
- :param signal_order: order in the signal
- :return: Boolean
- """
- logger.debug("[OrderAnalyser] is_normal_matching called with user_order: %s, signal_order: %s" % (user_order,
- signal_order))
- split_user_order = user_order.split()
- split_signal_order_without_brackets = cls._get_split_order_without_bracket(signal_order)
- c1, c2 = Counter(split_signal_order_without_brackets), Counter(split_user_order)
- for k, n in c1.items():
- if n > c2[k]:
- return False
- return True
- @classmethod
- def is_strict_matching(cls, user_order, signal_order):
- """
- True if :
- - all word in the user_order are present in the signal_order
- - no additional word
- :param user_order: order from the user
- :param signal_order: order in the signal
- :return: Boolean
- """
- logger.debug("[OrderAnalyser] is_strict_matching called with user_order: %s, signal_order: %s" % (user_order,
- signal_order))
- if cls.is_normal_matching(user_order=user_order, signal_order=signal_order):
-
- if Utils.is_containing_bracket(signal_order):
- signal_order = cls._get_instantiated_order_signal_from_user_order(signal_order, user_order)
- split_user_order = user_order.split()
- split_instantiated_signal = signal_order.split()
- if len(split_user_order) == len(split_instantiated_signal):
- return True
- return False
- @classmethod
- def is_ordered_strict_matching(cls, user_order, signal_order):
- """
- True if :
- - all word in the user_order are present in the signal_order
- - no additional word
- - same order as word present in signal_order
- :param user_order: order from the user
- :param signal_order: order in the signal
- :return: Boolean
- """
- logger.debug(
- "[OrderAnalyser] ordered_strict_matching called with user_order: %s, signal_order: %s" % (user_order,
- signal_order))
- if cls.is_normal_matching(user_order=user_order, signal_order=signal_order) and \
- cls.is_strict_matching(user_order=user_order, signal_order=signal_order):
-
- if Utils.is_containing_bracket(signal_order):
- signal_order = cls._get_instantiated_order_signal_from_user_order(signal_order, user_order)
- split_user_order = user_order.split()
- split_signal_order = signal_order.split()
- return split_user_order == split_signal_order
- return False
- @classmethod
- def is_order_matching(cls, user_order, signal_order, expected_order_type="normal"):
- """
- return True if the user_order matches the signal_order following the expected_order_type
- where "expected_order_type" is in
- - normal: normal matching. all words are present in the user_order. this is the default
- - strict: only word in the user order match. no more word
- - ordered-strict: only word in the user order and in the same order
- :param user_order: order from the user
- :param signal_order: order in the signal
- :param expected_order_type: type of order (normal, strict, ordered-strict)
- :return: True if the order match
- """
- matching_type_function = {
- "normal": cls.is_normal_matching,
- "strict": cls.is_strict_matching,
- "ordered-strict": cls.is_ordered_strict_matching
- }
-
- user_order = user_order.lower()
- signal_order = signal_order.lower()
- if expected_order_type in matching_type_function:
- return matching_type_function[expected_order_type](user_order, signal_order)
- else:
- logger.debug("[OrderAnalyser] non existing matching-type: '%s', fallback to 'normal'" % expected_order_type)
- return matching_type_function["normal"](user_order, signal_order)
- @classmethod
- def _get_instantiated_order_signal_from_user_order(cls, signal_order, user_order):
- """
- return instantiated signal order with parameters loaded from the user order
- E.g:
- signal_order = "this is an {{ variable }}
- user_order = "this is an order"
- returned value is: "this is an order"
- :param user_order: the order from the user
- :param signal_order: the order with brackets from the synapse
- :return: jinja instantiated order from the signal
- """
-
- parameters_from_user_order = NeuronParameterLoader.get_parameters(synapse_order=signal_order,
- user_order=user_order)
-
- t = Template(signal_order)
- signal_order = t.render(**parameters_from_user_order)
- return signal_order
|