Browse Source

Merge pull request #257 from kalliope-project/review_args

Review args
Monf 8 years ago
parent
commit
134537aa68

+ 8 - 10
Docs/neurons.md

@@ -55,22 +55,20 @@ From global variables: (cf: [settings.md](settings.md))
 
 
 From the captured order:
 From the captured order:
 ```yml
 ```yml
-  - name: "run-neuron-with-parameter-in-order"
+  - name: "say-hello"
     signals:
     signals:
-      - order: "this is an order with the parameter {{ parameter3 }}"
+      - order: "say hello to {{ name }}"
     neurons:
     neurons:
-      - neuron_name:
-          parameter1: "value1"
-          parameter2: "value2"
-          args:
-          - parameter3
+      - say:
+          message:
+            - "Hello {{ name }}"
 ```
 ```
 
 
-Here, the spoken value captured by the TTS engine will be passed as an argument to the neuron in the variable named `parameter3`.
+Here, the spoken value captured by the TTS engine will be passed as an argument to the neuron in every parameters that want use it.
 
 
-Example, with the synapse declaration above, if you say "this is an order with the parameter Amy Winehouse". The neuron will receive a parameter named `parameter3` with "Amy Winehouse" as a value of this parameter.
+Example, with the synapse declaration above, if you say "say hello to Bob". The parameter parameter message is instantiated and all `{{ name }}` are replaced by "bob".
 We recommend the reading of the [signals documentation](signals.md) for a complete understanding of how arguments in a neuron work.
 We recommend the reading of the [signals documentation](signals.md) for a complete understanding of how arguments in a neuron work.
-
+> **Note:** If a parameter of a neuron is waiting for a variable from the order and this variable haven't been found in the spoken order, then the neuron is not launched.
 
 
 ## Output values
 ## Output values
 
 

+ 124 - 23
Tests/test_neuron_launcher.py

@@ -2,7 +2,7 @@ import unittest
 import mock
 import mock
 
 
 from kalliope.core.Models.Resources import Resources
 from kalliope.core.Models.Resources import Resources
-from kalliope.core.NeuronLauncher import NeuronLauncher
+from kalliope.core.NeuronLauncher import NeuronLauncher, NeuronParameterNotAvailable
 from kalliope.core.ConfigurationManager import SettingLoader
 from kalliope.core.ConfigurationManager import SettingLoader
 
 
 from kalliope.core.Models.Neuron import Neuron
 from kalliope.core.Models.Neuron import Neuron
@@ -41,38 +41,29 @@ class TestNeuronLauncher(unittest.TestCase):
         """
         """
 
 
         with mock.patch("kalliope.core.NeuronLauncher.launch_neuron") as mock_launch_neuron_method:
         with mock.patch("kalliope.core.NeuronLauncher.launch_neuron") as mock_launch_neuron_method:
-            # Assert to the neuron is launched
+            # Assert to the neuron is launched with not parameter from order
             neuron1 = Neuron(name='neurone1', parameters={'var1': 'val1'})
             neuron1 = Neuron(name='neurone1', parameters={'var1': 'val1'})
-            params = {
-                'param1':'parval1'
-            }
-            NeuronLauncher.start_neuron(neuron=neuron1,
-                                        parameters_dict=params)
+
+            NeuronLauncher.start_neuron(neuron=neuron1)
             mock_launch_neuron_method.assert_called_with(neuron1)
             mock_launch_neuron_method.assert_called_with(neuron1)
             mock_launch_neuron_method.reset_mock()
             mock_launch_neuron_method.reset_mock()
 
 
             # Assert the params are well passed to the neuron
             # Assert the params are well passed to the neuron
-            neuron2 = Neuron(name='neurone2', parameters={'var2': 'val2', 'args': ['arg1', 'arg2']})
+            neuron2 = Neuron(name='neurone2', parameters={'var2': 'val2', 'var3': "{{ var3 }}"})
             params = {
             params = {
-                'arg1':'argval1',
-                'arg2':'argval2'
+                'var3': 'value3'
             }
             }
             NeuronLauncher.start_neuron(neuron=neuron2,
             NeuronLauncher.start_neuron(neuron=neuron2,
                                         parameters_dict=params)
                                         parameters_dict=params)
