OrderAnalyser.py 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. # coding: utf8
  2. import re
  3. from collections import Counter
  4. from core.Utils import Utils
  5. from core.Models import Order
  6. from core.NeuroneLauncher import NeuroneLauncher
  7. import logging
  8. logging.basicConfig()
  9. logger = logging.getLogger("kalliope")
  10. class OrderAnalyser:
  11. """
  12. This Class is used to compare the incoming message to the Signal/Order sentences.
  13. """
  14. def __init__(self, order, main_controller=None, brain=None):
  15. """
  16. Class used to load brain and run neuron attached to the received order
  17. :param order: spelt order
  18. :param main_controller
  19. :param brain: loaded brain
  20. """
  21. self.main_controller = main_controller
  22. self.order = order
  23. if isinstance(self.order, str):
  24. self.order = order.decode('utf-8')
  25. self.brain = brain
  26. logger.debug("OrderAnalyser, Received order: %s" % self.order)
  27. def start(self):
  28. # TODO : refactor this method !!
  29. """
  30. This method matches the incoming messages to the signals/order sentences provided in the Brain
  31. """
  32. synapses_found = False
  33. problem_in_neuron_found = False
  34. # create a dict of synapses that have benn launched
  35. launched_synapses = list()
  36. for synapse in self.brain.synapses:
  37. for signal in synapse.signals:
  38. if type(signal) == Order:
  39. if self._spelt_order_match_brain_order_via_table(signal.sentence, self.order):
  40. launched_synapses.append(synapse)
  41. synapses_found = True
  42. logger.debug("Order found! Run neurons: %s" % synapse.neurons)
  43. Utils.print_success("Order matched in the brain. Running synapse \"%s\"" % synapse.name)
  44. # if the order contains bracket, we get parameters said by the user
  45. params = None
  46. if self._is_containing_bracket(signal.sentence):
  47. params = self._associate_order_params_to_values(self.order, signal.sentence)
  48. logger.debug("Parameters for order: %s" % params)
  49. for neuron in synapse.neurons:
  50. if isinstance(neuron.parameters, dict):
  51. # print neuron.parameters
  52. if "args" in neuron.parameters:
  53. logger.debug("The neuron wait for parameter")
  54. # check that the user added parameters to his order
  55. if params is None:
  56. # we don't raise an error and break the program but we don't run the neuron
  57. problem_in_neuron_found = True
  58. Utils.print_danger("Error: The neuron %s is waiting for argument. "
  59. "Argument found in bracket in the given order" % neuron.name)
  60. else:
  61. # we add wanted arguments the existing neuron parameter dict
  62. for arg in neuron.parameters["args"]:
  63. if arg in params:
  64. logger.debug("Parameter %s added to the current parameter "
  65. "of the neuron: %s" % (arg, neuron.name))
  66. neuron.parameters[arg] = params[arg]
  67. else:
  68. # we don't raise an error and break the program but
  69. # we don't run the neuron
  70. problem_in_neuron_found = True
  71. Utils.print_danger("Error: Argument \"%s\" not found in the"
  72. " order" % arg)
  73. # if no error detected, we run the neuron
  74. if not problem_in_neuron_found:
  75. NeuroneLauncher.start_neurone(neuron)
  76. else:
  77. Utils.print_danger("A problem has been found in the Synapse.")
  78. if not synapses_found:
  79. Utils.print_info("No synapse match the captured order: %s" % self.order)
  80. # return the list of launched synapse
  81. return launched_synapses
  82. @classmethod
  83. def _associate_order_params_to_values(cls, order, order_to_check):
  84. """
  85. Associate the variables from the order to the incoming user order
  86. :param order_to_check: the order to check incoming from the brain
  87. :type order_to_check: str
  88. :param order: the order from user
  89. :type order: str
  90. :return: the dict corresponding to the key / value of the params
  91. """
  92. pattern = '\s+(?=[^\{\{\}\}]*\}\})'
  93. # Remove white spaces (if any) between the variable and the double brace then split
  94. list_word_in_order = re.sub(pattern, '', order_to_check).split()
  95. # get the order, defined by the first words before {{
  96. # /!\ Could be empty if order starts with double brace
  97. the_order = order_to_check[:order_to_check.find('{{')]
  98. # remove sentence before order which are sentences not matching anyway
  99. truncate_user_sentence = order[order.find(the_order):]
  100. truncate_list_word_said = truncate_user_sentence.split()
  101. # make dict var:value
  102. dict_var = {}
  103. for idx, ow in enumerate(list_word_in_order):
  104. if cls._is_containing_bracket(ow):
  105. # remove bracket and grab the next value / stop value
  106. var_name = ow.replace("{{", "").replace("}}", "")
  107. stop_value = cls._get_next_value_list(list_word_in_order[idx:])
  108. if stop_value is None:
  109. dict_var[var_name] = " ".join(truncate_list_word_said)
  110. break
  111. for word_said in truncate_list_word_said:
  112. if word_said == stop_value:
  113. break
  114. if var_name in dict_var:
  115. dict_var[var_name] += " " + word_said
  116. truncate_list_word_said = truncate_list_word_said[1:]
  117. else:
  118. dict_var[var_name] = word_said
  119. truncate_list_word_said = truncate_list_word_said[1:]
  120. return dict_var
  121. @staticmethod
  122. def _is_containing_bracket(sentence):
  123. """
  124. Return True if the text in <sentence> contains brackets
  125. :param sentence:
  126. :return:
  127. """
  128. # print "sentence to test %s" % sentence
  129. pattern = r"{{|}}"
  130. # prog = re.compile(pattern)
  131. check_bool = re.search(pattern, sentence)
  132. if check_bool is not None:
  133. return True
  134. return False
  135. @staticmethod
  136. def _get_next_value_list(list_to_check):
  137. ite = list_to_check.__iter__()
  138. next(ite, None)
  139. return next(ite, None)
  140. @classmethod
  141. def _spelt_order_match_brain_order_via_table(cls, order_to_analyse, user_said):
  142. """
  143. return true if all string that are in the sentence are present in the order to test
  144. :param order_to_analyse: String order to test
  145. :param user_said: String to compare to the order
  146. :return: True if all string are present in the order
  147. """
  148. list_word_user_said = user_said.split()
  149. split_order_without_bracket = cls._get_split_order_without_bracket(order_to_analyse)
  150. # if all words in the list of what the user said in in the list of word in the order
  151. return cls._counter_subset(split_order_without_bracket, list_word_user_said)
  152. @staticmethod
  153. def _get_split_order_without_bracket(order):
  154. """
  155. Get an order with bracket inside like: "hello my name is {{ name }}.
  156. return a list of string without bracket like ["hello", "my", "name", "is"]
  157. :param order: sentence to split
  158. :return: list of string without bracket
  159. """
  160. pattern = r"((?:{{\s*)[\w\.]+(?:\s*}}))"
  161. # find everything like {{ word }}
  162. matches = re.findall(pattern, order)
  163. for match in matches:
  164. order = order.replace(match, "")
  165. # then split
  166. split_order = order.split()
  167. return split_order
  168. @staticmethod
  169. def _counter_subset(list1, list2):
  170. """
  171. check if the number of occurrences matches
  172. :param list1:
  173. :param list2:
  174. :return:
  175. """
  176. c1, c2 = Counter(list1), Counter(list2)
  177. for k, n in c1.items():
  178. if n > c2[k]:
  179. return False
  180. return True