Browse Source

Merge pull request #212 from kalliope-project/global_parameters

#203  Global parameters
Nicolas Marcq 8 years ago
parent
commit
ec0730313d

+ 11 - 0
Docs/neurons.md

@@ -47,6 +47,7 @@ Full list of [available neuron here](neuron_list.md)
 
 Neurons require some **parameters** from the synapse declaration to work. Those parameters, also called arguments, can be passed to the neuron in two way:
 - from the neuron declaration
+- from global variables
 - from the captured order
 
 From the neuron declaration:
@@ -57,6 +58,16 @@ neurons:
         parameter2: "value2"
 ```
 
+From global variables: (cf: [settings.md](settings.md))
+```yml
+  - name: "run-simple-sleep"
+    signals:
+      - order: "Wait for me "
+    neurons:
+      - sleep:
+          seconds: {{variable}}
+```
+
 From the captured order:
 ```yml
   - name: "run-neuron-with-parameter-in-order"

+ 34 - 0
Docs/settings.md

@@ -289,5 +289,39 @@ resource_directory:
   trigger: "/full/path/to/trigger"
 ```
 
+
+## Global Variables
+
+The Global Variables paths list where to load the global variables.
+Those variables can be reused in neuron parameters within double brackets.
+
+E.g 
+```yml
+var_files:
+  - variables.yml
+  - variables2.yml
+```
+/!\ If a variable is defined in different files, the last file defines the value.
+
+In the files the variables are defined by key/value:
+```yml
+variable: 60
+baseURL: "http://blabla.com/"
+password: "secret"
+```
+
+And use variables in your neurons:
+/!\ Because YAML format does no allow double braces not surrounded by quotes: you must use the variable between double quotes. 
+```yml
+  - name: "run-simple-sleep"
+    signals:
+      - order: "Wait for me "
+    neurons:
+      - uri:
+          url: "{{baseURL}}get/1"        
+          user: "admin"
+          password: "{{password}}"
+```
+
 ## Next: configure the brain of Kalliope
 Now your settings are ok, you can start creating the [brain](brain.md) of your assistant.

+ 7 - 0
Tests/settings/settings_test.yml

@@ -110,3 +110,10 @@ resource_directory:
   stt: "/tmp/kalliope/tests/kalliope_resources_dir/stt"
   tts: "/tmp/kalliope/tests/kalliope_resources_dir/tts"
   trigger: "/tmp/kalliope/tests/kalliope_resources_dir/trigger"
+
+# ---------------------------
+# Global files variables
+# /!\ If a variable is defined in different files, the last file defines the value.
+# ---------------------------
+var_files:
+  - "../Tests/settings/variables.yml"

+ 3 - 0
Tests/settings/variables.yml

@@ -0,0 +1,3 @@
+test: kalliope
+test_number: 60
+author: Lamonf

+ 124 - 55
Tests/test_order_analyser.py

@@ -160,51 +160,6 @@ class TestOrderAnalyser(unittest.TestCase):
             mock_start_neuron_method.assert_not_called()
             mock_start_neuron_method.reset_mock()
 
-
-    def test_is_containing_bracket(self):
-        #  Success
-        order_to_test = "This test contains {{ bracket }}"
-        self.assertTrue(OrderAnalyser._is_containing_bracket(order_to_test),
-                        "Fail returning True when order contains spaced brackets")
-
-        order_to_test = "This test contains {{bracket }}"
-        self.assertTrue(OrderAnalyser._is_containing_bracket(order_to_test),
-                        "Fail returning True when order contains right spaced bracket")
-
-        order_to_test = "This test contains {{ bracket}}"
-        self.assertTrue(OrderAnalyser._is_containing_bracket(order_to_test),
-                        "Fail returning True when order contains left spaced bracket")
-
-        order_to_test = "This test contains {{bracket}}"
-        self.assertTrue(OrderAnalyser._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(OrderAnalyser._is_containing_bracket(order_to_test),
-                         "Fail returning False when order has no brackets")
-
-        #  Behaviour
-        order_to_test = ""
-        self.assertFalse(OrderAnalyser._is_containing_bracket(order_to_test),
-                         "Fail returning False when no order")
-
-    def test_get_next_value_list(self):
-        # Success
-        list_to_test = {1, 2, 3}
-        self.assertEqual(OrderAnalyser._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(OrderAnalyser._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(OrderAnalyser._get_next_value_list(list_to_test), None,
-                         "Fail to ensure the empty list return None value")
-
     def test_spelt_order_match_brain_order_via_table(self):
         order_to_test = "this is the order"
         sentence_to_test = "this is the order"
@@ -224,7 +179,6 @@ class TestOrderAnalyser(unittest.TestCase):
                         "Fail matching Upper/lower cases")
 
     def test_format_sentences_to_analyse(self):
-
         # First capital in sentence
         order_to_test = "this is the order"
         sentence_to_test = "This is the order"
@@ -262,7 +216,6 @@ class TestOrderAnalyser(unittest.TestCase):
                          "Fails formatting the sentences with random in both order and sentence")
 
     def test_get_split_order_without_bracket(self):
-
         # Success
         order_to_test = "this is the order"
         expected_result = ["this", "is", "the", "order"]
@@ -565,9 +518,9 @@ class TestOrderAnalyser(unittest.TestCase):
         """
         Test to find the good synapse to run
         Scenarii:
-            - Find the synapse
-            - No synpase found, no default synapse
-            - No synapse found, run the default synapse
+            - 1/ Find the synapse
+            - 2/ No synpase found, no default synapse
+            - 3/ No synapse found, run the default synapse
         """
         # Init
         neuron1 = Neuron(name='neurone1', parameters={'var1': 'val1'})
@@ -589,7 +542,7 @@ class TestOrderAnalyser(unittest.TestCase):
 
         br = Brain(synapses=all_synapse_list)
         st = Settings()
-        # Find synapse
+        # 1/ Find synapse
         order = "this is the sentence"
         expected_result = synapse1
         oa_tuple_list = OrderAnalyser._find_synapse_to_run(brain=br,settings=st, order=order)
@@ -599,16 +552,17 @@ class TestOrderAnalyser(unittest.TestCase):
 
         expected_result = signal1.sentence
         self.assertEquals(oa_tuple_list[0].order,
-                        expected_result,
-                        "Fail to run the proper synapse matching the order")
-        # No Default synapse
+                          expected_result,
+                          "Fail to run the proper synapse matching the order")
+
+        # 2/ No Default synapse
         order = "No default synapse"
         expected_result = []
         self.assertEquals(OrderAnalyser._find_synapse_to_run(brain=br,settings=st, order=order),
                           expected_result,
                           "Fail to run no synapse, when no default is defined")
 
-        # Default synapse
+        # 3/ Default synapse
         st = Settings(default_synapse="Synapse2")
         order = "default synapse"
         expected_result = synapse2
@@ -617,6 +571,121 @@ class TestOrderAnalyser(unittest.TestCase):
                           expected_result,
                           "Fail to run the default synapse")
 
+    def test_replace_global_variables(self):
+        """
+        Testing the _replace_global_variables function from the OrderAnalyser.
+        Scenarii:
+            - 1/ only one global variable
+            - 2/ global variable with string after
+            - 3/ global variable with int after
+            - 4/ multiple global variables
+            - 5/ parameter value is a list
+
+        """
+
+        # 1/ only one global variable
+        neuron1 = Neuron(name='neuron1', parameters={'var1': '{{hello}}'})
+        variables = {
+            "hello": "test",
+            "hello2": "test2",
+        }
+        st = Settings(variables=variables)
+
+        expected_neuron_result = Neuron(name='neuron1', parameters={'var1': 'test'})
+
+        # assign global variable to neuron1
+        OrderAnalyser._replace_global_variables(neuron=neuron1,
+                                                settings=st)
+        self.assertEquals(neuron1,
+                          expected_neuron_result,
+                          "Fail to assign a single global variable to neuron")
+
+        # 2/ global variable with string after
+        neuron1 = Neuron(name='neuron1', parameters={'var1': '{{hello}} Sispheor'})
+        variables = {
+            "hello": "test",
+            "hello2": "test2",
+        }
+        st = Settings(variables=variables)
+
+        expected_neuron_result = Neuron(name='neuron1', parameters={'var1': 'test Sispheor'})
+
+        # assign global variable to neuron1
+        OrderAnalyser._replace_global_variables(neuron=neuron1,
+                                                settings=st)
+        self.assertEquals(neuron1,
+                          expected_neuron_result,
+                          "Fail to assign a global variable with string after to neuron")
+
+        # 3/ global variable with int after
+        neuron1 = Neuron(name='neuron1', parameters={'var1': '{{hello}}0'})
+        variables = {
+            "hello": 60,
+            "hello2": "test2",
+        }
+        st = Settings(variables=variables)
+
+        expected_neuron_result = Neuron(name='neuron1', parameters={'var1': '600'})
+
+        # assign global variable to neuron1
+        OrderAnalyser._replace_global_variables(neuron=neuron1,
+                                                settings=st)
+        self.assertEquals(neuron1,
+                          expected_neuron_result,
+                          "Fail to assign global variable with int after to neuron")
+
+        # 4/ multiple global variables
+        neuron1 = Neuron(name='neuron1', parameters={'var1': '{{hello}} {{me}}'})
+        variables = {
+            "hello": "hello",
+            "me": "LaMonf"
+        }
+        st = Settings(variables=variables)
+
+        expected_neuron_result = Neuron(name='neuron1', parameters={'var1': 'hello LaMonf'})
+
+        # assign global variable to neuron1
+        OrderAnalyser._replace_global_variables(neuron=neuron1,
+                                                settings=st)
+        self.assertEquals(neuron1,
+                          expected_neuron_result,
+                          "Fail to assign multiple global variables to neuron")
+
+        # 5/ parameter value is a list
+        neuron1 = Neuron(name='neuron1', parameters={'var1': '[hello {{name}}, bonjour {{name}}]'})
+        variables = {
+            "name": "LaMonf",
+            "hello2": "test2",
+        }
+        st = Settings(variables=variables)
+
+        expected_neuron_result = Neuron(name='neuron1', parameters={'var1': '[hello LaMonf, bonjour LaMonf]'})
+
+        # assign global variable to neuron1
+        OrderAnalyser._replace_global_variables(neuron=neuron1,
+                                                settings=st)
+        self.assertEquals(neuron1,
+                          expected_neuron_result,
+                          "Fail to assign a single global when parameter value is a list to neuron")
+
+    def test_get_global_variable(self):
+        """
+        Test the get_global_variable of the OrderAnalyser Class
+        """
+        sentence = "i am {{name2}}"
+        variables = {
+            "name": "LaMonf",
+            "name2": "kalliope",
+        }
+        st = Settings(variables=variables)
+
+        expected_result = "i am kalliope"
+
+        self.assertEquals(OrderAnalyser._get_global_variable(sentence=sentence,
+                                                             settings=st),
+                          expected_result,
+                          "Fail to get the global variable from the sentence")
+
 
 if __name__ == '__main__':
     unittest.main()

+ 17 - 3
Tests/test_settings_loader.py

@@ -51,10 +51,10 @@ class TestSettingLoader(unittest.TestCase):
             'text_to_speech': [
                 {'pico2wave': {'cache': True, 'language': 'fr-FR'}},
                 {'voxygen': {'voice': 'Agnes', 'cache': True}}
-            ]
+            ],
+            'var_files': ["../Tests/settings/variables.yml"]
         }
 
-
         # Init the folders, otherwise it raises an exceptions
         os.makedirs("/tmp/kalliope/tests/kalliope_resources_dir/neurons")
         os.makedirs("/tmp/kalliope/tests/kalliope_resources_dir/stt")
@@ -105,8 +105,12 @@ class TestSettingLoader(unittest.TestCase):
                               stt_folder="/tmp/kalliope/tests/kalliope_resources_dir/stt",
                               tts_folder="/tmp/kalliope/tests/kalliope_resources_dir/tts",
                               trigger_folder="/tmp/kalliope/tests/kalliope_resources_dir/trigger")
-
         settings_object.resources = resources
+        settings_object.variables = {
+            "author": "Lamonf",
+            "test_number": 60,
+            "test": "kalliope"
+        }
         settings_object.machine = platform.machine()
 
         sl = SettingLoader(file_path=self.settings_file_to_test)
@@ -189,6 +193,16 @@ class TestSettingLoader(unittest.TestCase):
         sl = SettingLoader(file_path=self.settings_file_to_test)
         self.assertEquals(expected_resource, sl._get_resources(self.settings_dict))
 
+    def test_get_variables(self):
+        expected_result = {
+            "author": "Lamonf",
+            "test_number": 60,
+            "test": "kalliope"
+        }
+        sl = SettingLoader(file_path=self.settings_file_to_test)
+        self.assertEqual(expected_result,
+                         sl._get_variables(self.settings_dict))
+
 
 if __name__ == '__main__':
     unittest.main()

+ 77 - 0
Tests/test_utils.py

@@ -138,3 +138,80 @@ class TestUtils(unittest.TestCase):
                                    Say),
                         "Fail instantiate a class")
 
+
+    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")
+
+    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 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 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")

+ 24 - 0
kalliope/core/ConfigurationManager/SettingLoader.py

@@ -112,6 +112,7 @@ class SettingLoader(object):
         cache_path = self._get_cache_path(settings)
         default_synapse = self._get_default_synapse(settings)
         resources = self._get_resources(settings)
+        variables = self._get_variables(settings)
 
         # Load the setting singleton with the parameters
         setting_object.default_tts_name = default_tts_name
@@ -129,6 +130,7 @@ class SettingLoader(object):
         setting_object.cache_path = cache_path
         setting_object.default_synapse = default_synapse
         setting_object.resources = resources
+        setting_object.variables = variables
 
         return setting_object
 
@@ -641,5 +643,27 @@ class SettingLoader(object):
 
         return on_ready_sounds
 
+    @staticmethod
+    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()
 
 

+ 2 - 0
kalliope/core/Models/Settings.py

@@ -24,6 +24,7 @@ class Settings(object):
                  cache_path=None,
                  default_synapse=None,
                  resources=None,
+                 variables= None, # dict()
                  machine=None,
                  kalliope_version=None):
 
@@ -42,6 +43,7 @@ class Settings(object):
         self.cache_path = cache_path
         self.default_synapse = default_synapse
         self.resources = resources
+        self.variables = variables
         self.machine = platform.machine()   # can be x86_64 or armv7l
         self.kalliope_version = current_kalliope_version
 

+ 52 - 33
kalliope/core/OrderAnalyser.py

@@ -65,7 +65,8 @@ class OrderAnalyser:
 
             # Start a neuron list with params
             self._start_list_neurons(list_neurons=tuple.synapse.neurons,
-                                     params=params)
+                                     params=params,
+                                     settings=self.settings)
             synapses_launched.append(tuple.synapse)
 
         # return the list of launched synapse
@@ -139,17 +140,58 @@ class OrderAnalyser:
         :return: the dict key/value
         """
         params = dict()
-        if cls._is_containing_bracket(string_order):
+        if Utils.is_containing_bracket(string_order):
             params = cls._associate_order_params_to_values(order_to_check, string_order)
             logger.debug("Parameters for order: %s" % params)
         return params
 
     @classmethod
-    def _start_list_neurons(cls, list_neurons, params):
+    def _start_list_neurons(cls, list_neurons, params, settings):
         # start neurons
         for neuron in list_neurons:
+            cls._replace_global_variables(neuron, settings)
             cls._start_neuron(neuron, params)
 
+    @classmethod
+    def _replace_global_variables(cls, neuron, settings):
+        """
+        Replace all the parameters with variables with the variable value.
+        :param neuron: the neuron
+        :param settings: the settings
+        """
+        for param in neuron.parameters:
+            if isinstance(neuron.parameters[param], list):
+                list_param_value = list()
+                for sentence in neuron.parameters[param]:
+                    sentence_with_global_variables = cls._get_global_variable(sentence=sentence,
+                                                                              settings=settings)
+                    list_param_value.append(sentence_with_global_variables)
+                neuron.parameters[param] = list_param_value
+
+            else:
+                if Utils.is_containing_bracket(neuron.parameters[param]):
+                    sentence_with_global_variables = cls._get_global_variable(sentence=neuron.parameters[param],
+                                                                              settings=settings)
+                    neuron.parameters[param] = sentence_with_global_variables
+
+    @staticmethod
+    def _get_global_variable(sentence, settings):
+        """
+        Get the global variable from the sentence with brackets
+        :param sentence: the sentence to check
+        :return: the global variable
+        """
+        sentence_no_spaces = Utils.remove_spaces_in_brackets(sentence=sentence)
+        list_of_bracket_params = Utils.find_all_matching_brackets(sentence=sentence_no_spaces)
+        for param_with_bracket in list_of_bracket_params:
+            param_no_brackets = param_with_bracket.replace("{{", "").replace("}}", "")
+            if param_no_brackets in settings.variables:
+                logger.debug("Replacing variable %s with  %s" % (param_with_bracket,
+                                                                 settings.variables[param_no_brackets]))
+                sentence_no_spaces = sentence_no_spaces.replace(param_with_bracket,
+                                                                str(settings.variables[param_no_brackets]))
+        return sentence_no_spaces
+
     @staticmethod
     def _start_neuron(neuron, params):
         """
@@ -190,8 +232,8 @@ class OrderAnalyser:
         else:
             Utils.print_danger("A problem has been found in the Synapse.")
 
-    @classmethod
-    def _associate_order_params_to_values(cls, order, order_to_check):
+    @staticmethod
+    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
@@ -203,9 +245,7 @@ class OrderAnalyser:
         logger.debug("[OrderAnalyser._associate_order_params_to_values] user order: %s, "
                      "order to check: %s" % (order, order_to_check))
 
-        pattern = '\s+(?=[^\{\{\}\}]*\}\})'
-        # Remove white spaces (if any) between the variable and the double brace then split
-        list_word_in_order = re.sub(pattern, '', order_to_check).split()
+        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
@@ -218,10 +258,10 @@ class OrderAnalyser:
         # make dict var:value
         dict_var = dict()
         for idx, ow in enumerate(list_word_in_order):
-            if cls._is_containing_bracket(ow):
+            if Utils.is_containing_bracket(ow):
                 # remove bracket and grab the next value / stop value
                 var_name = ow.replace("{{", "").replace("}}", "")
-                stop_value = cls._get_next_value_list(list_word_in_order[idx:])
+                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
@@ -236,26 +276,6 @@ class OrderAnalyser:
             truncate_list_word_said = truncate_list_word_said[1:]
         return dict_var
 
-    @staticmethod
-    def _is_containing_bracket(sentence):
-        """
-        Return True if the text in <sentence> contains brackets
-        :param sentence:
-        :return:
-        """
-        # print "sentence to test %s" % sentence
-        pattern = r"{{|}}"
-        # prog = re.compile(pattern)
-        check_bool = re.search(pattern, sentence)
-        if check_bool is not None:
-            return True
-        return False
-
-    @staticmethod
-    def _get_next_value_list(list_to_check):
-        ite = list_to_check.__iter__()
-        next(ite, None)
-        return next(ite, None)
 
     @classmethod
     def spelt_order_match_brain_order_via_table(cls, order_to_analyse, user_said):
@@ -305,9 +325,8 @@ class OrderAnalyser:
         :param order: sentence to split
         :return: list of string without bracket
         """
-        pattern = r"((?:{{\s*)[\w\.]+(?:\s*}}))"
-        # find everything like {{ word }}
-        matches = re.findall(pattern, order)
+
+        matches = Utils.find_all_matching_brackets(order)
         for match in matches:
             order = order.replace(match, "")
         # then split

+ 56 - 2
kalliope/core/Utils/Utils.py

@@ -2,8 +2,7 @@ import logging
 import os
 import inspect
 import imp
-
-import sys
+import re
 
 logging.basicConfig()
 logger = logging.getLogger("kalliope")
@@ -217,3 +216,58 @@ class Utils(object):
                 return valid[choice]
             else:
                 Utils.print_warning("Please respond with 'yes' or 'no' or 'y' or 'n').\n")
+
+    ##################
+    #
+    # Brackets management
+    #
+    #########
+    @staticmethod
+    def is_containing_bracket(sentence):
+        """
+        Return True if the text in <sentence> contains brackets
+        :param sentence:
+        :return:
+        """
+        # print "sentence to test %s" % sentence
+        pattern = r"{{|}}"
+        # prog = re.compile(pattern)
+        check_bool = re.search(pattern, str(sentence))
+        if check_bool is not None:
+            return True
+        return False
+
+    @staticmethod
+    def find_all_matching_brackets(sentence):
+        """
+        Find all the bracket matches from a given sentence
+        :param sentence: the sentence to check
+        :return: the list with all the matches
+        """
+
+        pattern = r"((?:{{\s*)[\w\.]+(?:\s*}}))"
+        # find everything like {{ word }}
+        return re.findall(pattern, str(sentence))
+
+    @staticmethod
+    def remove_spaces_in_brackets(sentence):
+        """
+        If has brackets it removes spaces in brackets
+        :param sentence: the sentence to work on
+        :return: the sentence without any spaces in brackets
+        """
+
+        pattern = '\s+(?=[^\{\{\}\}]*\}\})'
+        # Remove white spaces (if any) between the variable and the double brace then split
+        return re.sub(pattern, '', str(sentence))
+
+    ##################
+    #
+    # Lists management
+    #
+    #########
+    @staticmethod
+    def get_next_value_list(list_to_check):
+        ite = list_to_check.__iter__()
+        next(ite, None)
+        return next(ite, None)

+ 4 - 0
kalliope/neurons/sleep/sleep.py

@@ -10,6 +10,10 @@ class Sleep(NeuronModule):
 
         # check parameters
         if self._is_parameters_ok():
+
+            if isinstance(self.seconds, str):
+                self.seconds = float(self.seconds)
+
             time.sleep(self.seconds)
 
     def _is_parameters_ok(self):

+ 2 - 2
kalliope/neurons/sleep/tests/test_sleep.py

@@ -7,7 +7,7 @@ from kalliope.neurons.sleep.sleep import Sleep
 class TestSleep(unittest.TestCase):
 
     def setUp(self):
-        self.second="second"
+        self.seconds = 10
         self.random="random"
 
     def testParameters(self):
@@ -19,7 +19,7 @@ class TestSleep(unittest.TestCase):
         parameters = dict()
         run_test(parameters)
 
-        # missing second
+        # missing seconds
         parameters = {
             "random": self.random
         }

+ 9 - 0
kalliope/settings.yml

@@ -153,3 +153,12 @@ default_synapse: "default-synapse"
 #  stt: "resources/stt"
 #  tts: "resources/tts"
 #  trigger: "resources/trigger"
+
+
+# ---------------------------
+# Global files variables
+# /!\ If a variable is defined in different files, the last file defines the value.
+# ---------------------------
+#var_files:
+#  - variables.yml
+#  - variables2.yml