-            neuron2_params = Neuron(name='neurone2',
-                                    parameters={'var2': 'val2',
-                                                'args': ['arg1', 'arg2'],
-                                                'arg1':'argval1',
-                                                'arg2':'argval2'}
-                                    )
+            neuron2_params = Neuron(name='neurone2', parameters={'var2': 'val2', 'var3': 'value3'})
+
             mock_launch_neuron_method.assert_called_with(neuron2_params)
             mock_launch_neuron_method.assert_called_with(neuron2_params)
             mock_launch_neuron_method.reset_mock()
             mock_launch_neuron_method.reset_mock()
 
 
             # Assert the Neuron is not started when missing args
             # Assert the Neuron is not started when missing args
-            neuron3 = Neuron(name='neurone3', parameters={'var3': 'val3', 'args': ['arg3', 'arg4']})
+            neuron3 = Neuron(name='neurone3', parameters={'var3': 'val3', 'var4': '{{val4}}'})
             params = {
             params = {
-                'arg1': 'argval1',
-                'arg2': 'argval2'
+                'not_exist': 'test'
             }
             }
             NeuronLauncher.start_neuron(neuron=neuron3,
             NeuronLauncher.start_neuron(neuron=neuron3,
                                         parameters_dict=params)
                                         parameters_dict=params)
@@ -80,11 +71,121 @@ class TestNeuronLauncher(unittest.TestCase):
             mock_launch_neuron_method.reset_mock()
             mock_launch_neuron_method.reset_mock()
 
 
             # Assert no neuron is launched when waiting for args and none are given
             # Assert no neuron is launched when waiting for args and none are given
-            neuron4 = Neuron(name='neurone4', parameters={'var4': 'val4', 'args': ['arg5', 'arg6']})
-            params = {}
-            NeuronLauncher.start_neuron(neuron=neuron4,
-                                        parameters_dict=params)
+            neuron4 = Neuron(name='neurone4', parameters={'var5': 'val5', 'var6': '{{val6}}'})
+
+            NeuronLauncher.start_neuron(neuron=neuron4)
             mock_launch_neuron_method.assert_not_called()
             mock_launch_neuron_method.assert_not_called()
             mock_launch_neuron_method.reset_mock()
             mock_launch_neuron_method.reset_mock()
 
 
