def _replace_brackets_by_loaded_parameter(cls, neuron_parameters, loaded_parameters): """ Receive a value (which can be a str or dict or list) and instantiate value in double brace bracket by the value specified in the loaded_parameters dict. This method will call itself until all values has been instantiated :param neuron_parameters: value to instantiate. Str or dict or list :param loaded_parameters: dict of parameters """ logger.debug("[NeuronLauncher] replacing brackets from %s, using %s" % (neuron_parameters, loaded_parameters)) # add variables from the short term memory to the list of loaded parameters that can be used in a template # the final dict is added into a key "kalliope_memory" to not override existing keys loaded form the order memory_dict = dict() memory_dict["kalliope_memory"] = Cortex.get_memory() if loaded_parameters is None: loaded_parameters = dict( ) # instantiate an empty dict in order to be able to add memory in it loaded_parameters.update(memory_dict) if isinstance(neuron_parameters, str) or isinstance( neuron_parameters, six.text_type): # replace bracket parameter only if the str contains brackets if Utils.is_containing_bracket(neuron_parameters): # check that the parameter to replace is available in the loaded_parameters dict if cls._neuron_parameters_are_available_in_loaded_parameters( neuron_parameters, loaded_parameters): # add parameters from global variable into the final loaded parameter dict settings = cls.load_settings() loaded_parameters.update(settings.variables) neuron_parameters = jinja2.Template( neuron_parameters).render(loaded_parameters) neuron_parameters = Utils.encode_text_utf8( neuron_parameters) return str(neuron_parameters) else: raise NeuronParameterNotAvailable return neuron_parameters if isinstance(neuron_parameters, dict): returned_dict = dict() for key, value in neuron_parameters.items(): # following keys are reserved by kalliope core if key in "say_template" or key in "file_template" or key in "kalliope_memory" \ or key in "from_answer_link": returned_dict[key] = value else: returned_dict[ key] = cls._replace_brackets_by_loaded_parameter( value, loaded_parameters) return returned_dict if isinstance(neuron_parameters, list): returned_list = list() for el in neuron_parameters: templated_value = cls._replace_brackets_by_loaded_parameter( el, loaded_parameters) returned_list.append(templated_value) return returned_list # in all other case (boolean or int for example) we return the value as it return neuron_parameters
def _find_synapse_to_run(cls, brain, settings, order): """ Find the list of the synapse matching the order. Note: we use named tuples: tuple_synapse_order = collections.namedtuple('tuple_synapse_matchingOrder',['synapse', 'order']) :param brain: the brain :param settings: the settings :param order: the provided order to match :return: the list of synapses launched (named tuples) """ synapse_to_run = cls._get_matching_synapse_list(brain.synapses, order) if not synapse_to_run: Utils.print_info("No synapse match the captured order: %s" % order) if settings.default_synapse is not None: default_synapse = cls._get_default_synapse_from_sysnapses_list( brain.synapses, settings.default_synapse) if default_synapse is not None: logger.debug("Default synapse found %s" % default_synapse) Utils.print_info("Default synapse found: %s, running it" % default_synapse.name) tuple_synapse_order = collections.namedtuple( 'tuple_synapse_matchingOrder', ['synapse', 'order']) synapse_to_run.append( tuple_synapse_order(synapse=default_synapse, order="")) return synapse_to_run
def _get_matching_synapse_list(cls, all_synapses_list, order_to_match): """ Class method to return all the matching synapses with the order from the complete of synapses. Note: we use named tuples: tuple_synapse_matchingOrder = collections.namedtuple('tuple_synapse_matchingOrder',['synapse', 'order']) :param all_synapses_list: the complete list of all synapses :param order_to_match: the order to match :type order_to_match: str :return: the list of matching synapses (named tuples) """ tuple_synapse_order = collections.namedtuple( 'tuple_synapse_matchingOrder', ['synapse', 'order']) matching_synapses_list = list() for synapse in all_synapses_list: for signal in synapse.signals: if type(signal) == Order: if cls.spelt_order_match_brain_order_via_table( signal.sentence, order_to_match): matching_synapses_list.append( tuple_synapse_order(synapse=synapse, order=signal.sentence)) logger.debug("Order found! Run neurons: %s" % synapse.neurons) Utils.print_success( "Order matched in the brain. Running synapse \"%s\"" % synapse.name) return matching_synapses_list
def test_remove_spaces_in_brackets(self): """ Test the Utils remove_spaces_in_brackets """ sentence = "This is the {{ bracket }}" expected_result = "This is the {{bracket}}" self.assertEqual(Utils.remove_spaces_in_brackets(sentence=sentence), expected_result, "Fail to remove spaces in one bracket") sentence = "This is the {{ bracket }} {{ second }}" expected_result = "This is the {{bracket}} {{second}}" self.assertEqual(Utils.remove_spaces_in_brackets(sentence=sentence), expected_result, "Fail to remove spaces in two brackets") # test with json sentence = "{\"params\": {\"apikey\": \"ISNOTMYPASSWORD\", " \ "\"query\": \"met le chauffage a {{ valeur }} degres\"}}" expected_result = "{\"params\": {\"apikey\": \"ISNOTMYPASSWORD\", " \ "\"query\": \"met le chauffage a {{valeur}} degres\"}}" self.assertEqual(Utils.remove_spaces_in_brackets(sentence=sentence), expected_result, "Fail to remove spaces in two brackets")
def start_neuron(cls, neuron, parameters_dict=None): """ Execute each neuron from the received neuron_list. Replace parameter if exist in the received dict of parameters_dict :param neuron: Neuron object to run :param parameters_dict: dict of parameter to load in each neuron if expecting a parameter :return: List of the instantiated neurons (no errors detected) """ if neuron.parameters is not None: try: neuron.parameters = cls._replace_brackets_by_loaded_parameter( neuron.parameters, parameters_dict) except NeuronParameterNotAvailable: Utils.print_danger( "Missing parameter in neuron %s. Execution skipped" % neuron.name) return None try: instantiated_neuron = NeuronLauncher.launch_neuron(neuron) except NeuronExceptions as e: Utils.print_danger("ERROR: Fail to execute neuron '%s'. " '%s' ". -> Execution skipped" % (neuron.name, e.message)) return None return instantiated_neuron
def get_list_match_synapse(cls, order, synapse_order_tuple): list_match_synapse = list() order = order.lower() # Needed for STT Correction for synapse in cls.brain.synapses: if synapse.enabled: for signal in synapse.signals: logger.debug("[OrderAnalyser] Testing Synapse name %s" % synapse.name) # we are only concerned by synapse with a order type of signal if signal.name == "order": # Check if signal is matching the order and parameters. if cls.is_order_matching_signal(user_order=order, signal=signal): order = cls.order_correction(order=order, signal=signal) 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, matched_order=cls.get_signal_order(signal), user_order=order)) # we matched this synapse with an order, don't need to check another break return list_match_synapse
def _neuron_parameters_are_available_in_loaded_parameters( string_parameters, loaded_parameters): """ Check that all parameters in brackets are available in the loaded_parameters dict E.g: string_parameters = "this is a {{ parameter1 }}" Will return true if the loaded_parameters looks like the following loaded_parameters { "parameter1": "a value"} :param string_parameters: The string that contains one or more parameters in brace brackets :param loaded_parameters: Dict of parameter :return: True if all parameters in brackets have an existing key in loaded_parameters dict """ list_parameters_with_brackets = Utils.find_all_matching_brackets( string_parameters) # remove brackets to keep only the parameter name for parameter_with_brackets in list_parameters_with_brackets: parameter = Utils.remove_spaces_in_brackets( parameter_with_brackets) parameter = parameter.replace("{{", "").replace("}}", "") if loaded_parameters is None or parameter not in loaded_parameters: Utils.print_danger( "The parameter %s is not available in the order" % str(parameter)) return False return True
def get_list_match_synapse(cls, order, synapse_order_tuple): list_match_synapse = list() for synapse in cls.brain.synapses: if synapse.enabled: for signal in synapse.signals: # we are only concerned by synapse with a order type of signal if signal.name == "order": # get the type of matching expected, by default "normal" expected_matching_type = "normal" signal_order = None if isinstance(signal.parameters, str) or isinstance(signal.parameters, six.text_type): 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 expected_matching_type = cls.get_matching_type(signal) order = cls.order_correction(order, signal) if cls.is_order_matching(user_order=order, signal_order=signal_order, expected_order_type=expected_matching_type): # the order match the synapse, we add it to the returned list 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)) return list_match_synapse
def test_get_dynamic_class_instantiation(self): """ Test that an instance as been instantiate properly. """ sl = SettingLoader() sl.settings.resource_dir = '/var/tmp/test/resources' neuron = Neuron( name='Say', parameters={'message': 'test dynamic class instantiate'}) say1 = Utils.get_dynamic_class_instantiation( package_name="neurons", module_name=neuron.name.capitalize(), parameters=neuron.parameters, resources_dir='/var/tmp/test/resources') self.assertTrue(isinstance(say1, Say), "Failed to dynamically instantiate neuron 'Say'") say2 = Utils.get_dynamic_class_instantiation( package_name="neurons", module_name=neuron.name.capitalize(), parameters=neuron.parameters, resources_dir='/var/tmp/test/resources') self.assertTrue(isinstance(say2, Say), "Failed to dynamically instantiate neuron 'Say'") self.assertNotEqual( id(say1), id(say2), "Dynamic class instantiations must return unique instances")
def start_neuron(cls, neuron, parameters_dict=dict()): """ Execute each neuron from the received neuron_list. Replace parameter if exist in the received dict of parameters_dict :param neuron: Neuron object to run :param parameters_dict: dict of parameter to load in each neuron if expecting a parameter :return: List of the instantiated neurons (no errors detected) """ if neuron.parameters is not None: try: neuron.parameters = cls._replace_brackets_by_loaded_parameter( neuron.parameters, parameters_dict) except NeuronParameterNotAvailable: Utils.print_danger( "Missing parameter in neuron %s. Execution skipped" % neuron.name) return None try: instantiated_neuron = NeuronLauncher.launch_neuron(neuron) except Exception as e: Utils.print_danger( f"ERROR: Fail to execute neuron '{neuron.name}'. " 'e.message' ". -> Execution skipped, run with debug flag for more information" ) logger.debug(traceback.format_exc()) return None return instantiated_neuron
def say(self, message): """ USe TTS to speak out loud the Message. A message can be a string, a list or a dict If it's a string, simply use the TTS with the message If it's a list, we select randomly a string in the list and give it to the TTS If it's a dict, we use the template given in parameter to create a string that we give to the TTS :param message: Can be a String or a dict or a list .. raises:: TTSModuleNotFound """ logger.debug("[NeuronModule] Say() called with message: %s" % message) tts_message = None # we can save parameters from the neuron in memory Cortex.save_neuron_parameter_in_memory(self.kalliope_memory, message) if isinstance(message, str) or isinstance(message, six.text_type): logger.debug("[NeuronModule] message is string") tts_message = message if isinstance(message, list): logger.debug("[NeuronModule] message is list") tts_message = random.choice(message) if isinstance(message, dict): logger.debug("[NeuronModule] message is dict") tts_message = self._get_message_from_dict(message) if tts_message is not None: logger.debug("[NeuronModule] tts_message to say: %s" % tts_message) self.tts_message = tts_message Utils.print_success(tts_message) # save in kalliope memory the last tts message Cortex.save("kalliope_last_tts_message", tts_message) # process the audio only if the mute flag is false if self.settings.options.mute: logger.debug("[NeuronModule] mute is True, Kalliope is muted") else: logger.debug( "[NeuronModule] mute is False, make Kalliope speaking") HookManager.on_start_speaking() # get the instance of the TTS module tts_folder = None if self.settings.resources: tts_folder = self.settings.resources.tts_folder tts_module_instance = Utils.get_dynamic_class_instantiation( package_name="tts", module_name=self.tts.name, parameters=self.tts.parameters, resources_dir=tts_folder) # generate the audio file and play it tts_module_instance.say(tts_message) HookManager.on_stop_speaking()
def signal_handler(signal, frame): """ Used to catch a keyboard signal like Ctrl+C in order to kill the kalliope program :param signal: signal handler :param frame: execution frame """ print "\n" Utils.print_info("Ctrl+C pressed. Killing Kalliope") sys.exit(0)
def say(self, message): """ USe TTS to speak out loud the Message. A message can be a string, a list or a dict If it's a string, simply use the TTS with the message If it's a list, we select randomly a string in the list and give it to the TTS If it's a dict, we use the template given in parameter to create a string that we give to the TTS :param message: Can be a String or a dict or a list .. raises:: TTSModuleNotFound """ logger.debug("[NeuronModule] Say() called with message: %s" % message) tts_message = None # we can save parameters from the neuron in memory Cortex.save_neuron_parameter_in_memory(self.kalliope_memory, message) if isinstance(message, str) or isinstance(message, six.text_type): logger.debug("[NeuronModule] message is string") tts_message = message if isinstance(message, list): logger.debug("[NeuronModule] message is list") tts_message = random.choice(message) if isinstance(message, dict): logger.debug("[NeuronModule] message is dict") tts_message = self._get_message_from_dict(message) if tts_message is not None: logger.debug("[NeuronModule] tts_message to say: %s" % tts_message) self.tts_message = tts_message Utils.print_success(tts_message) # save in kalliope memory the last tts message Cortex.save("kalliope_last_tts_message", tts_message) # process the audio only if the mute flag is false if self.settings.options.mute: logger.debug("[NeuronModule] mute is True, Kalliope is muted") else: logger.debug("[NeuronModule] mute is False, make Kalliope speaking") HookManager.on_start_speaking() # get the instance of the TTS module tts_folder = None if self.settings.resources: tts_folder = self.settings.resources.tts_folder tts_module_instance = Utils.get_dynamic_class_instantiation(package_name="tts", module_name=self.tts.name, parameters=self.tts.parameters, resources_dir=tts_folder) # generate the audio file and play it tts_module_instance.say(tts_message) HookManager.on_stop_speaking()
def load_stt_plugin(self): if self.stt is None: self.stt_module_name = self.settings.default_stt_name for stt_object in self.settings.stts: if stt_object.name == self.stt_module_name: stt_object.parameters["callback"] = self.callback Utils.get_dynamic_class_instantiation( package_name='stt', module_name=stt_object.name.capitalize(), parameters=stt_object.parameters)
def __init__(self, file_path=None): self.file_path = file_path if self.file_path is None: self.file_path = Utils.get_real_file_path(FILE_NAME) else: self.file_path = Utils.get_real_file_path(file_path) # if the returned file path is none, the file doesn't exist if self.file_path is None: raise SettingNotFound("Settings.yml file not found") self.yaml_config = self._get_yaml_config() self.settings = self._get_settings()
def say(self, message): """ USe TTS to speak out loud the Message. A message can be a string, a list or a dict If it's a string, simply use the TTS with the message If it's a list, we select randomly a string in the list and give it to the TTS If it's a dict, we use the template given in parameter to create a string that we give to the TTS :param message: Can be a String or a dict or a list .. raises:: TTSModuleNotFound """ logger.debug("[NeuronModule] Say() called with message: %s" % message) tts_message = None if isinstance(message, str) or isinstance(message, six.text_type): logger.debug("[NeuronModule] message is string") tts_message = message if isinstance(message, list): logger.debug("[NeuronModule] message is list") tts_message = random.choice(message) if isinstance(message, dict): logger.debug("[NeuronModule] message is dict") tts_message = self._get_message_from_dict(message) if tts_message is not None: logger.debug("[NeuronModule] tts_message to say: %s" % tts_message) self.tts_message = tts_message Utils.print_success(tts_message) # process the audio only if the no_voice flag is false if self.no_voice: logger.debug("[NeuronModule] no_voice is True, Kalliope is muted") else: logger.debug("[NeuronModule] no_voice is False, make Kalliope speaking") # get the instance of the TTS module tts_folder = None if self.settings.resources: tts_folder = self.settings.resources.tts_folder tts_module_instance = Utils.get_dynamic_class_instantiation(package_name="tts", module_name=self.tts.name, parameters=self.tts.parameters, resources_dir=tts_folder) # Kalliope will talk, turn on the LED self.switch_on_led_talking(rpi_settings=self.settings.rpi_settings, on=True) # generate the audio file and play it tts_module_instance.say(tts_message) # Kalliope has finished to talk, turn off the LED self.switch_on_led_talking(rpi_settings=self.settings.rpi_settings, on=False)
def test_find_all_matching_brackets(self): """ Test the Utils find all matching brackets """ sentence = "This is the {{bracket}}" expected_result = ["{{bracket}}"] self.assertEqual(Utils.find_all_matching_brackets(sentence=sentence), expected_result, "Fail to match one bracket") sentence = "This is the {{bracket}} {{second}}" expected_result = ["{{bracket}}", "{{second}}"] self.assertEqual(Utils.find_all_matching_brackets(sentence=sentence), expected_result, "Fail to match two brackets")
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') # We use a namedtuple to associate the synapse and the signal of the synapse synapse_order_tuple = collections.namedtuple( 'tuple_synapse_matchingOrder', ['synapse', 'order']) list_match_synapse = list() # if the received order is None we can stop the process immediately if order is None: return list_match_synapse # test each synapse from the brain for synapse in cls.brain.synapses: # we are only concerned by synapse with a order type of signal for signal in synapse.signals: if signal.name == "order": if cls.spelt_order_match_brain_order_via_table( signal.parameters, order): # the order match the synapse, we add it to the returned list 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.parameters)) # create a list of MatchedSynapse from the tuple list 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
def _replace_brackets_by_loaded_parameter(cls, neuron_parameters, loaded_parameters=dict()): """ Receive a value (which can be a str or dict or list) and instantiate value in double brace bracket by the value specified in the loaded_parameters dict. This method will call itself until all values has been instantiated :param neuron_parameters: value to instantiate. Str or dict or list :param loaded_parameters: dict of parameters """ logger.debug("[NeuronLauncher] replacing brackets from %s, using %s" % (neuron_parameters, loaded_parameters)) if isinstance(neuron_parameters, str) or isinstance(neuron_parameters, six.text_type): # replace bracket parameter only if the str contains brackets if Utils.is_containing_bracket(neuron_parameters): settings = cls.load_settings() # Priority to memory over the variables loaded_parameters.update(settings.variables) memory_dict = dict() # add variables from the short term memory to the list of loaded parameters that can be used in a template # the final dict is added into a key "kalliope_memory" to not override existing keys loaded form the order memory_dict["kalliope_memory"] = Cortex.get_memory() loaded_parameters.update(memory_dict) # check that the parameter to replace is available in the loaded_parameters dict if cls._neuron_parameters_are_available_in_loaded_parameters(neuron_parameters, loaded_parameters): # add parameters from global variable into the final loaded parameter dict neuron_parameters = jinja2.Template(neuron_parameters).render(loaded_parameters) neuron_parameters = Utils.encode_text_utf8(neuron_parameters) return str(neuron_parameters) else: raise NeuronParameterNotAvailable return neuron_parameters if isinstance(neuron_parameters, dict): returned_dict = dict() for key, value in neuron_parameters.items(): # following keys are reserved by kalliope core if key in "say_template" or key in "file_template" or key in "kalliope_memory" \ or key in "from_answer_link": returned_dict[key] = value else: returned_dict[key] = cls._replace_brackets_by_loaded_parameter(value, loaded_parameters) return returned_dict if isinstance(neuron_parameters, list): returned_list = list() for el in neuron_parameters: templated_value = cls._replace_brackets_by_loaded_parameter(el, loaded_parameters) returned_list.append(templated_value) return returned_list # in all other case (boolean or int for example) we return the value as it return neuron_parameters
def test_get_next_value_list(self): # Success list_to_test = {1, 2, 3} self.assertEqual(Utils.get_next_value_list(list_to_test), 2, "Fail to match the expected next value from the list") # Failure list_to_test = {1} self.assertEqual(Utils.get_next_value_list(list_to_test), None, "Fail to ensure there is no next value from the list") # Behaviour list_to_test = {} self.assertEqual(Utils.get_next_value_list(list_to_test), None, "Fail to ensure the empty list return None value")
def save_neuron_parameter_in_memory(cls, kalliope_memory_dict, neuron_parameters): """ receive a dict of value send by the child neuron save in kalliope memory all value E.g dict_parameter_to_save = {"my_key_to_save_in_memory": "{{ output_val_from_neuron }}"} neuron_parameter = {"output_val_from_neuron": "this_is_a_value" } then the cortex will save in memory the key "my_key_to_save_in_memory" and attach the value "this_is_a_value" :param neuron_parameters: dict of parameter the neuron has processed and send to the neurone module to be processed by the TTS engine :param kalliope_memory_dict: a dict of key value the user want to save from the dict_neuron_parameter """ if kalliope_memory_dict is not None: logger.debug("[Cortex] save_memory - User want to save: %s" % kalliope_memory_dict) logger.debug( "[Cortex] save_memory - Available parameters in the neuron: %s" % neuron_parameters) for key, value in kalliope_memory_dict.items(): # ask the cortex to save in memory the target "key" if it was in parameters of the neuron if isinstance(neuron_parameters, dict): if Utils.is_containing_bracket(value): value = jinja2.Template(value).render( neuron_parameters) Cortex.save(key, value)
def save_neuron_parameter_in_memory(cls, kalliope_memory_dict, neuron_parameters): """ receive a dict of value send by the child neuron save in kalliope memory all value E.g dict_parameter_to_save = {"my_key_to_save_in_memory": "{{ output_val_from_neuron }}"} neuron_parameter = {"output_val_from_neuron": "this_is_a_value" } then the cortex will save in memory the key "my_key_to_save_in_memory" and attach the value "this_is_a_value" :param neuron_parameters: dict of parameter the neuron has processed and send to the neurone module to be processed by the TTS engine :param kalliope_memory_dict: a dict of key value the user want to save from the dict_neuron_parameter """ if kalliope_memory_dict is not None: logger.debug("[Cortex] save_memory - User want to save: %s" % kalliope_memory_dict) logger.debug("[Cortex] save_memory - Available parameters in the neuron: %s" % neuron_parameters) for key, value in kalliope_memory_dict.items(): # ask the cortex to save in memory the target "key" if it was in parameters of the neuron if isinstance(neuron_parameters, dict): if Utils.is_containing_bracket(value): value = jinja2.Template(value).render(neuron_parameters) Cortex.save(key, value)
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 the signal order contains bracket, we need to instantiate it with loaded parameters from the user 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
def load_stt_correction_file(cls, stt_correction_file): stt_correction_file_path = Utils.get_real_file_path( stt_correction_file) stt_correction_file = open(stt_correction_file_path, "r") stt_correction = yaml.full_load(stt_correction_file) return stt_correction
def _get_random_wake_up_sounds(settings): """ Return a list of the wake up sounds set up on the settings.yml file :param settings: The YAML settings file :type settings: dict :return: list of wake up sounds :rtype: list of str :Example: wakeup_sounds = cls._get_random_wake_up_sounds(settings) .. seealso:: .. raises:: NullSettingException .. warnings:: Class Method and Private """ try: random_wake_up_sounds_list = settings["random_wake_up_sounds"] # In case files are declared in settings.yml, make sure kalliope can access them. for sound in random_wake_up_sounds_list: if Utils.get_real_file_path(sound) is None: raise SettingInvalidException("sound file %s not found" % sound) except KeyError: # User does not provide this settings return None # The the setting is present, the list cannot be empty if random_wake_up_sounds_list is None: raise NullSettingException( "random_wake_up_sounds settings is empty") return random_wake_up_sounds_list
def test_remove_spaces_in_brackets(self): """ Test the Utils remove_spaces_in_brackets """ sentence = "This is the {{ bracket }}" expected_result = "This is the {{bracket}}" self.assertEqual(Utils.remove_spaces_in_brackets(sentence=sentence), expected_result, "Fail to remove spaces in one bracket") sentence = "This is the {{ bracket }} {{ second }}" expected_result = "This is the {{bracket}} {{second}}" self.assertEqual(Utils.remove_spaces_in_brackets(sentence=sentence), expected_result, "Fail to remove spaces in two brackets")
def save_parameter_from_order_in_memory(cls, order_parameters): """ Save key from the temp dict (where parameters loaded from the voice order where placed temporary) into the memory dict :param order_parameters: dict of key to save. {'key_name_in_memory': 'key_name_in_temp_dict'} :return True if a value has been saved in the kalliope memory """ order_saved = False if order_parameters is not None: logger.debug( "[Cortex] save_parameter_from_order_in_memory - User want to save: %s" % order_parameters) logger.debug( "[Cortex] save_parameter_from_order_in_memory - Available parameters in orders: %s" % cls.temp) for key, value in order_parameters.items(): # ask the cortex to save in memory the target "key" if it was in the order if Utils.is_containing_bracket(value): # if the key exist in the temp dict we can load it with jinja value = jinja2.Template(value).render(Cortex.temp) if value: Cortex.save(key, value) order_saved = True return order_saved
def _replace_brackets_by_loaded_parameter(cls, neuron_parameters, loaded_parameters): """ Receive a value (which can be a str or dict or list) and instantiate value in double brace bracket by the value specified in the loaded_parameters dict. This method will call itself until all values has been instantiated :param neuron_parameters: value to instantiate. Str or dict or list :param loaded_parameters: dict of parameters """ logger.debug("[NeuronLauncher] replacing brackets from %s, using %s" % (neuron_parameters, loaded_parameters)) if isinstance(neuron_parameters, str) or isinstance( neuron_parameters, six.text_type): # replace bracket parameter only if the str contains brackets if Utils.is_containing_bracket(neuron_parameters): # check that the parameter to replace is available in the loaded_parameters dict if cls._neuron_parameters_are_available_in_loaded_parameters( neuron_parameters, loaded_parameters): neuron_parameters = jinja2.Template( neuron_parameters).render(loaded_parameters) neuron_parameters = Utils.encode_text_utf8( neuron_parameters) return str(neuron_parameters) else: raise NeuronParameterNotAvailable return neuron_parameters if isinstance(neuron_parameters, dict): returned_dict = dict() for key, value in neuron_parameters.items(): if key in "say_template" or key in "file_template": # those keys are reserved for the TTS. returned_dict[key] = value else: returned_dict[ key] = cls._replace_brackets_by_loaded_parameter( value, loaded_parameters) return returned_dict if isinstance(neuron_parameters, list): returned_list = list() for el in neuron_parameters: templated_value = cls._replace_brackets_by_loaded_parameter( el, loaded_parameters) returned_list.append(templated_value) return returned_list # in all other case (boolean or int for example) we return the value as it return neuron_parameters
def test_get_next_value_list(self): # Success list_to_test = {1, 2, 3} self.assertEqual( Utils.get_next_value_list(list_to_test), 2, "Fail to match the expected next value from the list") # Failure list_to_test = {1} self.assertEqual( Utils.get_next_value_list(list_to_test), None, "Fail to ensure there is no next value from the list") # Behaviour list_to_test = {} self.assertEqual(Utils.get_next_value_list(list_to_test), None, "Fail to ensure the empty list return None value")
def _associate_order_params_to_values(order, order_to_check): """ Associate the variables from the order to the incoming user order :param order_to_check: the order to check incoming from the brain :type order_to_check: str :param order: the order from user :type order: str :return: the dict corresponding to the key / value of the params """ logger.debug( "[OrderAnalyser._associate_order_params_to_values] user order: %s, " "order to check: %s" % (order, order_to_check)) list_word_in_order = Utils.remove_spaces_in_brackets( order_to_check).split() # get the order, defined by the first words before {{ # /!\ Could be empty if order starts with double brace the_order = order_to_check[:order_to_check.find('{{')] # remove sentence before order which are sentences not matching anyway truncate_user_sentence = order[order.find(the_order):] truncate_list_word_said = truncate_user_sentence.split() # make dict var:value dict_var = dict() for idx, ow in enumerate(list_word_in_order): if Utils.is_containing_bracket(ow): # remove bracket and grab the next value / stop value var_name = ow.replace("{{", "").replace("}}", "") stop_value = Utils.get_next_value_list( list_word_in_order[idx:]) if stop_value is None: dict_var[var_name] = " ".join(truncate_list_word_said) break for word_said in truncate_list_word_said: if word_said == stop_value: break if var_name in dict_var: dict_var[var_name] += " " + word_said truncate_list_word_said = truncate_list_word_said[1:] else: dict_var[var_name] = word_said truncate_list_word_said = truncate_list_word_said[1:] return dict_var
def _get_default_synapse_from_sysnapses_list(all_synapses_list, default_synapse_name): """ Static method to get the default synapse if it exists. :param all_synapses_list: the complete list of all synapses :param default_synapse_name: the synapse to find :return: the Synapse """ default_synapse = None for synapse in all_synapses_list: if synapse.name == default_synapse_name: logger.debug("Default synapse found: %s" % synapse.name) default_synapse = synapse break if default_synapse is None: logger.debug("Default synapse not found") Utils.print_warning("Default synapse not found") return default_synapse
def start_neuron(cls, neuron, parameters_dict=None): """ Execute each neuron from the received neuron_list. Replace parameter if exist in the received dict of parameters_dict :param neuron: Neuron object to run :param parameters_dict: dict of parameter to load in each neuron if expecting a parameter :return: List of the instantiated neurons (no errors detected) """ if neuron.parameters is not None: try: neuron.parameters = cls._replace_brackets_by_loaded_parameter( neuron.parameters, parameters_dict) except NeuronParameterNotAvailable: Utils.print_danger("The neuron %s cannot be launched" % neuron.name) return None instantiated_neuron = NeuronLauncher.launch_neuron(neuron) return instantiated_neuron
def test_get_current_file_parent_path(self): """ Expect to get back the parent path file """ path_to_test = "../kalliope/core/Utils" expected_result = os.path.normpath("../kalliope/core") self.assertEqual( Utils.get_current_file_parent_path(path_to_test), expected_result, "fail getting the parent parent path from the given path")
def test_get_current_file_parent_parent_path(self): """ Expect to get back the parent parent path file """ path_to_test = "../kalliope/core/Utils" expected_result = os.path.normpath("../kalliope") self.assertEqual(Utils.get_current_file_parent_parent_path(path_to_test), expected_result, "fail getting the parent parent path from the given path")
def say(self, message): """ USe TTS to speak out loud the Message. A message can be a string, a list or a dict If it's a string, simply use the TTS with the message If it's a list, we select randomly a string in the list and give it to the TTS If it's a dict, we use the template given in parameter to create a string that we give to the TTS :param message: Can be a String or a dict or a list .. raises:: TTSModuleNotFound """ logger.debug("NeuronModule Say() called with message: %s" % message) tts_message = None if isinstance(message, str) or isinstance(message, unicode): logger.debug("message is string") tts_message = message if isinstance(message, list): logger.debug("message is list") tts_message = random.choice(message) if isinstance(message, dict): logger.debug("message is dict") tts_message = self._get_message_from_dict(message) if tts_message is not None: logger.debug("tts_message to say: %s" % tts_message) # create a tts object from the tts the user want to use tts_object = next( (x for x in self.settings.ttss if x.name == self.tts), None) if tts_object is None: raise TTSModuleNotFound( "The tts module name %s does not exist in settings file" % self.tts) # change the cache settings with the one precised for the current neuron if self.override_cache is not None: tts_object.parameters = self._update_cache_var( self.override_cache, tts_object.parameters) logger.debug("NeuroneModule: TTS args: %s" % tts_object) # get the instance of the TTS module tts_folder = None if self.settings.resources: tts_folder = self.settings.resources.tts_folder tts_module_instance = Utils.get_dynamic_class_instantiation( package_name="tts", module_name=tts_object.name, parameters=tts_object.parameters, resources_dir=tts_folder) # generate the audio file and play it tts_module_instance.say(tts_message)
def get_list_match_synapse(cls, order, synapse_order_tuple): list_match_synapse = list() for synapse in cls.brain.synapses: if synapse.enabled: for signal in synapse.signals: # we are only concerned by synapse with a order type of signal if signal.name == "order": # get the type of matching expected, by default "normal" expected_matching_type = "normal" signal_order = None if isinstance(signal.parameters, str) or isinstance( signal.parameters, six.text_type): 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 expected_matching_type = cls.get_matching_type( signal) order = cls.order_correction(order, signal) if cls.is_order_matching( user_order=order, signal_order=signal_order, expected_order_type=expected_matching_type): # the order match the synapse, we add it to the returned list 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)) return list_match_synapse
def _get_file_template(cls, file_template, message_dict): real_file_template_path = Utils.get_real_file_path(file_template) if real_file_template_path is None: raise TemplateFileNotFoundException("Template file %s not found in templates folder" % real_file_template_path) # load the content of the file as template t = Template(cls._get_content_of_file(real_file_template_path)) returned_message = t.render(**message_dict) return returned_message
def test_encode_text_utf8(self): """ Test encoding the text in utf8 """ sentence = "kâllìöpé" if sys.version_info[0] < 3: sentence = sentence.decode('utf8') expected_sentence = "kâllìöpé" self.assertEqual(Utils.encode_text_utf8(text=sentence), expected_sentence)
def serialize(self): """ This method allows to serialize in a proper way this object :return: A dict of name and parameters :rtype: Dict """ self.tts_message = Utils.encode_text_utf8(self.tts_message) return { 'neuron_name': self.neuron_name, 'generated_message': self.tts_message }
def start_neuron(cls, neuron, parameters_dict=dict()): """ Execute each neuron from the received neuron_list. Replace parameter if exist in the received dict of parameters_dict :param neuron: Neuron object to run :param parameters_dict: dict of parameter to load in each neuron if expecting a parameter :return: List of the instantiated neurons (no errors detected) """ if neuron.parameters is not None: try: neuron.parameters = cls._replace_brackets_by_loaded_parameter(neuron.parameters, parameters_dict) except NeuronParameterNotAvailable: Utils.print_danger("Missing parameter in neuron %s. Execution skipped" % neuron.name) return None try: instantiated_neuron = NeuronLauncher.launch_neuron(neuron) except NeuronExceptions as e: Utils.print_danger("ERROR: Fail to execute neuron '%s'. " '%s' ". -> Execution skipped" % (neuron.name, e.message)) return None return instantiated_neuron
def _neuron_parameters_are_available_in_loaded_parameters(string_parameters, loaded_parameters): """ Check that all parameters in brackets are available in the loaded_parameters dict E.g: string_parameters = "this is a {{ parameter1 }}" Will return true if the loaded_parameters looks like the following loaded_parameters { "parameter1": "a value"} :param string_parameters: The string that contains one or more parameters in brace brackets :param loaded_parameters: Dict of parameter :return: True if all parameters in brackets have an existing key in loaded_parameters dict """ list_parameters_with_brackets = Utils.find_all_matching_brackets(string_parameters) # remove brackets to keep only the parameter name for parameter_with_brackets in list_parameters_with_brackets: parameter = Utils.remove_spaces_in_brackets(parameter_with_brackets) parameter = parameter.replace("{{", "").replace("}}", "") if loaded_parameters is None or parameter not in loaded_parameters: Utils.print_danger("The parameter %s is not available in the order" % str(parameter)) return False return True
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, "") # then split split_order = order.split() return split_order
def test_get_dynamic_class_instantiation(self): """ Test that an instance as been instantiate properly. """ sl = SettingLoader() sl.settings.resource_dir = '/var/tmp/test/resources' neuron = Neuron(name='Say', parameters={'message': 'test dynamic class instantiate'}) self.assertTrue(isinstance(Utils.get_dynamic_class_instantiation(package_name="neurons", module_name=neuron.name.capitalize(), parameters=neuron.parameters, resources_dir='/var/tmp/test/resources'), Say), "Fail instantiate a class")
def launch_neuron(cls, neuron): """ Start a neuron plugin :param neuron: neuron object :type neuron: Neuron :return: """ logger.debug("Run neuron: \"%s\"" % (neuron.__str__())) settings = cls.load_settings() neuron_folder = None if settings.resources: neuron_folder = settings.resources.neuron_folder return Utils.get_dynamic_class_instantiation(package_name="neurons", module_name=neuron.name, parameters=neuron.parameters, resources_dir=neuron_folder)
def load_stt_plugin(self): if self.stt is None: self.stt_module_name = self.settings.default_stt_name logger.debug("[OrderListener] stt module name : %s" % self.stt_module_name) for stt_object in self.settings.stts: if stt_object.name == self.stt_module_name: stt_object.parameters["callback"] = self.callback # add the audio file path to the list of parameter if set if self.audio_file_path is not None: stt_object.parameters["audio_file_path"] = self.audio_file_path stt_folder = None if self.settings.resources: stt_folder = self.settings.resources.stt_folder return Utils.get_dynamic_class_instantiation(package_name='stt', module_name=stt_object.name.capitalize(), parameters=stt_object.parameters, resources_dir=stt_folder)
def _get_variables(settings): """ Return the dict of variables from the settings. :param settings: The YAML settings file :return: dict """ variables = dict() try: variables_files_name = settings["var_files"] # In case files are declared in settings.yml, make sure kalliope can access them. for files in variables_files_name: var = Utils.get_real_file_path(files) if var is None: raise SettingInvalidException("Variables file %s not found" % files) else: variables.update(YAMLLoader.get_config(var)) return variables except KeyError: # User does not provide this settings return dict()
def test_is_containing_bracket(self): # Success order_to_test = "This test contains {{ bracket }}" self.assertTrue(Utils.is_containing_bracket(order_to_test), "Fail returning True when order contains spaced brackets") order_to_test = "This test contains {{bracket }}" self.assertTrue(Utils.is_containing_bracket(order_to_test), "Fail returning True when order contains right spaced bracket") order_to_test = "This test contains {{ bracket}}" self.assertTrue(Utils.is_containing_bracket(order_to_test), "Fail returning True when order contains left spaced bracket") order_to_test = "This test contains {{bracket}}" self.assertTrue(Utils.is_containing_bracket(order_to_test), "Fail returning True when order contains no spaced bracket") # Failure order_to_test = "This test does not contain bracket" self.assertFalse(Utils.is_containing_bracket(order_to_test), "Fail returning False when order has no brackets") # Behaviour order_to_test = "" self.assertFalse(Utils.is_containing_bracket(order_to_test), "Fail returning False when no order") # Behaviour int order_to_test = 6 self.assertFalse(Utils.is_containing_bracket(order_to_test), "Fail returning False when an int") # Behaviour unicode order_to_test = "j'aime les goûters l'été" self.assertFalse(Utils.is_containing_bracket(order_to_test), "Fail returning False when an int")
def save_parameter_from_order_in_memory(cls, order_parameters): """ Save key from the temp dict (where parameters loaded from the voice order where placed temporary) into the memory dict :param order_parameters: dict of key to save. {'key_name_in_memory': 'key_name_in_temp_dict'} :return True if a value has been saved in the kalliope memory """ order_saved = False if order_parameters is not None: logger.debug("[Cortex] save_parameter_from_order_in_memory - User want to save: %s" % order_parameters) logger.debug("[Cortex] save_parameter_from_order_in_memory - Available parameters in orders: %s" % cls.temp) for key, value in order_parameters.items(): # ask the cortex to save in memory the target "key" if it was in the order if Utils.is_containing_bracket(value): # if the key exist in the temp dict we can load it with jinja value = jinja2.Template(value).render(Cortex.temp) if value: Cortex.save(key, value) order_saved = True return order_saved
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 the signal order contains bracket, we need to instantiate it with loaded parameters from the user 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
def test_get_real_file_path(self): """ Expect to load the proper file following the order : - Provided absolute path - Current user path + file_name - /etc/kalliope + file_name - /path/to/kalliope/ +file_name """ ### # Test the absolute path dir_path = "/tmp/kalliope/tests/" file_name = "test_real_file_path" absolute_path_to_test = os.path.join(dir_path, file_name) expected_result = absolute_path_to_test if not os.path.exists(dir_path): os.makedirs(dir_path) # touch the file open(absolute_path_to_test, 'a').close() self.assertEqual(Utils.get_real_file_path(absolute_path_to_test), expected_result, "Fail to match the given absolute path ") # Clean up if os.path.exists(absolute_path_to_test): os.remove(absolute_path_to_test) ### # test the Current path file_name = "test_real_file_path" expected_result = os.getcwd() + os.sep + file_name # touch the file open(file_name, 'a').close() self.assertEqual(Utils.get_real_file_path(file_name), expected_result, "Fail to match the Current path ") # Clean up if os.path.exists(file_name): os.remove(file_name) ### # test /etc/kalliope # /!\ need permissions # dir_path = "/etc/kalliope/" # file_name = "test_real_file_path" # path_to_test = os.path.join(dir_path,file_name) # expected_result = "/etc/kalliope" + os.sep + file_name # if not os.path.exists(dir_path): # os.makedirs(dir_path) # # # touch the file # open(path_to_test, 'a').close() # # self.assertEquals(Utils.get_real_file_path(file_name), # expected_result, # "Fail to match the /etc/kalliope path") # # Clean up # if os.path.exists(file_name): # os.remove(file_name) ### # /an/unknown/path/kalliope/ dir_path = "../kalliope/" file_name = "test_real_file_path" path_to_test = os.path.join(dir_path, file_name) expected_result = os.path.normpath(os.getcwd() + os.sep + os.pardir + os.sep + "kalliope" + os.sep + file_name) if not os.path.exists(dir_path): os.makedirs(dir_path) # touch the file open(path_to_test, 'a').close() self.assertEqual(Utils.get_real_file_path(file_name), expected_result, "Fail to match the /an/unknown/path/kalliope path") # Clean up if os.path.exists(expected_result): os.remove(expected_result)
def load_stt_correction_file(cls, stt_correction_file): stt_correction_file_path = Utils.get_real_file_path(stt_correction_file) stt_correction_file = open(stt_correction_file_path, "r") stt_correction = yaml.load(stt_correction_file) return stt_correction