+    def test_replace_brackets_by_loaded_parameter(self):
+        # -------------------
+        # test with string
+        # -------------------
+        # the target value to replace is present in the loaded parameter dict
+        neuron_parameters = {
+            "param1": "this is a value {{ replaced }}"
+        }
+
+        loaded_parameters = {
+            "replaced": "replaced successfully"
+        }
+
+        expected_result = {
+            "param1": "this is a value replaced successfully"
+        }
+
+        self.assertEqual(expected_result, NeuronLauncher._replace_brackets_by_loaded_parameter(neuron_parameters,
+                                                                                               loaded_parameters))
+
+        # the target value to replace is NOT present in the loaded parameter dict
+        neuron_parameters = {
+            "param1": "this is a value {{ replaced }}"
+        }
+
+        loaded_parameters = {
+            "not_exist": "replaced successfully"
+        }
+
+        with self.assertRaises(NeuronParameterNotAvailable):
+            NeuronLauncher._replace_brackets_by_loaded_parameter(neuron_parameters, loaded_parameters)
+
+        # one parameter doesn't contains bracket, the other one do
+        neuron_parameters = {
+            "param1": "this is a value {{ replaced }}",
+            "param2": "value"
+        }
+
+        loaded_parameters = {
+            "replaced": "replaced successfully"
+        }
+
+        expected_result = {
+            "param1": "this is a value replaced successfully",
+            "param2": "value"
+        }
+
+        self.assertEqual(expected_result, NeuronLauncher._replace_brackets_by_loaded_parameter(neuron_parameters,
+                                                                                               loaded_parameters))
+
+        # parameters are integer or boolean
+        neuron_parameters = {
+            "param1": 1,
+            "param2": True
+        }
+
+        loaded_parameters = {
+            "replaced": "replaced successfully"
+        }
+
+        expected_result = {
+            "param1": 1,
+            "param2": True
+        }
+
+        self.assertEqual(expected_result, NeuronLauncher._replace_brackets_by_loaded_parameter(neuron_parameters,
+                                                                                               loaded_parameters))
+
+    def test_parameters_are_available_in_loaded_parameters(self):
+        # the parameter in bracket is available in the dict
+        string_parameters = "this is a {{ parameter1 }}"
+        loaded_parameters = {"parameter1": "value"}
+
+        self.assertTrue(NeuronLauncher._neuron_parameters_are_available_in_loaded_parameters(string_parameters,
+                                                                                            loaded_parameters))
+
+        # the parameter in bracket is NOT available in the dict
+        string_parameters = "this is a {{ parameter1 }}"
+        loaded_parameters = {"parameter2": "value"}
+
+        self.assertFalse(NeuronLauncher._neuron_parameters_are_available_in_loaded_parameters(string_parameters,
+                                                                                             loaded_parameters))
+
+        # the string_parameters doesn't contains bracket in bracket is available in the dict
+        string_parameters = "this is a {{ parameter1 }}"
+        loaded_parameters = {"parameter1": "value"}
+
+        self.assertTrue(NeuronLauncher._neuron_parameters_are_available_in_loaded_parameters(string_parameters,
+                                                                                            loaded_parameters))
+
+        # the string_parameters contains 2 parameters available in the dict
+        string_parameters = "this is a {{ parameter1 }} and this is {{ parameter2 }}"
+        loaded_parameters = {"parameter1": "value", "parameter2": "other value"}
+
+        self.assertTrue(NeuronLauncher._neuron_parameters_are_available_in_loaded_parameters(string_parameters,
+                                                                                            loaded_parameters))
+
+        # the string_parameters contains 2 parameters and one of them is not available in the dict
+        string_parameters = "this is a {{ parameter1 }} and this is {{ parameter2 }}"
+        loaded_parameters = {"parameter1": "value", "parameter3": "other value"}
+
+        self.assertFalse(NeuronLauncher._neuron_parameters_are_available_in_loaded_parameters(string_parameters,
+                                                                                             loaded_parameters))
+
+
+if __name__ == '__main__':
+    unittest.main()
 
 
+    # suite = unittest.TestSuite()
+    # suite.addTest(TestNeuronLauncher("test_start_neuron"))
+    # runner = unittest.TextTestRunner()
+    # runner.run(suite)

+ 1 - 2
brain_examples/neurotransmitter.yml

@@ -77,8 +77,7 @@
           lang: "fr"
           lang: "fr"
           temp_unit: "celsius"
           temp_unit: "celsius"
           country: "FR"
           country: "FR"
-          args:
-            - location
+          location: "{{location}}"
           say_template:
           say_template:
           - "Aujourd'hui a {{ location }} le temps est {{ weather_today }} avec une température de {{ temp_today_temp }} degrés et demain le temps sera {{ weather_tomorrow }} avec une température de {{ temp_tomorrow_temp }} degrés"
           - "Aujourd'hui a {{ location }} le temps est {{ weather_today }} avec une température de {{ temp_today_temp }} degrés et demain le temps sera {{ weather_tomorrow }} avec une température de {{ temp_tomorrow_temp }} degrés"
 
 

+ 1 - 2
brain_examples/openweathermap.yml

@@ -8,8 +8,7 @@
           lang: "fr"
           lang: "fr"
           temp_unit: "celsius"
           temp_unit: "celsius"
           country: "FR"
           country: "FR"
-          args:
-            - location
+          location: "{{location}}"
           say_template:
           say_template:
           - "Aujourd'hui a {{ location }} le temps est {{ weather_today }} avec une température de {{ temp_today_temp }} degrés et demain le temps sera {{ weather_tomorrow }} avec une température de {{ temp_tomorrow_temp }} degrés"
           - "Aujourd'hui a {{ location }} le temps est {{ weather_today }} avec une température de {{ temp_today_temp }} degrés et demain le temps sera {{ weather_tomorrow }} avec une température de {{ temp_tomorrow_temp }} degrés"
 
 

+ 0 - 2
brain_examples/twitter.yml

@@ -11,5 +11,3 @@
           access_token_secret: ""
           access_token_secret: ""
           say_template:
           say_template:
           - "voici le tweet {{ tweet }}"
           - "voici le tweet {{ tweet }}"
-          args:
-            - tweet

+ 1 - 2
brain_examples/wikipedia.yml

@@ -7,6 +7,5 @@
       - wikipedia_searcher:
       - wikipedia_searcher:
           language: "fr"
           language: "fr"
           sentences: 1
           sentences: 1
-          args:
-            - query
+          query: "{{query}}"
           file_template: "templates/wikipedia_returned_value.j2"
           file_template: "templates/wikipedia_returned_value.j2"

+ 72 - 29
kalliope/core/NeuronLauncher.py

@@ -1,5 +1,7 @@
 import logging
 import logging
 
 
+import jinja2
+
 from kalliope.core.Utils.Utils import Utils
 from kalliope.core.Utils.Utils import Utils
 from kalliope.core.ConfigurationManager.SettingLoader import SettingLoader
 from kalliope.core.ConfigurationManager.SettingLoader import SettingLoader
 
 
@@ -7,6 +9,10 @@ logging.basicConfig()
 logger = logging.getLogger("kalliope")
 logger = logging.getLogger("kalliope")
 
 
 
 
+class NeuronParameterNotAvailable(Exception):
+    pass
+
+
 class NeuronLauncher:
 class NeuronLauncher:
 
 
     def __init__(self):
     def __init__(self):
@@ -41,35 +47,72 @@ class NeuronLauncher:
         :param parameters_dict: dict of parameter to load in each neuron if expecting a parameter
         :param parameters_dict: dict of parameter to load in each neuron if expecting a parameter
         :return: List of the instantiated neurons (no errors detected)
         :return: List of the instantiated neurons (no errors detected)
         """
         """
-        instantiated_neuron = None
-        problem_in_neuron_found = False
-        if isinstance(neuron.parameters, dict):
-            if "args" in neuron.parameters:
-                logger.debug("The neuron waits for parameter")
-                # check that the user added parameters to his order
-                if parameters_dict is None:
-                    # we don't raise an error and break the program but we don't run the neuron
-                    problem_in_neuron_found = True
-                    Utils.print_danger("Error: The neuron %s is waiting for argument. "
-                                       "Argument found in bracket in the given order" % neuron.name)
+        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
+
+    @classmethod
+    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" % (str(neuron_parameters),str(loaded_parameters)))
+        if isinstance(neuron_parameters, str) or isinstance(neuron_parameters, unicode):
+            # 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)
+                    return str(neuron_parameters)
                 else:
                 else:
-                    # we add wanted arguments the existing neuron parameter dict
-                    for arg in neuron.parameters["args"]:
-                        if arg in parameters_dict:
-                            logger.debug("Parameter %s added to the current parameter "
-                                         "of the neuron: %s" % (arg, neuron.name))
-                            neuron.parameters[arg] = parameters_dict[arg]
-                        else:
-                            # we don't raise an error and break the program but
-                            # we don't run the neuron
-                            problem_in_neuron_found = True
-                            Utils.print_danger("Error: Argument \"%s\" not found in the"
-                                               " order" % arg)
+                    raise NeuronParameterNotAvailable
+            return neuron_parameters
 
 
-            # if no error detected, we run the neuron
-            if not problem_in_neuron_found:
-                instantiated_neuron = cls.launch_neuron(neuron)
-            else:
-                Utils.print_danger("A problem has been found in the Synapse.")
+        if isinstance(neuron_parameters, dict):
+            returned_dict = dict()
+            for key, value in neuron_parameters.items():
+                returned_dict[key] = cls._replace_brackets_by_loaded_parameter(value, loaded_parameters)
+            return returned_dict
 
 
-        return instantiated_neuron
+        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
+
+    @staticmethod
+    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