Browse Source

Merge pull request #256 from kalliope-project/draft_FIFO

use a LIFO to run synapse
Monf 8 years ago
parent
commit
d75e5ce79a
43 changed files with 1850 additions and 676 deletions
  1. 79 46
      Docs/rest_api.md
  2. 63 0
      Tests/brains/lifo_buffer_test_brain.yml
  3. 0 140
      Tests/test_launchers.py
  4. 358 0
      Tests/test_lifo_buffer.py
  5. 484 0
      Tests/test_models.py
  6. 90 0
      Tests/test_neuron_launcher.py
  7. 0 40
      Tests/test_neuron_model.py
  8. 11 56
      Tests/test_neuron_module.py
  9. 8 1
      Tests/test_order_analyser.py
  10. 68 80
      Tests/test_rest_api.py
  11. 81 36
      Tests/test_synapse_launcher.py
  12. 31 0
      Tests/test_trigger_launcher.py
  13. 5 4
      kalliope/__init__.py
  14. 0 8
      kalliope/core/ConfigurationManager/BrainLoader.py
  15. 0 4
      kalliope/core/ConfigurationManager/SettingLoader.py
  16. 1 1
      kalliope/core/EventManager.py
  17. 193 0
      kalliope/core/LIFOBuffer.py
  18. 4 1
      kalliope/core/MainController.py
  19. 23 0
      kalliope/core/Models/APIResponse.py
  20. 1 5
      kalliope/core/Models/Dna.py
  21. 1 3
      kalliope/core/Models/Event.py
  22. 55 0
      kalliope/core/Models/MatchedSynapse.py
  23. 8 4
      kalliope/core/Models/Neuron.py
  24. 1 1
      kalliope/core/Models/Order.py
  25. 1 5
      kalliope/core/Models/Resources.py
  26. 24 9
      kalliope/core/Models/RestAPI.py
  27. 33 3
      kalliope/core/Models/Settings.py
  28. 7 1
      kalliope/core/Models/Stt.py
  29. 1 8
      kalliope/core/Models/Synapse.py
  30. 7 1
      kalliope/core/Models/Trigger.py
  31. 7 1
      kalliope/core/Models/Tts.py
  32. 28 32
      kalliope/core/NeuronLauncher.py
  33. 40 36
      kalliope/core/NeuronModule.py
  34. 11 1
      kalliope/core/OrderAnalyser.py
  35. 32 27
      kalliope/core/RestAPI/FlaskAPI.py
  36. 1 1
      kalliope/core/ShellGui.py
  37. 50 56
      kalliope/core/SynapseLauncher.py
  38. 0 27
      kalliope/core/TTS/TTSLauncher.py
  39. 1 0
      kalliope/core/TTS/__init__.py
  40. 2 1
      kalliope/core/Utils/Utils.py
  41. 3 3
      kalliope/core/__init__.py
  42. 14 5
      kalliope/neurons/neurotransmitter/neurotransmitter.py
  43. 23 29
      kalliope/neurons/neurotransmitter/tests/test_neurotransmitter.py

+ 79 - 46
Docs/rest_api.md

@@ -130,23 +130,20 @@ curl -i --user admin:secret -X POST  http://localhost:5000/synapses/start/id/say
 Output example:
 ```JSON
 {
-  "synapses": {
-    "name": "say-hello",
-    "neurons": [
-      {
-        "say": {
-          "message": [
-            "Bonjour monsieur"
-          ]
+  "matched_synapses": [
+    {
+      "matched_order": null,
+      "neuron_module_list": [
+        {
+          "generated_message": "Bonjour monsieur",
+          "neuron_name": "Say"
         }
-      }
-    ],
-    "signals": [
-      {
-        "order": "bonjour"
-      }
-    ]
-  }
+      ],
+      "synapse_name": "say-hello-fr"
+    }
+  ],
+  "status": "complete",
+  "user_order": null
 }
 ```
 
@@ -174,31 +171,49 @@ curl -i --user admin:secret -H "Content-Type: application/json" -X POST --data @
 Output example if the order have matched and so launched synapses:
 ```JSON
 {
-  "synapses": [
+  "matched_synapses": [
     {
-      "name": "Say-hello", 
-      "neurons": [
+      "matched_order": "Bonjour",
+      "neuron_module_list": [
         {
-          "name": "say", 
-          "parameters": "{'message': ['Hello sir']}"
+          "generated_message": "Bonjour monsieur",
+          "neuron_name": "Say"
         }
-      ], 
-      "signals": [
+      ],
+      "synapse_name": "say-hello-fr"
+    }
+  ],
+  "status": "complete",
+  "user_order": "bonjour"
+}
+```
+
+If the order haven't match any synapses it will try to run the default synapse if it exists in your settings:
+```JSON
+{
+  "matched_synapses": [
+    {
+      "matched_order": null,
+      "neuron_module_list": [
         {
-          "order": "hello"
+          "generated_message": "Je n'ai pas compris votre ordre",
+          "neuron_name": "Say"
         }
-      ]
+      ],
+      "synapse_name": "default-synapse"
     }
-  ]
+  ],
+  "status": "complete",
+  "user_order": "not existing order"
 }
 ```
 
-If the order haven't match any synapses:
-```JSON
+Or return an empty list of matched synapse
+```
 {
-  "error": {
-    "error": "The given order doesn't match any synapses"
-  }
+  "matched_synapses": [],
+  "status": null,
+  "user_order": "not existing order"
 }
 ```
 
@@ -217,30 +232,48 @@ curl -i --user admin:secret -X POST  http://localhost:5000/synapses/start/audio
 Output example if the order inside the audio have matched and so launched synapses:
 ```JSON
 {
-  "synapses": [
+  "matched_synapses": [
     {
-      "name": "Say-hello", 
-      "neurons": [
+      "matched_order": "Bonjour",
+      "neuron_module_list": [
         {
-          "name": "say", 
-          "parameters": "{'message': ['Hello sir']}"
+          "generated_message": "Bonjour monsieur",
+          "neuron_name": "Say"
         }
-      ], 
-      "signals": [
+      ],
+      "synapse_name": "say-hello-fr"
+    }
+  ],
+  "status": "complete",
+  "user_order": "bonjour"
+}
+```
+
+If the order haven't match any synapses it will try to run the default synapse if it exists in your settings:
+```JSON
+{
+  "matched_synapses": [
+    {
+      "matched_order": null,
+      "neuron_module_list": [
         {
-          "order": "hello"
+          "generated_message": "Je n'ai pas compris votre ordre",
+          "neuron_name": "Say"
         }
-      ]
+      ],
+      "synapse_name": "default-synapse"
     }
-  ]
+  ],
+  "status": "complete",
+  "user_order": "not existing order"
 }
 ```
 
-If the order haven't match any synapses:
-```JSON
+Or return an empty list of matched synapse
+```
 {
-  "error": {
-    "error": "The given order doesn't match any synapses"
-  }
+  "matched_synapses": [],
+  "status": null,
+  "user_order": "not existing order"
 }
 ```

+ 63 - 0
Tests/brains/lifo_buffer_test_brain.yml

@@ -0,0 +1,63 @@
+- name: "synapse1"
+  signals:
+    - order: "enter in synapse 1"
+  neurons:
+    - say:
+        message: "question in synapse 1"
+    - neurotransmitter:
+        from_answer_link:
+          - synapse: "synapse2"
+            answers:
+              - "answer synapse1"
+          - synapse: "synapse3"
+            answers:
+              - "other answer synapse1"
+        default: "synapse4"
+    - say:
+        message: "last neuron in synapse 1"
+
+- name: "synapse2"
+  signals:
+    - order: "synapse2"
+  neurons:
+    - say:
+        message: "enter synapse 2"
+    - neurotransmitter:
+        from_answer_link:
+          - synapse: "synapse5"
+            answers:
+              - "synapse5"
+        default: "synapse4"
+
+- name: "synapse3"
+  signals:
+    - order: "synapse3"
+  neurons:
+    - say:
+        message: "I enter in synapse 3"
+
+- name: "synapse4"
+  signals:
+    - order: "synapse4"
+  neurons:
+    - say:
+        message: "not understood"
+
+
+- name: "synapse5"
+  signals:
+    - order: "synapse5"
+  neurons:
+    - say:
+        message: "execution of synapse 5"
+
+- name: "synapse6"
+  signals:
+    - order: "synapse6"
+  neurons:
+    - neurotransmitter:
+        from_answer_link:
+          - synapse: "synapse5"
+            answers:
+              - "synapse 6 answer"
+        default: "synapse4"

+ 0 - 140
Tests/test_launchers.py

@@ -1,140 +0,0 @@
-import unittest
-import mock
-
-from kalliope.core.Models.Resources import Resources
-from kalliope.core.NeuronLauncher import NeuronLauncher
-from kalliope.core.SynapseLauncher import SynapseLauncher, SynapseNameNotFound
-from kalliope.core.TriggerLauncher import TriggerLauncher
-from kalliope.core.ConfigurationManager import SettingLoader
-from kalliope.core.Models.Settings import Settings
-
-from kalliope.core.Models.Trigger import Trigger
-from kalliope.core.Models.Neuron import Neuron
-from kalliope.core.Models.Order import Order
-from kalliope.core.Models.Brain import Brain
-from kalliope.core.Models.Synapse import Synapse
-
-
-class TestLaunchers(unittest.TestCase):
-    """
-    Class to test Launchers Classes (TriggerLauncher, SynapseLauncher, NeuronLauncher) and methods
-    """
-
-    def setUp(self):
-        pass
-
-    ####
-    # Trigger Launcher
-    def test_get_trigger(self):
-        """
-        Test the Trigger Launcher trying to run the trigger
-        """
-        trigger = Trigger("Trigger", {})
-        with mock.patch("kalliope.core.Utils.get_dynamic_class_instantiation") as mock_get_class_instantiation:
-            TriggerLauncher.get_trigger(trigger=trigger,
-                                        callback=None)
-
-            mock_get_class_instantiation.assert_called_once_with(package_name="trigger",
-                                                                 module_name=trigger.name,
-                                                                 parameters=trigger.parameters)
-            mock_get_class_instantiation.reset_mock()
-
-    ####
-    # Synapse Launcher
-    def test_start_synapse(self):
-        """
-        Test the Synapse launcher trying to start synapse
-        """
-        # Init
-        neuron1 = Neuron(name='neurone1', parameters={'var1': 'val1'})
-        neuron2 = Neuron(name='neurone2', parameters={'var2': 'val2'})
-        neuron3 = Neuron(name='neurone3', parameters={'var3': 'val3'})
-        neuron4 = Neuron(name='neurone4', parameters={'var4': 'val4'})
-
-        signal1 = Order(sentence="this is the sentence")
-        signal2 = Order(sentence="this is the second sentence")
-        signal3 = Order(sentence="that is part of the third sentence")
-
-        synapse1 = Synapse(name="Synapse1", neurons=[neuron1, neuron2], signals=[signal1])
-        synapse2 = Synapse(name="Synapse2", neurons=[neuron3, neuron4], signals=[signal2])
-        synapse3 = Synapse(name="Synapse3", neurons=[neuron2, neuron4], signals=[signal3])
-
-        all_synapse_list = [synapse1,
-                            synapse2,
-                            synapse3]
-
-        br = Brain(synapses=all_synapse_list)
-
-        sl = SettingLoader()
-        r = Resources(neuron_folder="/var/tmp/test/resources")
-        sl.settings.resources = r
-        with mock.patch("kalliope.core.Utils.get_dynamic_class_instantiation") as mock_get_class_instantiation:
-            # Success
-            SynapseLauncher.start_synapse("Synapse1", brain=br)
-
-            calls = [mock.call(package_name="neurons",
-                               module_name=neuron1.name,
-                               parameters=neuron1.parameters,
-                               resources_dir='/var/tmp/test/resources'),
-                     mock.call(package_name="neurons",
-                               module_name=neuron2.name,
-                               parameters=neuron2.parameters,
-                               resources_dir='/var/tmp/test/resources')]
-            mock_get_class_instantiation.assert_has_calls(calls=calls)
-            mock_get_class_instantiation.reset_mock()
-
-            # Fail
-            with self.assertRaises(SynapseNameNotFound):
-                SynapseLauncher.start_synapse("Synapse4", brain=br)
-
-    def test_run_synapse(self):
-        """
-        Test to run a Synapse
-        """
-        neuron1 = Neuron(name='neurone1', parameters={'var1': 'val1'})
-        neuron2 = Neuron(name='neurone2', parameters={'var2': 'val2'})
-        signal1 = Order(sentence="this is the sentence")
-        synapse1 = Synapse(name="Synapse1", neurons=[neuron1, neuron2], signals=[signal1])
-        synapse_empty = Synapse(name="Synapse_empty", neurons=[], signals=[signal1])
-        sl = SettingLoader()
-        resources = Resources(neuron_folder='/var/tmp/test/resources')
-        sl.settings.resources = resources
-        with mock.patch("kalliope.core.Utils.get_dynamic_class_instantiation") as mock_get_class_instantiation:
-            SynapseLauncher._run_synapse(synapse=synapse1)
-
-            calls = [mock.call(package_name="neurons",
-                               module_name=neuron1.name,
-                               parameters=neuron1.parameters,
-                               resources_dir="/var/tmp/test/resources"),
-                     mock.call(package_name="neurons",
-                               module_name=neuron2.name,
-                               parameters=neuron2.parameters,
-                               resources_dir="/var/tmp/test/resources")]
-            mock_get_class_instantiation.assert_has_calls(calls=calls)
-            mock_get_class_instantiation.reset_mock()
-
-            # Do not any Neurons
-            SynapseLauncher._run_synapse(synapse=synapse_empty)
-            mock_get_class_instantiation.assert_not_called()
-            mock_get_class_instantiation.reset_mock()
-
-    ####
-    # Neurons Launcher
-    def test_start_neuron(self):
-        """
-        Test the Neuron Launcher trying to start a Neuron
-        """
-        neuron = Neuron(name='neurone1', parameters={'var1': 'val1'})
-        sl = SettingLoader()
-        resources = Resources(neuron_folder='/var/tmp/test/resources')
-        sl.settings.resources = resources
-        with mock.patch("kalliope.core.Utils.get_dynamic_class_instantiation") as mock_get_class_instantiation:
-            NeuronLauncher.start_neuron(neuron=neuron)
-
-            mock_get_class_instantiation.assert_called_once_with(package_name="neurons",
-                                                                 module_name=neuron.name,
-                                                                 parameters=neuron.parameters,
-                                                                 resources_dir=sl.settings.resources.neuron_folder)
-            mock_get_class_instantiation.reset_mock()
-
-

+ 358 - 0
Tests/test_lifo_buffer.py

@@ -0,0 +1,358 @@
+import os
+import unittest
+
+import mock
+
+from kalliope.core import LIFOBuffer
+from kalliope.core.ConfigurationManager import BrainLoader
+from kalliope.core.LIFOBuffer import Serialize, SynapseListAddedToLIFO
+
+from kalliope.core.Models import Singleton
+from kalliope.core.Models.MatchedSynapse import MatchedSynapse
+
+
+class TestLIFOBuffer(unittest.TestCase):
+
+    def setUp(self):
+        # be sure the brain haven't been instantiated before
+        Singleton._instances = dict()
+
+        if "/Tests" in os.getcwd():
+            self.brain_to_test = os.getcwd() + os.sep + "brains/lifo_buffer_test_brain.yml"
+        else:
+            self.brain_to_test = os.getcwd() + os.sep + "Tests/brains/lifo_buffer_test_brain.yml"
+
+        BrainLoader(file_path=self.brain_to_test)
+        # create a new lifo buffer
+        self.lifo_buffer = LIFOBuffer()
+        self.lifo_buffer.clean()
+
+    def test_execute(self):
+        """
+        In this test the brain contains a neurotransmitter
+        """
+        # --------------------------------------
+        # Test 1. The user answers correctly to all neurotransmitter
+        # --------------------------------------
+
+        # we suppose that the first synapse has matched the first synapse
+        synapse = BrainLoader().brain.get_synapse_by_name("synapse1")
+        order = "enter in synapse 1"
+        matched_synapse = MatchedSynapse(matched_synapse=synapse,
+                                         user_order=order,
+                                         matched_order=order)
+        list_matched_synapse = list()
+        list_matched_synapse.append(matched_synapse)
+        self.lifo_buffer.add_synapse_list_to_lifo(list_matched_synapse)
+        self.lifo_buffer.api_response.user_order = order
+
+        with mock.patch("kalliope.core.TTS.TTSModule.generate_and_play"):
+
+            response = self.lifo_buffer.execute(is_api_call=True)
+
+            expected_result = {
+                'status': 'waiting_for_answer',
+                'matched_synapses': [
+                    {
+                        'matched_order': 'enter in synapse 1',
+                        'neuron_module_list':
+                            [
+                                {
+                                    'neuron_name': 'Say',
+                                    'generated_message': 'question in synapse 1'
+                                }
+                            ],
+                        'synapse_name': 'synapse1'
+                    }
+                ],
+                'user_order': 'enter in synapse 1'
+            }
+
+            self.assertEqual(response, expected_result)
+
+            # give an answer
+            answer = "answer synapse1"
+            response = self.lifo_buffer.execute(answer=answer,
+                                                is_api_call=True)
+            expected_result = {
+                'status': 'waiting_for_answer',
+                'matched_synapses': [
+                    {
+                        'matched_order': 'enter in synapse 1',
+                        'neuron_module_list': [
+                            {
+                                'neuron_name': 'Say',
+                                 'generated_message': 'question in synapse 1'
+                            },
+                            {
+                                'neuron_name': 'Neurotransmitter',
+                                'generated_message': None
+                            }
+                        ],
+                        'synapse_name': 'synapse1'
+                    },
+                    {
+                        'matched_order': 'answer synapse1',
+                        'neuron_module_list': [
+                            {
+                                'neuron_name': 'Say',
+                                'generated_message': 'enter synapse 2'
+                            }
+                        ],
+                    'synapse_name': 'synapse2'
+                    }
+                ],
+                'user_order': None
+            }
+            self.assertEqual(response, expected_result)
+
+            # give the last answer
+            answer = "synapse5"
+            response = self.lifo_buffer.execute(answer=answer,
+                                                is_api_call=True)
+            expected_result = {
+                'status': 'complete',
+                'matched_synapses': [
+                    {
+                        'matched_order': 'answer synapse1',
+                        'neuron_module_list': [
+                            {
+                                'neuron_name': 'Say',
+                                'generated_message': 'enter synapse 2'
+                            },
+                            {
+                                'neuron_name': 'Neurotransmitter',
+                                'generated_message': None
+                            }
+                        ],
+                        'synapse_name': 'synapse2'
+                    },
+                    {
+                        'matched_order': 'synapse5',
+                        'neuron_module_list': [
+                            {
+                                'neuron_name': 'Say',
+                                'generated_message': 'execution of synapse 5'
+                            }
+                        ],
+                        'synapse_name': 'synapse5'
+                    },
+                    {
+                        'matched_order': 'enter in synapse 1',
+                        'neuron_module_list':[
+                            {
+                                'neuron_name': 'Say',
+                                'generated_message': 'question in synapse 1'
+                            },
+                            {
+                                'neuron_name': 'Neurotransmitter',
+                                'generated_message': None
+                            },
+                            {
+                                'neuron_name': 'Say',
+                                'generated_message': 'last neuron in synapse 1'
+                            }
+                        ],
+                        'synapse_name': 'synapse1'
+                    }
+                ],
+                'user_order': None
+            }
+
+            self.assertEqual(response, expected_result)
+
+        # --------------------------------------
+        # Test 2. The user doesn't answered correctly to the first neurotransmitter
+        # --------------------------------------
+
+        # we suppose that the first synapse has matched the first synapse
+        synapse = BrainLoader().brain.get_synapse_by_name("synapse1")
+        order = "enter in synapse 1"
+        matched_synapse = MatchedSynapse(matched_synapse=synapse,
+                                         user_order=order,
+                                         matched_order=order)
+        list_matched_synapse = list()
+        list_matched_synapse.append(matched_synapse)
+        self.lifo_buffer.add_synapse_list_to_lifo(list_matched_synapse)
+        self.lifo_buffer.api_response.user_order = order
+
+        with mock.patch("kalliope.core.TTS.TTSModule.generate_and_play"):
+            # fist call to enter in the neurotransmitter
+            self.lifo_buffer.execute(is_api_call=True)
+
+            wrong_answer = "wrong answer"
+            response = self.lifo_buffer.execute(answer=wrong_answer, is_api_call=True)
+
+            expected_result = {
+                'status': 'complete',
+                'matched_synapses': [
+                    {
+                        'matched_order': 'enter in synapse 1',
+                        'neuron_module_list': [
+                            {
+                                'neuron_name': 'Say',
+                                'generated_message': 'question in synapse 1'
+                            },
+                            {
+                                'neuron_name': 'Neurotransmitter',
+                                'generated_message': None
+                            },
+                            {
+                                'neuron_name': 'Say',
+                                'generated_message': 'last neuron in synapse 1'
+                            }
+                        ],
+                        'synapse_name': 'synapse1'
+                    },
+                    {
+                        'matched_order': None,
+                        'neuron_module_list': [
+                            {
+                                'neuron_name': 'Say',
+                                'generated_message': 'not understood'
+                            }
+                        ],
+                        'synapse_name': 'synapse4'
+                    }
+                ],
+                'user_order': None
+            }
+
+            self.assertEqual(response, expected_result)
+
+        # --------------------------------------
+        # Test 3. No synapse matched, we still execute the list
+        # --------------------------------------
+        list_matched_synapse = list()
+        self.lifo_buffer.add_synapse_list_to_lifo(list_matched_synapse)
+        self.lifo_buffer.api_response.user_order = "this is an order"
+
+        with mock.patch("kalliope.core.TTS.TTSModule.generate_and_play"):
+            # fist call to enter in the neurotransmitter
+            response = self.lifo_buffer.execute(is_api_call=True)
+
+            expected_result = {
+                'status': None,
+                'matched_synapses': [],
+                'user_order': 'this is an order'
+            }
+
+            self.assertEqual(response, expected_result)
+
+    def test_add_synapse_list_to_lifo(self):
+        """
+        Testing to add a synapse to the lifo
+        """
+        synapse = BrainLoader().brain.get_synapse_by_name("synapse1")
+        order = "enter in synapse 1"
+        matched_synapse = MatchedSynapse(matched_synapse=synapse,
+                                         user_order=order,
+                                         matched_order=order)
+        list_matched_synapse = list()
+        list_matched_synapse.append(matched_synapse)
+        self.lifo_buffer.add_synapse_list_to_lifo(list_matched_synapse)
+
+        self.assertEqual(self.lifo_buffer.lifo_list, [list_matched_synapse])
+
+    def test_clean(self):
+        """
+        Test the Cleaning of the matched synapses list
+        """
+        synapse = BrainLoader().brain.get_synapse_by_name("synapse1")
+        order = "enter in synapse 1"
+        matched_synapse = MatchedSynapse(matched_synapse=synapse,
+                                         user_order=order,
+                                         matched_order=order)
+        list_matched_synapse = list()
+        list_matched_synapse.append(matched_synapse)
+        self.lifo_buffer.add_synapse_list_to_lifo(list_matched_synapse)
+
+        self.lifo_buffer.clean()
+        self.assertEqual(0, len(self.lifo_buffer.lifo_list))
+
+    def test_return_serialized_api_response(self):
+        """
+        Test the serialization
+        """
+        self.lifo_buffer.clean()
+        self.lifo_buffer.execute(is_api_call=True)
+        expected_result = {'status': None, 'matched_synapses': [], 'user_order': None}
+        response = self.lifo_buffer._return_serialized_api_response()
+        self.assertEqual(expected_result, response)
+
+    def test_process_synapse_list(self):
+        """
+        Testing the neuron list from a synapse
+        """
+        synapse = BrainLoader().brain.get_synapse_by_name("synapse1")
+        order = "enter in synapse 1"
+        matched_synapse = MatchedSynapse(matched_synapse=synapse,
+                                         user_order=order,
+                                         matched_order=order)
+        list_matched_synapse = list()
+        list_matched_synapse.append(matched_synapse)
+
+        with mock.patch("kalliope.core.LIFOBuffer._process_neuron_list"):
+            self.lifo_buffer._process_synapse_list(list_matched_synapse)
+            expected_response = {
+                'status': None,
+                'matched_synapses': [
+                    {
+                        'matched_order': 'enter in synapse 1',
+                        'neuron_module_list': [],
+                        'synapse_name': 'synapse1'
+                    }
+                ],
+                'user_order': None
+            }
+            self.assertEqual(expected_response, self.lifo_buffer.api_response.serialize())
+            self.assertEqual(0, len(self.lifo_buffer.lifo_list))
+
+    def test_process_neuron_list(self):
+        # Test with a neuron that doesn't wait for an answer
+        synapse = BrainLoader().brain.get_synapse_by_name("synapse5")
+        order = "synapse5"
+        matched_synapse = MatchedSynapse(matched_synapse=synapse,
+                                         user_order=order,
+                                         matched_order=order)
+
+        with mock.patch("kalliope.core.TTS.TTSModule.generate_and_play"):
+            self.lifo_buffer.set_api_call(True)
+            self.lifo_buffer._process_neuron_list(matched_synapse=matched_synapse)
+            self.assertEqual("complete", self.lifo_buffer.api_response.status)
+
+        # test with neuron that wait for an answer
+        self.lifo_buffer.clean()
+        synapse = BrainLoader().brain.get_synapse_by_name("synapse6")
+        order = "synapse6"
+        matched_synapse = MatchedSynapse(matched_synapse=synapse,
+                                         user_order=order,
+                                         matched_order=order)
+
+        self.lifo_buffer.set_api_call(True)
+        with mock.patch("kalliope.core.TTS.TTSModule.generate_and_play"):
+            with self.assertRaises(Serialize):
+                self.lifo_buffer._process_neuron_list(matched_synapse=matched_synapse)
+
+        # test with a neuron that want to add a synapse list to the LIFO
+        self.lifo_buffer.clean()
+        synapse = BrainLoader().brain.get_synapse_by_name("synapse6")
+        order = "synapse6"
+        matched_synapse = MatchedSynapse(matched_synapse=synapse,
+                                         user_order=order,
+                                         matched_order=order)
+
+        self.lifo_buffer.set_api_call(True)
+        self.lifo_buffer.set_answer("synapse 6 answer")
+        with mock.patch("kalliope.core.TTS.TTSModule.generate_and_play"):
+            with self.assertRaises(SynapseListAddedToLIFO):
+                self.lifo_buffer._process_neuron_list(matched_synapse=matched_synapse)
+
+
+if __name__ == '__main__':
+    unittest.main()
+
+    # suite = unittest.TestSuite()
+    # suite.addTest(TestLIFOBuffer("test_process_neuron_list"))
+    # runner = unittest.TextTestRunner()
+    # runner.run(suite)

+ 484 - 0
Tests/test_models.py

@@ -0,0 +1,484 @@
+import unittest
+
+import mock
+from kalliope.core.Models.Tts import Tts
+
+from kalliope.core.Models.Trigger import Trigger
+
+from kalliope.core.Models.Stt import Stt
+
+from kalliope.core.Models.RestAPI import RestAPI
+
+from kalliope.core.Models.Dna import Dna
+
+from kalliope.core import LIFOBuffer
+from kalliope.core.Models.Settings import Settings
+
+from kalliope.core.Models import Neuron, Order, Synapse, Brain, Event, Resources
+
+from kalliope.core.Models.APIResponse import APIResponse
+from kalliope.core.Models.MatchedSynapse import MatchedSynapse
+
+
+class TestModels(unittest.TestCase):
+
+    def setUp(self):
+        # Init
+        neuron1 = Neuron(name='neurone1', parameters={'var1': 'val1'})
+        neuron2 = Neuron(name='neurone2', parameters={'var2': 'val2'})
+        neuron3 = Neuron(name='neurone3', parameters={'var3': 'val3'})
+        neuron4 = Neuron(name='neurone4', parameters={'var4': 'val4'})
+
+        signal1 = Order(sentence="this is the sentence")
+        signal2 = Order(sentence="this is the second sentence")
+        signal3 = Order(sentence="that is part of the third sentence")
+
+        self.synapse1 = Synapse(name="Synapse1", neurons=[neuron1, neuron2], signals=[signal1])
+        self.synapse2 = Synapse(name="Synapse2", neurons=[neuron3, neuron4], signals=[signal2])
+        self.synapse3 = Synapse(name="Synapse3", neurons=[neuron2, neuron4], signals=[signal3])
+
+        self.all_synapse_list1 = [self.synapse1,
+                                  self.synapse2,
+                                  self.synapse3]
+
+        self.all_synapse_list2 = [self.synapse2,
+                                  self.synapse3]
+
+        self.brain_test1 = Brain(synapses=self.all_synapse_list1)
+        self.brain_test2 = Brain(synapses=self.all_synapse_list2)
+        # this brain is the same as the first one
+        self.brain_test3 = Brain(synapses=self.all_synapse_list1)
+
+        self.settings_test = Settings(default_synapse="Synapse3")
+
+        # clean the LiFO
+        LIFOBuffer.lifo_list = list()
+
+    def test_APIResponse(self):
+        user_order = "user order"
+        self.matched_synapse = MatchedSynapse(matched_synapse=self.synapse1, matched_order=user_order)
+
+        api_response = APIResponse()
+        api_response.user_order = user_order
+        api_response.list_processed_matched_synapse = [self.matched_synapse]
+
+        expected_result_serialize = {
+            'status': None,
+            'matched_synapses':
+                [
+                    {
+                        'matched_order': 'user order',
+                        'neuron_module_list': [],
+                        'synapse_name': 'Synapse1'
+                    }
+                ],
+                'user_order': 'user order'
+        }
+
+        self.assertEqual(expected_result_serialize, api_response.serialize())
+
+        expected_result_str = "{'status': None, 'matched_synapses': [{'matched_order': 'user order', " \
+                              "'neuron_module_list': [], 'synapse_name': 'Synapse1'}], 'user_order': 'user order'}"
+
+        self.assertEqual(expected_result_str, api_response.__str__())
+
+    def test_Brain(self):
+        # test get synapse by name
+        expect_result = self.synapse1
+        synapse_name = "Synapse1"
+        self.assertEqual(self.brain_test1.get_synapse_by_name(synapse_name), expect_result)
+
+        # test equals
+        self.assertTrue(self.brain_test1.__eq__(self.brain_test3))
+
+        # test not equals
+        self.assertFalse(self.brain_test1.__eq__(self.brain_test2))
+
+    def test_Dna(self):
+        # create DNA object
+        dna1 = Dna(name="dna1", module_type="neuron", author="kalliope",
+                   kalliope_supported_version="0.4.4", tags="test")
+
+        dna2 = Dna(name="dna2", module_type="neuron", author="community",
+                   kalliope_supported_version="0.4.2", tags="other")
+
+        # this dna is exactly the same as the first one
+        dna3 = Dna(name="dna1", module_type="neuron", author="kalliope",
+                   kalliope_supported_version="0.4.4", tags="test")
+
+        expected_result_serialize = {
+            'kalliope_supported_version': '0.4.4',
+            'tags': 'test',
+            'type': 'neuron',
+            'name': 'dna1',
+            'author': 'kalliope'
+        }
+        expected_result_str = "{'kalliope_supported_version': '0.4.4', 'tags': 'test', 'type':" \
+                              " 'neuron', 'name': 'dna1', 'author': 'kalliope'}"
+
+        self.assertEqual(expected_result_serialize, dna1.serialize())
+        self.assertEqual(expected_result_str, dna1.__str__())
+
+        self.assertTrue(dna1.__eq__(dna3))
+        self.assertFalse(dna1.__eq__(dna2))
+
+    def test_Event(self):
+        event1 = Event(year=2017, month=12, day=31, week=53, day_of_week=2,
+                       hour=8, minute=30, second=0)
+
+        event2 = Event(year=2018, month=11, day=30, week=25, day_of_week=4,
+                       hour=9, minute=40, second=0)
+
+        # same as the event1
+        event3 = Event(year=2017, month=12, day=31, week=53, day_of_week=2,
+                       hour=8, minute=30, second=0)
+
+        expected_result_serialize = {
+            'event': {
+                'week': 53,
+                'second': 0,
+                'minute': 30,
+                'hour': 8,
+                'year': 2017,
+                'day': 31,
+                'day_of_week': 2,
+                'month': 12
+            }
+        }
+        expected_result_str = "{'event': {'week': 53, 'second': 0, 'minute': 30, 'hour': 8, " \
+                              "'year': 2017, 'day': 31, 'day_of_week': 2, 'month': 12}}"
+
+        self.assertEqual(expected_result_serialize, event1.serialize())
+        self.assertEqual(expected_result_str, event1.__str__())
+
+        self.assertTrue(event1.__eq__(event3))
+        self.assertFalse(event1.__eq__(event2))
+
+    def test_MatchedSynapse(self):
+        user_order = "user order"
+        matched_synapse1 = MatchedSynapse(matched_synapse=self.synapse1, matched_order=user_order)
+        matched_synapse2 = MatchedSynapse(matched_synapse=self.synapse2, matched_order=user_order)
+        matched_synapse3 = MatchedSynapse(matched_synapse=self.synapse1, matched_order=user_order)
+
+        expected_result_serialize = {
+            'matched_order': 'user order',
+            'neuron_module_list': [],
+            'synapse_name': 'Synapse1'
+        }
+        expected_result_str = "{'matched_order': 'user order', 'neuron_module_list': [], 'synapse_name': 'Synapse1'}"
+
+        self.assertEqual(expected_result_serialize, matched_synapse1.serialize())
+        self.assertEqual(expected_result_str, matched_synapse1.__str__())
+
+        self.assertTrue(matched_synapse1.__eq__(matched_synapse3))
+        self.assertFalse(matched_synapse1.__eq__(matched_synapse2))
+
+        # test neuron parameter loader is called
+        with mock.patch("kalliope.core.NeuronParameterLoader.get_parameters") as mock_get_parameters:
+
+            MatchedSynapse(matched_synapse=self.synapse1, matched_order=user_order, user_order=user_order)
+            mock_get_parameters.assert_called_once_with(synapse_order=user_order,
+                                                        user_order=user_order)
+            mock_get_parameters.reset_mock()
+
+    def test_Neuron(self):
+
+        neuron1 = Neuron(name="test", parameters={"key1": "val1", "key2": "val2"})
+        neuron2 = Neuron(name="test", parameters={"key3": "val3", "key4": "val4"})
+        neuron3 = Neuron(name="test", parameters={"key1": "val1", "key2": "val2"})
+
+        expected_result_serialize = {'name': 'test', 'parameters': {'key2': 'val2', 'key1': 'val1'}}
+        expected_result_str = "{'name': 'test', 'parameters': {'key2': 'val2', 'key1': 'val1'}}"
+
+        self.assertEqual(expected_result_serialize, neuron1.serialize())
+        self.assertEqual(expected_result_str, neuron1.__str__())
+
+        self.assertTrue(neuron1.__eq__(neuron3))
+        self.assertFalse(neuron1.__eq__(neuron2))
+
+        # test password
+        neuron_name = "test"
+        neuron_parameters = {
+            "password": "my secret",
+            "parameter": "test"
+        }
+
+        neuron = Neuron()
+        neuron.name = neuron_name
+        neuron.parameters = neuron_parameters
+
+        expected_result = "{'name': 'test', 'parameters': {'password': '*****', 'parameter': 'test'}}"
+
+        self.assertEqual(neuron.__str__(), expected_result)
+
+        neuron_name = "test"
+        neuron_parameters = {
+            "password_parameter": "my secret",
+            "parameter": "test"
+        }
+
+        neuron = Neuron()
+        neuron.name = neuron_name
+        neuron.parameters = neuron_parameters
+
+        expected_result = "{'name': 'test', 'parameters': {'parameter': 'test', 'password_parameter': '*****'}}"
+
+        self.assertEqual(neuron.__str__(), expected_result)
+
+    def test_Order(self):
+        order1 = Order(sentence="this is an order")
+        order2 = Order(sentence="this is an other order")
+        order3 = Order(sentence="this is an order")
+
+        expected_result_serialize = {'order': 'this is an order'}
+        expected_result_str = "{'order': 'this is an order'}"
+
+        self.assertEqual(expected_result_serialize, order1.serialize())
+        self.assertEqual(expected_result_str, order1.__str__())
+
+        self.assertTrue(order1.__eq__(order3))
+        self.assertFalse(order1.__eq__(order2))
+
+    def test_Resources(self):
+        resource1 = Resources(neuron_folder="/path/neuron", stt_folder="/path/stt",
+                              tts_folder="/path/tts", trigger_folder="/path/trigger")
+
+        resource2 = Resources(neuron_folder="/other_path/neuron", stt_folder="/other_path/stt",
+                              tts_folder="/other_path/tts", trigger_folder="/other_path/trigger")
+
+        resource3 = Resources(neuron_folder="/path/neuron", stt_folder="/path/stt",
+                              tts_folder="/path/tts", trigger_folder="/path/trigger")
+
+        expected_result_serialize = {
+            'tts_folder': '/path/tts',
+            'neuron_folder': '/path/neuron',
+            'stt_folder': '/path/stt',
+            'trigger_folder': '/path/trigger'
+        }
+        expected_result_str = "{'tts_folder': '/path/tts', 'neuron_folder': '/path/neuron', " \
+                              "'stt_folder': '/path/stt', 'trigger_folder': '/path/trigger'}"
+
+        self.assertEqual(expected_result_serialize, resource1.serialize())
+        self.assertEqual(expected_result_str, resource1.__str__())
+
+        self.assertTrue(resource1.__eq__(resource3))
+        self.assertFalse(resource1.__eq__(resource2))
+
+    def test_RestAPI(self):
+
+        rest_api1 = RestAPI(password_protected=True, login="admin", password="password", active=True,
+                            port=5000, allowed_cors_origin="*")
+
+        rest_api2 = RestAPI(password_protected=False, active=False,
+                            port=5000, allowed_cors_origin=None)
+
+        rest_api3 = RestAPI(password_protected=True, login="admin", password="password", active=True,
+                            port=5000, allowed_cors_origin="*")
+
+        expected_result_serialize = {
+            'password_protected': True,
+            'port': 5000,
+            'active': True,
+            'allowed_cors_origin': '*',
+            'password': 'password',
+            'login': 'admin'
+        }
+        expected_result_str = "{'password_protected': True, 'port': 5000, 'active': True, 'allowed_cors_origin': '*', " \
+                              "'password': 'password', 'login': 'admin'}"
+
+        self.assertEqual(expected_result_serialize, rest_api1.serialize())
+        self.assertEqual(expected_result_str, rest_api1.__str__())
+
+        self.assertTrue(rest_api1.__eq__(rest_api3))
+        self.assertFalse(rest_api1.__eq__(rest_api2))
+
+    def test_Settings(self):
+        with mock.patch('platform.machine', return_value='pumpkins'):
+            rest_api1 = RestAPI(password_protected=True, login="admin", password="password", active=True,
+                                port=5000, allowed_cors_origin="*")
+
+            setting1 = Settings(default_tts_name="pico2wav",
+                                default_stt_name="google",
+                                default_trigger_name="swoyboy",
+                                ttss=["ttts"],
+                                stts=["stts"],
+                                random_wake_up_answers=["yes"],
+                                random_wake_up_sounds=None,
+                                play_on_ready_notification=False,
+                                on_ready_answers=None,
+                                on_ready_sounds=None,
+                                triggers=["snowboy"],
+                                rest_api=rest_api1,
+                                cache_path="/tmp/kalliope",
+                                default_synapse="default_synapse",
+                                resources=None,
+                                variables={"key1": "val1"})
+
+            setting2 = Settings(default_tts_name="accapela",
+                                default_stt_name="bing",
+                                default_trigger_name="swoyboy",
+                                ttss=["ttts"],
+                                stts=["stts"],
+                                random_wake_up_answers=["no"],
+                                random_wake_up_sounds=None,
+                                play_on_ready_notification=False,
+                                on_ready_answers=None,
+                                on_ready_sounds=None,
+                                triggers=["snowboy"],
+                                rest_api=rest_api1,
+                                cache_path="/tmp/kalliope_tmp",
+                                default_synapse="my_default_synapse",
+                                resources=None,
+                                variables={"key1": "val1"})
+
+            setting3 = Settings(default_tts_name="pico2wav",
+                                default_stt_name="google",
+                                default_trigger_name="swoyboy",
+                                ttss=["ttts"],
+                                stts=["stts"],
+                                random_wake_up_answers=["yes"],
+                                random_wake_up_sounds=None,
+                                play_on_ready_notification=False,
+                                on_ready_answers=None,
+                                on_ready_sounds=None,
+                                triggers=["snowboy"],
+                                rest_api=rest_api1,
+                                cache_path="/tmp/kalliope",
+                                default_synapse="default_synapse",
+                                resources=None,
+                                variables={"key1": "val1"})
+
+            expected_result_serialize = {
+                'default_synapse': 'default_synapse',
+                'default_tts_name': 'pico2wav',
+                'rest_api':
+                    {
+                        'password_protected': True,
+                        'port': 5000,
+                        'active': True,
+                        'allowed_cors_origin': '*',
+                        'password': 'password',
+                        'login': 'admin'
+                    },
+                'play_on_ready_notification': False,
+                'default_stt_name': 'google',
+                'kalliope_version': '0.4.4b',
+                'random_wake_up_sounds': None,
+                'on_ready_answers': None,
+                'default_trigger_name': 'swoyboy',
+                'cache_path': '/tmp/kalliope',
+                'stts': ['stts'],
+                'machine': 'pumpkins',
+                'random_wake_up_answers': ['yes'],
+                'on_ready_sounds': None,
+                'ttss': ['ttts'],
+                'variables': {'key1': 'val1'},
+                'resources': None,
+                'triggers': ['snowboy']
+            }
+
+            expected_result_str = "{'default_synapse': 'default_synapse', 'default_tts_name': 'pico2wav', 'rest_api': " \
+                                  "{'password_protected': True, 'port': 5000, 'active': True, 'allowed_cors_origin': " \
+                                  "'*', 'password': 'password', 'login': 'admin'}, 'play_on_ready_notification': " \
+                                  "False, 'default_stt_name': 'google', 'kalliope_version': '0.4.4b', " \
+                                  "'random_wake_up_sounds': None, 'on_ready_answers': None, 'default_trigger_name': " \
+                                  "'swoyboy', 'cache_path': '/tmp/kalliope', 'stts': ['stts'], 'machine': 'pumpkins', " \
+                                  "'random_wake_up_answers': ['yes'], 'on_ready_sounds': None, 'ttss': ['ttts'], " \
+                                  "'variables': {'key1': 'val1'}, 'resources': None, 'triggers': ['snowboy']}"
+
+            self.assertEqual(expected_result_serialize, setting1.serialize())
+            self.assertEqual(expected_result_str, setting1.__str__())
+
+            self.assertTrue(setting1.__eq__(setting3))
+            self.assertFalse(setting1.__eq__(setting2))
+
+    def test_Stt(self):
+        stt1 = Stt(name="stt1", parameters={"key1": "val1"})
+        stt2 = Stt(name="stt2", parameters={"key2": "val2"})
+        stt3 = Stt(name="stt1", parameters={"key1": "val1"})
+
+        expected_result_serialize = {'name': 'stt1', 'parameters': {'key1': 'val1'}}
+        expected_result_str = "{'name': 'stt1', 'parameters': {'key1': 'val1'}}"
+
+        self.assertEqual(expected_result_serialize, stt1.serialize())
+        self.assertEqual(expected_result_str, stt1.__str__())
+
+        self.assertTrue(stt1.__eq__(stt3))
+        self.assertFalse(stt1.__eq__(stt2))
+
+    def test_Synapse(self):
+        neuron1 = Neuron(name='neurone1', parameters={'var1': 'val1'})
+        neuron2 = Neuron(name='neurone2', parameters={'var2': 'val2'})
+        neuron3 = Neuron(name='neurone3', parameters={'var3': 'val3'})
+        neuron4 = Neuron(name='neurone4', parameters={'var4': 'val4'})
+
+        signal1 = Order(sentence="this is the sentence")
+        signal2 = Order(sentence="this is the second sentence")
+
+        synapse1 = Synapse(name="Synapse1", neurons=[neuron1, neuron2], signals=[signal1])
+        synapse2 = Synapse(name="Synapse2", neurons=[neuron3, neuron4], signals=[signal2])
+        synapse3 = Synapse(name="Synapse1", neurons=[neuron1, neuron2], signals=[signal1])
+
+        expected_result_serialize = {
+            'signals': [
+                {
+                    'order': 'this is the sentence'
+                }
+            ],
+            'neurons': [
+                {
+                    'name': 'neurone1',
+                     'parameters': {
+                         'var1': 'val1'
+                     }
+                },
+                {
+                    'name': 'neurone2',
+                    'parameters':
+                        {
+                            'var2': 'val2'
+                        }
+                }
+            ],
+            'name': 'Synapse1'
+        }
+
+        expected_result_str = "{'signals': [{'order': 'this is the sentence'}], 'neurons': [{'name': 'neurone1', " \
+                              "'parameters': {'var1': 'val1'}}, {'name': 'neurone2', 'parameters': {'var2': " \
+                              "'val2'}}], 'name': 'Synapse1'}"
+
+        self.assertEqual(expected_result_serialize, synapse1.serialize())
+        self.assertEqual(expected_result_str, synapse1.__str__())
+
+        self.assertTrue(synapse1.__eq__(synapse3))
+        self.assertFalse(synapse1.__eq__(synapse2))
+
+    def test_Trigger(self):
+        trigger1 = Trigger(name="trigger1", parameters={"key1": "val1"})
+        trigger2 = Trigger(name="trigger2", parameters={"key2": "val2"})
+        trigger3 = Trigger(name="trigger1", parameters={"key1": "val1"})
+
+        expected_result_serialize = {'name': 'trigger1', 'parameters': {'key1': 'val1'}}
+        expected_result_str = "{'name': 'trigger1', 'parameters': {'key1': 'val1'}}"
+
+        self.assertEqual(expected_result_serialize, trigger1.serialize())
+        self.assertEqual(expected_result_str, trigger1.__str__())
+
+        self.assertTrue(trigger1.__eq__(trigger3))
+        self.assertFalse(trigger1.__eq__(trigger2))
+
+    def test_Tts(self):
+        tts1 = Tts(name="tts1", parameters={"key1": "val1"})
+        tts2 = Tts(name="tts2", parameters={"key2": "val2"})
+        tts3 = Tts(name="tts1", parameters={"key1": "val1"})
+
+        expected_result_serialize = {'name': 'tts1', 'parameters': {'key1': 'val1'}}
+        expected_result_str = "{'name': 'tts1', 'parameters': {'key1': 'val1'}}"
+
+        self.assertEqual(expected_result_serialize, tts1.serialize())
+        self.assertEqual(expected_result_str, tts1.__str__())
+
+        self.assertTrue(tts1.__eq__(tts3))
+        self.assertFalse(tts1.__eq__(tts2))
+
+

+ 90 - 0
Tests/test_neuron_launcher.py

@@ -0,0 +1,90 @@
+import unittest
+import mock
+
+from kalliope.core.Models.Resources import Resources
+from kalliope.core.NeuronLauncher import NeuronLauncher
+from kalliope.core.ConfigurationManager import SettingLoader
+
+from kalliope.core.Models.Neuron import Neuron
+
+
+class TestNeuronLauncher(unittest.TestCase):
+    """
+    Class to test Launchers Classes (TriggerLauncher, SynapseLauncher, NeuronLauncher) and methods
+    """
+
+    def setUp(self):
+        pass
+
+    ####
+    # Neurons Launcher
+    def test_launch_neuron(self):
+        """
+        Test the Neuron Launcher trying to start a Neuron
+        """
+        neuron = Neuron(name='neurone1', parameters={'var1': 'val1'})
+        sl = SettingLoader()
+        resources = Resources(neuron_folder='/var/tmp/test/resources')
+        sl.settings.resources = resources
+        with mock.patch("kalliope.core.Utils.get_dynamic_class_instantiation") as mock_get_class_instantiation:
+            NeuronLauncher.launch_neuron(neuron=neuron)
+
+            mock_get_class_instantiation.assert_called_once_with(package_name="neurons",
+                                                                 module_name=neuron.name,
+                                                                 parameters=neuron.parameters,
+                                                                 resources_dir=sl.settings.resources.neuron_folder)
+            mock_get_class_instantiation.reset_mock()
+
+    def test_start_neuron(self):
+        """
+        Testing params association and starting a Neuron
+        """
+
+        with mock.patch("kalliope.core.NeuronLauncher.launch_neuron") as mock_launch_neuron_method:
+            # Assert to the neuron is launched
+            neuron1 = Neuron(name='neurone1', parameters={'var1': 'val1'})
+            params = {
+                'param1':'parval1'
+            }
+            NeuronLauncher.start_neuron(neuron=neuron1,
+                                        parameters_dict=params)
+            mock_launch_neuron_method.assert_called_with(neuron1)
+            mock_launch_neuron_method.reset_mock()
+
+            # Assert the params are well passed to the neuron
+            neuron2 = Neuron(name='neurone2', parameters={'var2': 'val2', 'args': ['arg1', 'arg2']})
+            params = {
+                'arg1':'argval1',
+                'arg2':'argval2'
+            }
+            NeuronLauncher.start_neuron(neuron=neuron2,
+                                        parameters_dict=params)
+            neuron2_params = Neuron(name='neurone2',
+                                    parameters={'var2': 'val2',
+                                                'args': ['arg1', 'arg2'],
+                                                'arg1':'argval1',
+                                                'arg2':'argval2'}
+                                    )
+            mock_launch_neuron_method.assert_called_with(neuron2_params)
+            mock_launch_neuron_method.reset_mock()
+
+            # Assert the Neuron is not started when missing args
+            neuron3 = Neuron(name='neurone3', parameters={'var3': 'val3', 'args': ['arg3', 'arg4']})
+            params = {
+                'arg1': 'argval1',
+                'arg2': 'argval2'
+            }
+            NeuronLauncher.start_neuron(neuron=neuron3,
+                                        parameters_dict=params)
+            mock_launch_neuron_method.assert_not_called()
+            mock_launch_neuron_method.reset_mock()
+
+            # 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)
+            mock_launch_neuron_method.assert_not_called()
+            mock_launch_neuron_method.reset_mock()
+
+

+ 0 - 40
Tests/test_neuron_model.py

@@ -1,40 +0,0 @@
-import os
-import unittest
-
-from kalliope.core.Models import Neuron
-
-
-class TestNeuronModule(unittest.TestCase):
-
-    def test_password_parameter(self):
-        neuron_name = "test"
-        neuron_parameters = {
-            "password": "my secret",
-            "parameter": "test"
-        }
-
-        neuron = Neuron()
-        neuron.name = neuron_name
-        neuron.parameters = neuron_parameters
-
-        expected_result = "Neuron: name: test, parameters: {'password': '*****', 'parameter': 'test'}"
-
-        self.assertEqual(neuron.__str__(), expected_result)
-
-    def test_password_in_parameter(self):
-        neuron_name = "test"
-        neuron_parameters = {
-            "password_parameter": "my secret",
-            "parameter": "test"
-        }
-
-        neuron = Neuron()
-        neuron.name = neuron_name
-        neuron.parameters = neuron_parameters
-
-        expected_result = "Neuron: name: test, parameters: {'parameter': 'test', 'password_parameter': '*****'}"
-
-        self.assertEqual(neuron.__str__(), expected_result)
-
-if __name__ == '__main__':
-    unittest.main()

+ 11 - 56
Tests/test_neuron_module.py

@@ -3,10 +3,6 @@ import unittest
 import mock
 
 from kalliope.core.NeuronModule import NeuronModule, TemplateFileNotFoundException
-from kalliope.core.Models.Neuron import Neuron
-from kalliope.core.Models.Synapse import Synapse
-from kalliope.core.Models.Brain import Brain
-from kalliope.core.Models.Order import Order
 
 
 class TestNeuronModule(unittest.TestCase):
@@ -114,61 +110,20 @@ class TestNeuronModule(unittest.TestCase):
         expected_result = "hello, this is a {{ test }}"
         self.assertEqual(NeuronModule._get_content_of_file(self.file_template), expected_result)
 
-    def test_run_synapse_by_name_with_order(self):
+    def test_serialize(self):
         """
-        Test to start a synapse with a specific given order
-        Scenarii :
-            - Neuron has been found and launched
-            - Neuron has not been found
+        Test the serialisation of the neuron module
         """
+        neuron_module = NeuronModule()
+        neuron_module.neuron_name = "kalliope"
+        neuron_module.tts_message = "I am french"
 
-        # Init
-        neuron1 = Neuron(name='neurone1', parameters={'var1': 'val1'})
-        neuron2 = Neuron(name='neurone2', parameters={'var2': 'val2'})
-        neuron3 = Neuron(name='neurone3', parameters={'var3': 'val3'})
-        neuron4 = Neuron(name='neurone4', parameters={'var4': 'val4'})
-
-        signal1 = Order(sentence="the sentence")
-        signal2 = Order(sentence="the second sentence")
-        signal3 = Order(sentence="part of the third sentence")
-
-        synapse1 = Synapse(name="Synapse1", neurons=[neuron1, neuron2], signals=[signal1])
-        synapse2 = Synapse(name="Synapse2", neurons=[neuron3, neuron4], signals=[signal2])
-        synapse3 = Synapse(name="Synapse3", neurons=[neuron2, neuron4], signals=[signal3])
-
-        all_synapse_list = [synapse1,
-                            synapse2,
-                            synapse3]
-
-        br = Brain(synapses=all_synapse_list)
-
-        order = "This is the order"
-        synapse_name = "Synapse2"
-        answer = "This is the {{ answer }}"
-        expected_parameter = {"answer": "order"}
-
-        with mock.patch("kalliope.core.NeuronLauncher.start_neuron_list") as mock_NeuronLauncher_start:
-            neuron_mod = NeuronModule()
-            neuron_mod.brain = br
-
-            # Success, run synapse 2
-            launched_synapse = neuron_mod.run_synapse_by_name_with_order(order=order,
-                                                                         synapse_name=synapse_name,
-                                                                         order_template=answer)
-            self.assertEqual(synapse2, launched_synapse)
-
-            mock_NeuronLauncher_start.assert_called_once_with(neuron_list=[neuron3, neuron4],
-                                                              parameters_dict=expected_parameter)
-            mock_NeuronLauncher_start.reset_mock()
-
-            # Fail
-            synapse_name = "Synapse5"
-            self.assertIsNone(neuron_mod.run_synapse_by_name_with_order(order=order,
-                                                                        synapse_name=synapse_name,
-                                                                        order_template=answer))
-
-            mock_NeuronLauncher_start.assert_not_called()
-            mock_NeuronLauncher_start.reset_mock()
+        expected_result = {
+            'neuron_name': "kalliope",
+            'generated_message': "I am french"
+        }
+
+        self.assertEqual(expected_result, neuron_module.serialize())
 
 
 

+ 8 - 1
Tests/test_order_analyser.py

@@ -5,6 +5,7 @@ from kalliope.core.Models import Brain
 from kalliope.core.Models import Neuron
 from kalliope.core.Models import Order
 from kalliope.core.Models import Synapse
+from kalliope.core.Models.MatchedSynapse import MatchedSynapse
 from kalliope.core.OrderAnalyser import OrderAnalyser
 
 
@@ -38,9 +39,15 @@ class TestOrderAnalyser(unittest.TestCase):
 
         # TEST1: should return synapse1
         spoken_order = "this is the sentence"
+
+        # Create the matched synapse
+        matched_synapse_1 = MatchedSynapse(matched_synapse=synapse1,
+                                           matched_order=spoken_order,
+                                           user_order=spoken_order)
+
         matched_synapses = OrderAnalyser.get_matching_synapse(order=spoken_order, brain=br)
         self.assertEqual(len(matched_synapses), 1)
-        self.assertTrue(any(synapse1 in matched_synapse for matched_synapse in matched_synapses))
+        self.assertTrue(matched_synapse_1 in matched_synapses)
 
         # TEST2: should return synapse1 and 2
         spoken_order = "this is the second sentence"

+ 68 - 80
Tests/test_rest_api.py

@@ -5,6 +5,7 @@ import unittest
 from flask import Flask
 from flask_testing import LiveServerTestCase
 
+from kalliope.core import LIFOBuffer
 from kalliope.core.Models import Singleton
 from kalliope._version import version_str
 from kalliope.core.ConfigurationManager import BrainLoader
@@ -16,6 +17,8 @@ class TestRestAPI(LiveServerTestCase):
 
     def tearDown(self):
         Singleton._instances = {}
+        # clean the lifo
+        LIFOBuffer.lifo_list = list()
 
     def create_app(self):
         """
@@ -128,8 +131,6 @@ class TestRestAPI(LiveServerTestCase):
         # a lot of char ti process
         self.maxDiff = None
         self.assertEqual(response.status_code, 200)
-        # print response.get_data()
-        # print json.dumps(expected_content)
         self.assertEqual(json.dumps(expected_content), json.dumps(json.loads(response.get_data())))
 
     def test_get_one_synapse(self):
@@ -175,26 +176,14 @@ class TestRestAPI(LiveServerTestCase):
         url = self.get_server_url() + "/synapses/start/id/test"
         result = self.client.post(url)
 
-        expected_content = {
-            "synapses": {
-                "name": "test",
-                "neurons": [
-                    {
-                        "name": "say",
-                        "parameters": {
-                            "message": [
-                                "test message"
-                            ]
-                        }
-                    }
-                ],
-                "signals": [
-                    {
-                        "order": "test_order"
-                    }
-                ]
-            }
-        }
+        expected_content = {'status': 'complete',
+                            'matched_synapses':
+                                [{'matched_order': None,
+                                  'neuron_module_list':
+                                      [{'generated_message': 'test message', 'neuron_name': 'Say'}],
+                                  'synapse_name': 'test'}],
+                            'user_order': None
+                            }
         self.assertEqual(json.dumps(expected_content), json.dumps(json.loads(result.get_data())))
         self.assertEqual(result.status_code, 201)
 
@@ -217,28 +206,22 @@ class TestRestAPI(LiveServerTestCase):
         data = {"order": "test_order"}
         result = self.client.post(url, headers=headers, data=json.dumps(data))
 
-        expected_content = {
-            "synapses": [
-                {
-                    "name": "test",
-                    "neurons": [
-                        {
-                            "name": "say",
-                            "parameters": {
-                                "message": [
-                                    "test message"
-                                ]
+        expected_content = {'status': 'complete',
+                            'matched_synapses':
+                                [
+                                    {
+                                        'matched_order': "test_order",
+                                        'neuron_module_list':
+                                            [
+                                                {
+                                                    'generated_message': 'test message', 'neuron_name': 'Say'
+                                                }
+                                            ],
+                                  'synapse_name': 'test'
+                                    }
+                                ],
+                            'user_order': "test_order"
                             }
-                        }
-                    ],
-                    "signals": [
-                        {
-                            "order": "test_order"
-                        }
-                    ]
-                }
-            ]
-        }
         self.assertEqual(json.dumps(expected_content), json.dumps(json.loads(result.get_data())))
         self.assertEqual(result.status_code, 201)
 
@@ -250,46 +233,51 @@ class TestRestAPI(LiveServerTestCase):
                                   headers=headers,
                                   data=json.dumps(data))
 
-        expected_content = {'error': {'error': "The given order doesn't match any synapses"}}
+        expected_content = {'status': None, 'matched_synapses': [], 'user_order': u'non existing order'}
 
         self.assertEqual(json.dumps(expected_content), json.dumps(json.loads(result.get_data())))
-        self.assertEqual(result.status_code, 400)
+        self.assertEqual(result.status_code, 201)
 
-    # TODO this doesn't work on travis but works locally with python -m unittest discover
-    # def test_post_synapse_by_audio(self):
-    #     url = self.get_server_url() + "/synapses/start/audio"
-    #     with open(os.path.join(self.audio_file), 'rb') as fp:
-    #         file = FileStorage(fp)
-    #         data = {
-    #             'file': file
-    #         }
-    #         result = self.client.post(url, data=data, content_type='multipart/form-data')
-    #
-    #         expected_content = {
-    #             "synapses": [
-    #                 {
-    #                     "name": "test2",
-    #                     "neurons": [
-    #                         {
-    #                             "name": "say",
-    #                             "parameters": {
-    #                                 "message": [
-    #                                     "test message"
-    #                                 ]
-    #                             }
-    #                         }
-    #                     ],
-    #                     "signals": [
-    #                         {
-    #                             "order": "bonjour"
-    #                         }
-    #                     ]
-    #                 }
-    #             ]
-    #         }
-    #
-    #         self.assertEqual(json.dumps(expected_content), json.dumps(json.loads(result.get_data())))
-    #         self.assertEqual(result.status_code, 201)
+        # TODO this doesn't work on travis but works locally with python -m unittest discover
+        # def test_post_synapse_by_audio(self):
+        #     url = self.get_server_url() + "/synapses/start/audio"
+        #     with open(os.path.join(self.audio_file), 'rb') as fp:
+        #         file = FileStorage(fp)
+        #         data = {
+        #             'file': file
+        #         }
+        #         result = self.client.post(url, data=data, content_type='multipart/form-data')
+        #
+        #         expected_content = {
+        #             "synapses": [
+        #                 {
+        #                     "name": "test2",
+        #                     "neurons": [
+        #                         {
+        #                             "name": "say",
+        #                             "parameters": {
+        #                                 "message": [
+        #                                     "test message"
+        #                                 ]
+        #                             }
+        #                         }
+        #                     ],
+        #                     "signals": [
+        #                         {
+        #                             "order": "bonjour"
+        #                         }
+        #                     ]
+        #                 }
+        #             ]
+        #         }
+        #
+        #         self.assertEqual(json.dumps(expected_content), json.dumps(json.loads(result.get_data())))
+        #         self.assertEqual(result.status_code, 201)
 
 if __name__ == '__main__':
     unittest.main()
+
+    # suite = unittest.TestSuite()
+    # suite.addTest(TestRestAPI("test_post_synapse_by_order_not_found"))
+    # runner = unittest.TextTestRunner()
+    # runner.run(suite)

+ 81 - 36
Tests/test_synapse_launcher.py

@@ -2,7 +2,9 @@ import unittest
 
 import mock
 
+from kalliope.core import LIFOBuffer
 from kalliope.core.Models import Brain
+from kalliope.core.Models.MatchedSynapse import MatchedSynapse
 from kalliope.core.Models.Settings import Settings
 from kalliope.core.SynapseLauncher import SynapseLauncher, SynapseNameNotFound
 
@@ -31,50 +33,93 @@ class TestSynapseLauncher(unittest.TestCase):
         self.synapse2 = Synapse(name="Synapse2", neurons=[neuron3, neuron4], signals=[signal2])
         self.synapse3 = Synapse(name="Synapse3", neurons=[neuron2, neuron4], signals=[signal3])
 
-        all_synapse_list = [self.synapse1,
-                            self.synapse2,
-                            self.synapse3]
+        self.all_synapse_list = [self.synapse1,
+                                 self.synapse2,
+                                 self.synapse3]
 
-        self.brain_test = Brain(synapses=all_synapse_list)
+        self.brain_test = Brain(synapses=self.all_synapse_list)
         self.settings_test = Settings(default_synapse="Synapse3")
 
-    def test_run_matching_synapse_or_default(self):
+        # clean the LiFO
+        LIFOBuffer.lifo_list = list()
 
+    def test_start_synapse_by_name(self):
+        # existing synapse in the brain
+        with mock.patch("kalliope.core.LIFOBuffer.execute"):
+            should_be_created_matched_synapse = MatchedSynapse(matched_synapse=self.synapse1)
+            SynapseLauncher.start_synapse_by_name("Synapse1", brain=self.brain_test)
+            # we expect that the lifo has been loaded with the synapse to run
+            expected_result = [[should_be_created_matched_synapse]]
+            self.assertEqual(expected_result, LIFOBuffer.lifo_list)
+
+        # non existing synapse in the brain
+        with self.assertRaises(SynapseNameNotFound):
+            SynapseLauncher.start_synapse_by_name("not_existing", brain=self.brain_test)
+
+    def test_run_matching_synapse_from_order(self):
+        # ------------------
         # test_match_synapse1
-        with mock.patch("kalliope.core.NeuronLauncher.start_neuron_list"):
+        # ------------------
+        with mock.patch("kalliope.core.LIFOBuffer.execute"):
             order_to_match = "this is the sentence"
-            expected_result = [self.synapse1]
 
-            self.assertEqual(expected_result,
-                             SynapseLauncher.run_matching_synapse_or_default(order_to_match,
-                                                                             brain=self.brain_test,
-                                                                             settings=self.settings_test))
+            should_be_created_matched_synapse = MatchedSynapse(matched_synapse=self.synapse1,
+                                                               user_order=order_to_match,
+                                                               matched_order="this is the sentence")
+            expected_result = [[should_be_created_matched_synapse]]
+            SynapseLauncher.run_matching_synapse_from_order(order_to_match,
+                                                            brain=self.brain_test,
+                                                            settings=self.settings_test)
 
+            self.assertEqual(expected_result, LIFOBuffer.lifo_list)
+
+        # -------------------------
         # test_match_synapse1_and_2
-        with mock.patch("kalliope.core.NeuronLauncher.start_neuron_list"):
+        # -------------------------
+        # clean LIFO
+        LIFOBuffer.lifo_list = list()
+        with mock.patch("kalliope.core.LIFOBuffer.execute"):
             order_to_match = "this is the second sentence"
-            expected_result = [self.synapse1, self.synapse2]
-
-            self.assertEqual(expected_result,
-                             SynapseLauncher.run_matching_synapse_or_default(order_to_match,
-                                                                             brain=self.brain_test,
-                                                                             settings=self.settings_test))
-
+            should_be_created_matched_synapse1 = MatchedSynapse(matched_synapse=self.synapse1,
+                                                                user_order=order_to_match,
+                                                                matched_order="this is the sentence")
+            should_be_created_matched_synapse2 = MatchedSynapse(matched_synapse=self.synapse2,
+                                                                user_order=order_to_match,
+                                                                matched_order="this is the second sentence")
+
+            expected_result = [[should_be_created_matched_synapse1, should_be_created_matched_synapse2]]
+            SynapseLauncher.run_matching_synapse_from_order(order_to_match,
+                                                            brain=self.brain_test,
+                                                            settings=self.settings_test)
+            self.assertEqual(expected_result, LIFOBuffer.lifo_list)
+
+        # -------------------------
         # test_match_default_synapse
-        with mock.patch("kalliope.core.NeuronLauncher.start_neuron"):
-            order_to_match = "this is an invalid order"
-            expected_result = [self.synapse3]
-
-            self.assertEqual(expected_result,
-                             SynapseLauncher.run_matching_synapse_or_default(order_to_match,
-                                                                             brain=self.brain_test,
-                                                                             settings=self.settings_test))
-
-    def test_start_synapse(self):
-        with mock.patch("kalliope.core.NeuronLauncher.start_neuron"):
-            expected_result = self.synapse1
-            self.assertEqual(expected_result,
-                             SynapseLauncher.start_synapse("Synapse1", brain=self.brain_test))
-
-        with self.assertRaises(SynapseNameNotFound):
-            SynapseLauncher.start_synapse(name="no_do_exist", brain=self.brain_test)
+        # -------------------------
+        # clean LIFO
+        LIFOBuffer.lifo_list = list()
+        with mock.patch("kalliope.core.LIFOBuffer.execute"):
+            order_to_match = "not existing sentence"
+            should_be_created_matched_synapse = MatchedSynapse(matched_synapse=self.synapse3,
+                                                               user_order=order_to_match,
+                                                               matched_order=None)
+
+            expected_result = [[should_be_created_matched_synapse]]
+            SynapseLauncher.run_matching_synapse_from_order(order_to_match,
+                                                            brain=self.brain_test,
+                                                            settings=self.settings_test)
+            self.assertEqual(expected_result, LIFOBuffer.lifo_list)
+
+        # -------------------------
+        # test_no_match_and_no_default_synapse
+        # -------------------------
+        # clean LIFO
+        LIFOBuffer.lifo_list = list()
+        with mock.patch("kalliope.core.LIFOBuffer.execute"):
+            order_to_match = "not existing sentence"
+            new_settings = Settings()
+            expected_result = [[]]
+            SynapseLauncher.run_matching_synapse_from_order(order_to_match,
+                                                            brain=self.brain_test,
+                                                            settings=new_settings)
+            self.assertEqual(expected_result, LIFOBuffer.lifo_list)

+ 31 - 0
Tests/test_trigger_launcher.py

@@ -0,0 +1,31 @@
+import unittest
+import mock
+
+from kalliope.core.TriggerLauncher import TriggerLauncher
+
+from kalliope.core.Models.Trigger import Trigger
+
+
+class TestTriggerLauncher(unittest.TestCase):
+    """
+    Class to test Launchers Classes (TriggerLauncher, SynapseLauncher, NeuronLauncher) and methods
+    """
+
+    def setUp(self):
+        pass
+
+    ####
+    # Trigger Launcher
+    def test_get_trigger(self):
+        """
+        Test the Trigger Launcher trying to run the trigger
+        """
+        trigger = Trigger("Trigger", {})
+        with mock.patch("kalliope.core.Utils.get_dynamic_class_instantiation") as mock_get_class_instantiation:
+            TriggerLauncher.get_trigger(trigger=trigger,
+                                        callback=None)
+
+            mock_get_class_instantiation.assert_called_once_with(package_name="trigger",
+                                                                 module_name=trigger.name,
+                                                                 parameters=trigger.parameters)
+            mock_get_class_instantiation.reset_mock()

+ 5 - 4
kalliope/__init__.py

@@ -120,13 +120,14 @@ def main():
 
         # user set a synapse to start
         if args.run_synapse is not None:
-            SynapseLauncher.start_synapse(args.run_synapse,
-                                          brain=brain)
+            SynapseLauncher.start_synapse_by_name(args.run_synapse,
+                                                  brain=brain)
 
         if args.run_order is not None:
-            SynapseLauncher.run_matching_synapse_or_default(args.run_order,
+            SynapseLauncher.run_matching_synapse_from_order(args.run_order,
                                                             brain=brain,
-                                                            settings=settings)
+                                                            settings=settings,
+                                                            is_api_call=False)
 
         if (args.run_synapse is None) and (args.run_order is None):
             # first, load events in event manager

+ 0 - 8
kalliope/core/ConfigurationManager/BrainLoader.py

@@ -87,7 +87,6 @@ class BrainLoader(object):
         for synapses_dict in dict_brain:
             if "includes" not in synapses_dict:     # we don't need to check includes as it's not a synapse
                 if ConfigurationChecker().check_synape_dict(synapses_dict):
-                    # print "synapses_dict ok"
                     name = synapses_dict["name"]
                     neurons = self._get_neurons(synapses_dict["neurons"], self.settings)
                     signals = self._get_signals(synapses_dict["signals"])
@@ -127,7 +126,6 @@ class BrainLoader(object):
         for neuron_dict in neurons_dict:
             if isinstance(neuron_dict, dict):
                 if ConfigurationChecker().check_neuron_dict(neuron_dict):
-                    # print "Neurons dict ok"
                     for neuron_name in neuron_dict:
 
                         name = neuron_name
@@ -164,11 +162,9 @@ class BrainLoader(object):
         .. seealso:: Event, Order
         .. warnings:: Class method and Private
         """
-        # print signals_dict
         signals = list()
         for signal_dict in signals_dict:
             if ConfigurationChecker().check_signal_dict(signal_dict):
-                # print "Signals dict ok"
                 event_or_order = cls._get_event_or_order_from_dict(signal_dict)
                 signals.append(event_or_order)
 
@@ -192,7 +188,6 @@ class BrainLoader(object):
         .. warnings:: Static method and Private
         """
         if 'event' in signal_or_event_dict:
-            # print "is event"
             event = signal_or_event_dict["event"]
             if ConfigurationChecker.check_event_dict(event):
                 return cls._get_event_object(event)
@@ -257,18 +252,15 @@ class BrainLoader(object):
         """
 
         if isinstance(parameter, dict):
-            # print "parameter is dict %s" % str(parameter)
             for key, value in parameter.iteritems():
                 parameter[key] = cls._replace_global_variables(value, settings=settings)
             return parameter
         if isinstance(parameter, list):
-            # print "parameter is list %s" % str(parameter)
             new_parameter_list = list()
             for el in parameter:
                 new_parameter_list.append(cls._replace_global_variables(el, settings=settings))
             return new_parameter_list
         if isinstance(parameter, str) or isinstance(parameter, unicode) or isinstance(parameter, int):
-            # print "parameter is string %s" % parameter
             if Utils.is_containing_bracket(parameter):
                 return cls._get_global_variable(sentence=parameter, settings=settings)
             return parameter

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

@@ -244,7 +244,6 @@ class SettingLoader(object):
         stts = list()
         for speechs_to_text_el in speechs_to_text_list:
             if isinstance(speechs_to_text_el, dict):
-                # print "Neurons dict ok"
                 for stt_name in speechs_to_text_el:
                     name = stt_name
                     parameters = speechs_to_text_el[name]
@@ -284,7 +283,6 @@ class SettingLoader(object):
         ttss = list()
         for text_to_speech_el in text_to_speech_list:
             if isinstance(text_to_speech_el, dict):
-                # print "Neurons dict ok"
                 for tts_name in text_to_speech_el:
                     name = tts_name
                     parameters = text_to_speech_el[name]
@@ -323,7 +321,6 @@ class SettingLoader(object):
         triggers = list()
         for trigger_el in triggers_list:
             if isinstance(trigger_el, dict):
-                # print "Neurons dict ok"
                 for trigger_name in trigger_el:
                     name = trigger_name
                     parameters = trigger_el[name]
@@ -458,7 +455,6 @@ class SettingLoader(object):
                      allowed_cors_origin = rest_api["allowed_cors_origin"]
 
             except KeyError, e:
-                # print e
                 raise SettingNotFound("%s settings not found" % e)
 
             # config ok, we can return the rest api object

+ 1 - 1
kalliope/core/EventManager.py

@@ -46,4 +46,4 @@ class EventManager(object):
         # get a brain
         brain_loader = BrainLoader()
         brain = brain_loader.brain
-        SynapseLauncher.start_synapse(synapse_name, brain=brain)
+        SynapseLauncher.start_synapse_by_name(synapse_name, brain=brain)

+ 193 - 0
kalliope/core/LIFOBuffer.py

@@ -0,0 +1,193 @@
+import logging
+
+from kalliope.core.NeuronLauncher import NeuronLauncher
+from kalliope.core.Models import Singleton
+from kalliope.core.Models.APIResponse import APIResponse
+
+logging.basicConfig()
+logger = logging.getLogger("kalliope")
+
+
+class Serialize(Exception):
+    """
+    When raised, the LIFO class return the current API response to the caller
+    """
+    pass
+
+
+class SynapseListAddedToLIFO(Exception):
+    """
+    When raised, a synapse list to process has been added to the LIFO list. 
+    The LIFO must start over and process the last synapse list added
+    """
+    pass
+
+
+class LIFOBuffer(object):
+    """
+    This class is a LIFO list of synapse to process where the last synapse list to enter will be the first synapse
+    list to be processed.
+    This design is needed in order to use Kalliope from the API. 
+    Because we want to return an information when a Neuron is still processing and waiting for an answer from the user
+    like with the Neurotransmitter neuron.
+    
+    """
+    __metaclass__ = Singleton
+
+    api_response = APIResponse()
+    lifo_list = list()
+    logger.debug("[LIFOBuffer] LIFO buffer created")
+    answer = None
+    is_api_call = False
+
+    @classmethod
+    def set_answer(cls, value):
+        cls.answer = value
+
+    @classmethod
+    def set_api_call(cls, value):
+        cls.is_api_call = value
+
+    @classmethod
+    def add_synapse_list_to_lifo(cls, matched_synapse_list):
+        """
+        Add a synapse list to process to the lifo
+        :param matched_synapse_list: List of Matched Synapse
+        :return: 
+        """
+        logger.debug("[LIFOBuffer] Add a new synapse list to process to the LIFO")
+        cls.lifo_list.append(matched_synapse_list)
+
+    @classmethod
+    def clean(cls):
+        """
+        Clean the LIFO by creating a new list
+        """
+        cls.lifo_list = list()
+        cls.api_response = APIResponse()
+
+    @classmethod
+    def _return_serialized_api_response(cls):
+        """
+        Serialize Exception has been raised by the execute process somewhere, return the serialized API response
+        to the caller. Clean up the APIResponse object for the next call
+        :return: 
+        """
+        # we prepare a json response
+        returned_api_response = cls.api_response.serialize()
+        # we clean up the API response object for the next call
+        cls.api_response = APIResponse()
+        return returned_api_response
+
+    @classmethod
+    def execute(cls, answer=None, is_api_call=False):
+        """
+        Process the LIFO list.
+        
+        The LIFO list contains multiple list of matched synapses.        
+        For each list of matched synapse we process synapses inside        
+        For each synapses we process neurons.        
+        If a neuron add a Synapse list to the lifo, this synapse list is processed before executing the first list 
+        in which we were in.
+        
+        :param answer: String answer to give the the last neuron which whas waiting for an answer
+        :param is_api_call: Boolean passed to all neuron in order to let them know if the current call comes from API
+        :return: serialized APIResponse object
+        """
+        # store the answer if present
+        cls.answer = answer
+        cls.is_api_call = is_api_call
+
+        try:
+            # we keep looping over the LIFO til we have synapse list to process in it
+            while cls.lifo_list:
+                logger.debug("[LIFOBuffer] number of synapse list to process: %s" % len(cls.lifo_list))
+                try:
+                    # get the last list of matched synapse in the LIFO
+                    last_synapse_fifo_list = cls.lifo_list[-1]
+                    cls._process_synapse_list(last_synapse_fifo_list)
+                except SynapseListAddedToLIFO:
+                    continue
+                # remove the synapse list from the LIFO
+                cls.lifo_list.remove(last_synapse_fifo_list)
+            raise Serialize
+
+        except Serialize:
+            return cls._return_serialized_api_response()
+
+    @classmethod
+    def _process_synapse_list(cls, synapse_list):
+        """
+        Process a list of matched synapse.
+        Execute each neuron list for each synapse.
+        Add info in the API response object after each processed synapse
+        Remove the synapse from the synapse_list when it has been fully executed
+        :param synapse_list: List of MatchedSynapse
+        """
+        # we keep processing til we have synapse in the FIFO to process
+        while synapse_list:
+            # get the first matched synapse in the list
+            matched_synapse = synapse_list[0]
+            # add the synapse to the API response so the user will get the status if the synapse was not already
+            # in the response
+            if matched_synapse not in cls.api_response.list_processed_matched_synapse:
+                cls.api_response.list_processed_matched_synapse.append(matched_synapse)
+
+            cls._process_neuron_list(matched_synapse=matched_synapse)
+
+            # The synapse has been processed we can remove it from the list.
+            synapse_list.remove(matched_synapse)
+
+    @classmethod
+    def _process_neuron_list(cls, matched_synapse):
+        """
+        Process the neuron list of the matched_synapse
+        Execute the Neuron
+        Executing a Neuron creates a NeuronModule object. This one can have 3 status:
+        - waiting for an answer: The neuron wait for an answer from the caller. The api response object is returned.
+                                 The neuron is not removed from the matched synapse to be executed again
+        - want to execute a synapse: The neuron add a list of synapse to execute to the lifo. 
+                                     The LIFO restart over to process it.The neuron is removed from the matched synapse
+        - complete: The neuron has been executed and its not waiting for an answer and doesn't want to start a synapse
+                    The neuron is removed from the matched synapse
+        :param matched_synapse: MatchedSynapse object to process
+        """
+
+        logger.debug("[LIFOBuffer] number of neuron to process: %s" % len(matched_synapse.neuron_fifo_list))
+        # while we have synapse to process in the list of synapse
+        while matched_synapse.neuron_fifo_list:
+            # get the first neuron in the FIFO neuron list
+            neuron = matched_synapse.neuron_fifo_list[0]
+            # from here, we are back into the last neuron we were processing.
+            if cls.answer is not None:  # we give the answer if exist to the first neuron
+                neuron.parameters["answer"] = cls.answer
+                # the next neuron should not get this answer
+                cls.answer = None
+            # todo fix this when we have a full client/server call. The client would be the voice or api call
+            neuron.parameters["is_api_call"] = cls.is_api_call
+            logger.debug("[LIFOBuffer] process_neuron_list: is_api_call: %s" % cls.is_api_call)
+            # execute the neuron
+            instantiated_neuron = NeuronLauncher.start_neuron(neuron=neuron,
+                                                              parameters_dict=matched_synapse.parameters)
+
+            # the status of an execution is "complete" if no neuron are waiting for an answer
+            cls.api_response.status = "complete"
+            if instantiated_neuron.is_waiting_for_answer:  # the neuron is waiting for an answer
+                logger.debug("[LIFOBuffer] Wait for answer mode")
+                cls.api_response.status = "waiting_for_answer"
+                raise Serialize
+            else:
+                logger.debug("[LIFOBuffer] complete mode")
+                # we add the instantiated neuron to the neuron_module_list.
+                # This one contains info about generated text
+                matched_synapse.neuron_module_list.append(instantiated_neuron)
+                # the neuron is fully processed we can remove it from the list
+                matched_synapse.neuron_fifo_list.remove(neuron)
+
+            if instantiated_neuron.pending_synapse:  # the last executed neuron want to run a synapse
+                logger.debug("[LIFOBuffer] Last executed neuron want to run a synapse. Restart the LIFO")
+                # add the synapse to the lifo (inside a list as expected by the lifo)
+                cls.add_synapse_list_to_lifo([instantiated_neuron.pending_synapse])
+                # we have added a list of synapse to the LIFO ! this one must start over.
+                # break all while loop until the execution is back to the LIFO loop
+                raise SynapseListAddedToLIFO

+ 4 - 1
kalliope/core/MainController.py

@@ -196,7 +196,10 @@ class MainController:
         Start the order analyser with the caught order to process
         """
         logger.debug("order in analysing_order_thread %s" % self.order_to_process)
-        SynapseLauncher.run_matching_synapse_or_default(self.order_to_process, self.brain, self.settings)
+        SynapseLauncher.run_matching_synapse_from_order(self.order_to_process,
+                                                        self.brain,
+                                                        self.settings,
+                                                        is_api_call=False)
 
         # return to the state "unpausing_trigger"
         self.unpause_trigger()

+ 23 - 0
kalliope/core/Models/APIResponse.py

@@ -0,0 +1,23 @@
+
+
+class APIResponse(object):
+
+    def __init__(self):
+        self.user_order = None
+        self.list_processed_matched_synapse = list()
+        self.status = None
+
+    def __str__(self):
+        return str(self.serialize())
+
+    def serialize(self):
+        """
+        This method allows to serialize in a proper way this object
+        :return: A dict of name and parameters
+        :rtype: Dict
+        """
+        return {
+            'user_order': self.user_order,
+            'matched_synapses': [e.serialize() for e in self.list_processed_matched_synapse],
+            'status': self.status
+        }

+ 1 - 5
kalliope/core/Models/Dna.py

@@ -25,11 +25,7 @@ class Dna(object):
         }
 
     def __str__(self):
-        return "Dna: name: %s, " \
-               "type: %s, " \
-               "author: %s, " \
-               "kalliope_supported_version: %s, " \
-               "tags: %s" % (self.name, self.module_type, self.author, self.kalliope_supported_version, self.tags)
+        return str(self.serialize())
 
     def __eq__(self, other):
         """

+ 1 - 3
kalliope/core/Models/Event.py

@@ -17,9 +17,7 @@ class Event(object):
         self.second = second
 
     def __str__(self):
-        return "%s:  year: %s, month: %s, day: %s, week: %s, day_of_week: %s, hour: %s, minute: %s, second: %s" \
-               % (self.__class__.__name__, self.year, self.month, self.day, self.week,
-                  self.day_of_week, self.hour, self.minute, self.second)
+        return str(self.serialize())
 
     def serialize(self):
         """

+ 55 - 0
kalliope/core/Models/MatchedSynapse.py

@@ -0,0 +1,55 @@
+import copy
+
+from kalliope.core.NeuronParameterLoader import NeuronParameterLoader
+
+
+class MatchedSynapse(object):
+    """
+    This class represent a synapse that has matched an order send by an User.
+    """
+
+    def __init__(self, matched_synapse=None, matched_order=None, user_order=None):
+        """
+        :param matched_synapse: The synapse that has matched in the brain.
+        :param matched_order: The order from the synapse that have matched.
+        :param user_order: The order said by the user.
+        """
+
+        # create a copy of the synapse. the received synapse come from the brain.
+        self.synapse = matched_synapse
+        # create a fifo list that contains all neurons to process.
+        # Create a copy to be sure when we remove a neuron from this list it will not be removed from the synapse's
+        # neuron list
+        self.neuron_fifo_list = copy.deepcopy(self.synapse.neurons)
+        self.matched_order = matched_order
+        self.parameters = dict()
+        if matched_order is not None:
+            self.parameters = NeuronParameterLoader.get_parameters(synapse_order=self.matched_order,
+                                                                   user_order=user_order)
+
+        # list of Neuron Module
+        self.neuron_module_list = list()
+
+    def __str__(self):
+        return str(self.serialize())
+
+    def serialize(self):
+        """
+        This method allows to serialize in a proper way this object
+
+        :return: A dict of name and parameters
+        :rtype: Dict
+        """
+        return {
+            'synapse_name': self.synapse.name,
+            'matched_order': self.matched_order,
+            'neuron_module_list': [e.serialize() for e in self.neuron_module_list]
+        }
+
+    def __eq__(self, other):
+        """
+        This is used to compare 2 objects
+        :param other:
+        :return:
+        """
+        return self.__dict__ == other.__dict__

+ 8 - 4
kalliope/core/Models/Neuron.py

@@ -27,16 +27,20 @@ class Neuron(object):
         the output of this parameter will be masked in order to not appears in clean in the console
         :return: string description of the neuron
         """
-        returned_string = str()
-        returned_string += "Neuron: name: %s" % self.name
+        returned_dict = {
+            'name': self.name,
+            'parameters': self.parameters
+        }
+
         cleaned_parameters = dict()
         for key, value in self.parameters.iteritems():
             if "password" in key:
                 cleaned_parameters[key] = "*****"
             else:
                 cleaned_parameters[key] = value
-        returned_string += ", parameters: %s" % cleaned_parameters
-        return returned_string
+        returned_dict["parameters"] = cleaned_parameters
+
+        return str(returned_dict)
 
     def __eq__(self, other):
         """

+ 1 - 1
kalliope/core/Models/Order.py

@@ -9,7 +9,7 @@ class Order(object):
         self.sentence = sentence
 
     def __str__(self):
-        return "%s: Sentence: %s" % (self.__class__.__name__, self.sentence)
+        return str(self.serialize())
 
     def serialize(self):
         """

+ 1 - 5
kalliope/core/Models/Resources.py

@@ -11,11 +11,7 @@ class Resources(object):
         self.trigger_folder = trigger_folder
 
     def __str__(self):
-        return "%s: neuron_folder: %s, stt_folder: %s, tts_folder: %s, trigger_folder: %s" % (self.__class__.__name__,
-                                                                                              self.neuron_folder,
-                                                                                              self.stt_folder,
-                                                                                              self.tts_folder,
-                                                                                              self.trigger_folder)
+        return str(self.serialize())
 
     def serialize(self):
         """

+ 24 - 9
kalliope/core/Models/RestAPI.py

@@ -3,7 +3,12 @@ class RestAPI(object):
     This Class is representing the rest API with all its configuration.
     """
 
-    def __init__(self, password_protected=None, login=None, password=None, active=None, port=None, allowed_cors_origin=None):
+    def __init__(self,
+                 password_protected=None,
+                 login=None, password=None,
+                 active=None,
+                 port=None,
+                 allowed_cors_origin=None):
         """
         :param password_protected: If true, the rest api will ask for an authentication
         :param login: login used if auth is activated
@@ -19,14 +24,24 @@ class RestAPI(object):
         self.allowed_cors_origin = allowed_cors_origin
 
     def __str__(self):
-        return "%s: RestAPI: password_protected: %s, login: %s, " \
-              "password: %s, active: %s, port: %s, allow_cors_request: %s" % (self.__class__.__name__,
-                                                       self.password_protected,
-                                                       self.login,
-                                                       self.password,
-                                                       self.active,
-                                                       self.port,
-                                                       self.allowed_cors_origin)
+        return str(self.serialize())
+
+    def serialize(self):
+        """
+        This method allows to serialize in a proper way this object
+
+        :return: A dict of order
+        :rtype: Dict
+        """
+
+        return {
+            'password_protected': self.password_protected,
+            'login': self.login,
+            'password': self.password,
+            'active': self.active,
+            'port': self.port,
+            'allowed_cors_origin': self.allowed_cors_origin
+        }
 
     def __eq__(self, other):
         """

+ 33 - 3
kalliope/core/Models/Settings.py

@@ -24,9 +24,7 @@ class Settings(object):
                  cache_path=None,
                  default_synapse=None,
                  resources=None,
-                 variables= None, # dict()
-                 machine=None,
-                 kalliope_version=None):
+                 variables=None):
 
         self.default_tts_name = default_tts_name
         self.default_stt_name = default_stt_name
@@ -47,6 +45,38 @@ class Settings(object):
         self.machine = platform.machine()   # can be x86_64 or armv7l
         self.kalliope_version = current_kalliope_version
 
+    def serialize(self):
+        """
+        This method allows to serialize in a proper way this object
+
+        :return: A dict of order
+        :rtype: Dict
+        """
+
+        return {
+            'default_tts_name': self.default_tts_name,
+            'default_stt_name': self.default_stt_name,
+            'default_trigger_name': self.default_trigger_name,
+            'ttss': self.ttss,
+            'stts': self.stts,
+            'random_wake_up_answers': self.random_wake_up_answers,
+            'random_wake_up_sounds': self.random_wake_up_sounds,
+            'play_on_ready_notification': self.play_on_ready_notification,
+            'on_ready_answers': self.on_ready_answers,
+            'on_ready_sounds': self.on_ready_sounds,
+            'triggers': self.triggers,
+            'rest_api': self.rest_api.serialize(),
+            'cache_path': self.cache_path,
+            'default_synapse': self.default_synapse,
+            'resources': self.resources,
+            'variables': self.variables,
+            'machine': self.machine,
+            'kalliope_version': self.kalliope_version
+        }
+
+    def __str__(self):
+        return str(self.serialize())
+
     def __eq__(self, other):
         """
         This is used to compare 2 objects

+ 7 - 1
kalliope/core/Models/Stt.py

@@ -10,7 +10,13 @@ class Stt(object):
         self.parameters = parameters
 
     def __str__(self):
-        return "Stt name: %s, parameters: %s" % (str(self.name), str(self.parameters))
+        return str(self.serialize())
+
+    def serialize(self):
+        return {
+            'name': self.name,
+            'parameters': self.parameters
+        }
 
     def __eq__(self, other):
         """

+ 1 - 8
kalliope/core/Models/Synapse.py

@@ -25,14 +25,7 @@ class Synapse(object):
         }
 
     def __str__(self):
-        return_val = "Synapse name: %s" % self.name
-        return_val += "\nneurons:"
-        for el in self.neurons:
-            return_val += str(el)
-        return_val += "\nsignals:"
-        for el in self.signals:
-            return_val += str(el)
-        return return_val
+        return str(self.serialize())
 
     def __eq__(self, other):
         """

+ 7 - 1
kalliope/core/Models/Trigger.py

@@ -10,7 +10,13 @@ class Trigger(object):
         self.parameters = parameters
 
     def __str__(self):
-        return "Trigger name: %s, parameters: %s" % (str(self.name), str(self.parameters))
+        return str(self.serialize())
+
+    def serialize(self):
+        return {
+            'name': self.name,
+            'parameters': self.parameters
+        }
 
     def __eq__(self, other):
         """

+ 7 - 1
kalliope/core/Models/Tts.py

@@ -10,7 +10,13 @@ class Tts(object):
         self.parameters = parameters
 
     def __str__(self):
-        return "Tts name: %s, parameters: %s" % (str(self.name), str(self.parameters))
+        return str(self.serialize())
+
+    def serialize(self):
+        return {
+            'name': self.name,
+            'parameters': self.parameters
+        }
 
     def __eq__(self, other):
         """

+ 28 - 32
kalliope/core/NeuronLauncher.py

@@ -13,7 +13,7 @@ class NeuronLauncher:
         pass
 
     @classmethod
-    def start_neuron(cls, neuron):
+    def launch_neuron(cls, neuron):
         """
         Start a neuron plugin
         :param neuron: neuron object
@@ -33,46 +33,42 @@ class NeuronLauncher:
                                                      resources_dir=neuron_folder)
 
     @classmethod
-    def start_neuron_list(cls, neuron_list, parameters_dict=None):
+    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_list: list of Neuron object to run
+        :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)
         """
-
-        instantiated_neuron = list()
-
-        for neuron in neuron_list:
-            problem_in_neuron_found = False
-            if isinstance(neuron.parameters, dict):
-                # print neuron.parameters
-                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)
-                    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)
+        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)
+                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)
 
             # if no error detected, we run the neuron
             if not problem_in_neuron_found:
-                instantiated_neuron.append(cls.start_neuron(neuron))
+                instantiated_neuron = cls.launch_neuron(neuron)
             else:
                 Utils.print_danger("A problem has been found in the Synapse.")
 

+ 40 - 36
kalliope/core/NeuronModule.py

@@ -8,6 +8,7 @@ from jinja2 import Template
 from kalliope.core import OrderListener
 from kalliope.core.ConfigurationManager import SettingLoader, BrainLoader
 from kalliope.core.Models import Order
+from kalliope.core.Models.MatchedSynapse import MatchedSynapse
 from kalliope.core.NeuronLauncher import NeuronLauncher
 from kalliope.core.NeuronParameterLoader import NeuronParameterLoader
 from kalliope.core.OrderAnalyser import OrderAnalyser
@@ -89,6 +90,31 @@ class NeuronModule(object):
         self.say_template = kwargs.get('say_template', None)
         # check if there is a template file associate to the output message
         self.file_template = kwargs.get('file_template', None)
+        # keep the generated message
+        self.tts_message = None
+        # if the current call is api one
+        self.is_api_call = kwargs.get('is_api_call', False)
+        # boolean to know id the synapse is waiting for an answer
+        self.is_waiting_for_answer = False
+        # the synapse name to add the the buffer
+        self.pending_synapse = None
+
+    def __str__(self):
+        retuned_string = ""
+        retuned_string += self.tts_message
+        return retuned_string
+
+    def serialize(self):
+        """
+        This method allows to serialize in a proper way this object
+
+        :return: A dict of name and parameters
+        :rtype: Dict
+        """
+        return {
+            'neuron_name': self.neuron_name,
+            'generated_message': self.tts_message
+        }
 
     def say(self, message):
         """
@@ -119,6 +145,8 @@ class NeuronModule(object):
 
         if tts_message is not None:
             logger.debug("tts_message to say: %s" % tts_message)
+            self.tts_message = tts_message
+            Utils.print_success(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)
@@ -186,48 +214,24 @@ class NeuronModule(object):
 
         return returned_message
 
-    def run_synapse_by_name(self, name):
-        SynapseLauncher.start_synapse(name=name, brain=self.brain)
+    def run_synapse_by_name(self, synapse_name, user_order=None, synapse_order=None):
+        """
+        call the lifo for adding a synapse to execute in the list of synapse list to process
+        :param synapse_name: The name of the synapse to run
+        :param user_order: The user order
+        :param synapse_order: The synapse order
+        """
+        synapse = BrainLoader().get_brain().get_synapse_by_name(synapse_name)
+        matched_synapse = MatchedSynapse(matched_synapse=synapse,
+                                         matched_order=synapse_order,
+                                         user_order=user_order)
+        self.pending_synapse = matched_synapse
 
     @staticmethod
     def is_order_matching(order_said, order_match):
         return OrderAnalyser().spelt_order_match_brain_order_via_table(order_to_analyse=order_match,
                                                                        user_said=order_said)
 
-    def run_synapse_by_name_with_order(self, order, synapse_name, order_template):
-        """
-        Run a synapse using its name, and giving an order so it can retrieve its params.
-        Useful for neurotransmitters.
-        :param order: the order to match
-        :param synapse_name: the name of the synapse
-        :param order_template: order_template coming from the neurotransmitter
-        :return: True if a synapse as been found and started using its params
-        """
-        synapse_to_run = self.brain.get_synapse_by_name(synapse_name=synapse_name)
-        if synapse_to_run:
-            # Make a list with the synapse
-            logger.debug("[run_synapse_by_name_with_order]-> a synapse has been found  %s" % synapse_to_run.name)
-            list_to_run = list()
-            list_to_run.append(synapse_to_run)
-
-            # load parameters from the answer
-            parameters = None
-            for signal in synapse_to_run.signals:
-                if isinstance(signal, Order):
-                    parameters = NeuronParameterLoader.get_parameters(synapse_order=order_template,
-                                                                      user_order=order)
-                    if parameters is not None:
-                        logger.debug("[NeuronModule]-> parameter load from user answer: %s" % parameters)
-                        break
-
-            # start the neuron list
-            NeuronLauncher.start_neuron_list(neuron_list=synapse_to_run.neurons, parameters_dict=parameters)
-
-        else:
-            logger.debug("[NeuronModule]-> run_synapse_by_name_with_order, the synapse has not been found : %s"
-                         % synapse_name)
-        return synapse_to_run
-
     @staticmethod
     def _get_content_of_file(real_file_template_path):
         """

+ 11 - 1
kalliope/core/OrderAnalyser.py

@@ -2,6 +2,7 @@
 import collections
 from collections import Counter
 
+from kalliope.core.Models.MatchedSynapse import MatchedSynapse
 from kalliope.core.Utils.Utils import Utils
 from kalliope.core.ConfigurationManager import SettingLoader
 from kalliope.core.Models import Order
@@ -53,7 +54,16 @@ class OrderAnalyser:
                         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.sentence))
-        return list_match_synapse
+
+        # 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
 
     @classmethod
     def spelt_order_match_brain_order_via_table(cls, order_to_analyse, user_said):

+ 32 - 27
kalliope/core/RestAPI/FlaskAPI.py

@@ -4,9 +4,11 @@ import threading
 
 import time
 
+from kalliope.core.LIFOBuffer import LIFOBuffer
+from kalliope.core.Models.MatchedSynapse import MatchedSynapse
 from kalliope.core.Utils.FileManager import FileManager
 
-from kalliope.core.ConfigurationManager import SettingLoader
+from kalliope.core.ConfigurationManager import SettingLoader, BrainLoader
 from kalliope.core.OrderListener import OrderListener
 from werkzeug.utils import secure_filename
 
@@ -45,8 +47,8 @@ class FlaskAPI(threading.Thread):
         sl = SettingLoader()
         self.settings = sl.settings
 
-        # list of launched synapse by the Order Analyser when using the /synapses/start/audio URL
-        self.launched_synapses = None
+        # api_response sent by the Order Analyser when using the /synapses/start/audio URL
+        self.api_response = None
         # boolean used to notify the main process that we get the list of returned synapse
         self.order_analyser_return = False
 
@@ -136,18 +138,25 @@ class FlaskAPI(threading.Thread):
         :param synapse_name:
         :return:
         """
-        synapse_target = self._get_synapse_by_name(synapse_name)
+        # get a synapse object from the name
+        synapse_target = BrainLoader().get_brain().get_synapse_by_name(synapse_name=synapse_name)
 
         if synapse_target is None:
             data = {
                 "synapse name not found": "%s" % synapse_name
             }
             return jsonify(error=data), 404
-
-        # run the synapse
-        SynapseLauncher.start_synapse(synapse_name, brain=self.brain)
-        data = jsonify(synapses=synapse_target.serialize())
-        return data, 201
+        else:
+            # generate a MatchedSynapse from the synapse
+            matched_synapse = MatchedSynapse(matched_synapse=synapse_target)
+            # get the current LIFO buffer
+            lifo_buffer = LIFOBuffer()
+            # this is a new call we clean up the LIFO
+            lifo_buffer.clean()
+            lifo_buffer.add_synapse_list_to_lifo([matched_synapse])
+            response = lifo_buffer.execute(is_api_call=True)
+            data = jsonify(response)
+            return data, 201
 
     @requires_auth
     def run_synapse_by_order(self):
@@ -169,20 +178,13 @@ class FlaskAPI(threading.Thread):
             # get the order
             order_to_run = order["order"]
 
-            launched_synapses = SynapseLauncher.run_matching_synapse_or_default(order_to_run,
-                                                                                self.brain,
-                                                                                self.settings)
-
-            if launched_synapses:
-                # if the list is not empty, we have launched one or more synapses
-                data = jsonify(synapses=[e.serialize() for e in launched_synapses])
-                return data, 201
-            else:
-                data = {
-                    "error": "The given order doesn't match any synapses"
-                }
-                return jsonify(error=data), 400
+            api_response = SynapseLauncher.run_matching_synapse_from_order(order_to_run,
+                                                                           self.brain,
+                                                                           self.settings,
+                                                                           is_api_call=True)
 
+            data = jsonify(api_response)
+            return data, 201
         else:
             data = {
                 "error": "order cannot be null"
@@ -226,9 +228,9 @@ class FlaskAPI(threading.Thread):
             while not self.order_analyser_return:
                 time.sleep(0.1)
             self.order_analyser_return = False
-            if self.launched_synapses is not None and self.launched_synapses:
-                data = jsonify(synapses=[e.serialize() for e in self.launched_synapses])
-                self.launched_synapses = None
+            if self.api_response is not None and self.api_response:
+                data = jsonify(self.api_response)
+                self.api_response = None
                 return data, 201
             else:
                 data = {
@@ -256,8 +258,11 @@ class FlaskAPI(threading.Thread):
         :return:
         """
         logger.debug("order to process %s" % order)
-        list_launched_synapse = SynapseLauncher.run_matching_synapse_or_default(order, self.brain, self.settings)
-        self.launched_synapses = list_launched_synapse
+        api_response = SynapseLauncher.run_matching_synapse_from_order(order,
+                                                                       self.brain,
+                                                                       self.settings,
+                                                                       is_api_call=True)
+        self.api_response = api_response
 
         # this boolean will notify the main process that the order have been processed
         self.order_analyser_return = True

+ 1 - 1
kalliope/core/ShellGui.py

@@ -193,5 +193,5 @@ class ShellGui:
             self.show_main_menu()
         if code == self.d.OK:
             logger.debug("Run synapse from GUI: %s" % tag)
-            SynapseLauncher.start_synapse(tag, brain=self.brain)
+            SynapseLauncher.start_synapse_by_name(tag, brain=self.brain)
             self.show_synapses_test_menu()

+ 50 - 56
kalliope/core/SynapseLauncher.py

@@ -1,7 +1,9 @@
 import logging
 
+from kalliope.core.ConfigurationManager import BrainLoader
+from kalliope.core.LIFOBuffer import LIFOBuffer
+from kalliope.core.Models.MatchedSynapse import MatchedSynapse
 from kalliope.core.NeuronLauncher import NeuronLauncher
-from kalliope.core.NeuronParameterLoader import NeuronParameterLoader
 from kalliope.core.OrderAnalyser import OrderAnalyser
 
 
@@ -20,75 +22,67 @@ class SynapseNameNotFound(Exception):
 
 class SynapseLauncher(object):
 
-    def __init__(self):
-        pass
-
     @classmethod
-    def start_synapse(cls, name, brain=None):
+    def start_synapse_by_name(cls, name, brain=None):
         """
         Start a synapse by it's name
         :param name: Name (Unique ID) of the synapse to launch
         :param brain: Brain instance
         """
-
+        logger.debug("[SynapseLauncher] start_synapse_by_name called with synapse name: %s " % name)
         # check if we have found and launched the synapse
         synapse = brain.get_synapse_by_name(synapse_name=name)
 
         if not synapse:
             raise SynapseNameNotFound("The synapse name \"%s\" does not exist in the brain file" % name)
         else:
-            cls._run_synapse(synapse=synapse)
-            return synapse
+            # get our singleton LIFO
+            lifo_buffer = LIFOBuffer()
+            list_synapse_to_process = list()
+            new_matching_synapse = MatchedSynapse(matched_synapse=synapse,
+                                                  matched_order=None,
+                                                  user_order=None)
+            list_synapse_to_process.append(new_matching_synapse)
+            lifo_buffer.add_synapse_list_to_lifo(list_synapse_to_process)
+            return lifo_buffer.execute(is_api_call=True)
 
     @classmethod
-    def _run_synapse(cls, synapse):
-        """
-        Start all neurons in the synapse
-        :param synapse: Synapse for which we run neurons
-        :return:
+    def run_matching_synapse_from_order(cls, order_to_process, brain, settings, is_api_call=False):
         """
-        for neuron in synapse.neurons:
-            NeuronLauncher.start_neuron(neuron)
-        return True
-
-    @classmethod
-    def run_matching_synapse_or_default(cls, order_to_process, brain, settings):
+        
+        :param order_to_process: the spoken order sent by the user
+        :param brain: Brain object
+        :param settings: Settings object
+        :param is_api_call: if True, the current call come from the API. This info must be known by launched Neuron
+        :return: list of matched synapse
         """
-        This method will run all synapse that match the given order "order_to_process"
-        :param order_to_process: The text order to process in the order analyser
-        :param brain: Brain instance
-        :param settings: Settings instance
-        :return: Return a list of launched synapse
-        """
-        no_synapse_match = False
-        # create a list of launched synapse to return
-        launched_synapses = list()
-        if order_to_process is not None:  # maybe we have received a null audio from STT engine
-            launched_synapses_tuple = OrderAnalyser.get_matching_synapse(order=order_to_process, brain=brain)
-
-            # oa contains the list Named tuple of synapse to run with the associated order that has matched
-            # for each synapse, get neurons, et for each neuron, get parameters
-            if not launched_synapses_tuple:
-                no_synapse_match = True
-            else:
-                # the order match one or more synapses
-                for tuple_el in launched_synapses_tuple:
-                    launched_synapses.append(tuple_el.synapse)
-                    logger.debug("[SynapseLauncher] Get parameter for %s " % tuple_el.synapse.name)
-                    parameters = NeuronParameterLoader.get_parameters(synapse_order=tuple_el.order,
-                                                                      user_order=order_to_process)
-                    # start the neuron list
-                    NeuronLauncher.start_neuron_list(neuron_list=tuple_el.synapse.neurons,
-                                                     parameters_dict=parameters)
-        else:
-            no_synapse_match = True
-
-        if no_synapse_match:  # then run the default synapse
-            if settings.default_synapse is not None:
-                logger.debug("[SynapseLauncher] No matching Synapse-> running default synapse ")
-                synapses = SynapseLauncher.start_synapse(name=settings.default_synapse,
-                                                         brain=brain)
-                launched_synapses.append(synapses)
 
-        # return the launched synapse list
-        return launched_synapses
+        # get our singleton LIFO
+        lifo_buffer = LIFOBuffer()
+
+        # if the LIFO is not empty, so, the current order is passed to the current processing synapse as an answer
+        if len(lifo_buffer.lifo_list) > 0:
+            # the LIFO is not empty, this is an answer to a previous call
+            return lifo_buffer.execute(answer=order_to_process, is_api_call=is_api_call)
+
+        else:  # the LIFO is empty, this is a new call
+            # get a list of matched synapse from the order
+            list_synapse_to_process = OrderAnalyser.get_matching_synapse(order=order_to_process, brain=brain)
+
+            if not list_synapse_to_process:  # the order analyser returned us an empty list
+                # add the default synapse if exist into the lifo
+                if settings.default_synapse:
+                    logger.debug("[SynapseLauncher] No matching Synapse-> running default synapse ")
+                    # get the default synapse
+                    default_synapse = brain.get_synapse_by_name(settings.default_synapse)
+                    new_matching_synapse = MatchedSynapse(matched_synapse=default_synapse,
+                                                          matched_order=None,
+                                                          user_order=order_to_process)
+                    list_synapse_to_process.append(new_matching_synapse)
+                else:
+                    logger.debug("[SynapseLauncher] No matching Synapse and no default synapse ")
+
+            lifo_buffer.add_synapse_list_to_lifo(list_synapse_to_process)
+            lifo_buffer.api_response.user_order = order_to_process
+
+            return lifo_buffer.execute(is_api_call=is_api_call)

+ 0 - 27
kalliope/core/TTS/TTSLauncher.py

@@ -1,27 +0,0 @@
-import logging
-
-from kalliope.core import Utils
-
-logging.basicConfig()
-logger = logging.getLogger("kalliope")
-
-
-class TTSLauncher(object):
-    def __init__(self):
-        pass
-
-    @classmethod
-    def get_tts(cls, tts):
-        """
-        Return an instance of a TTS module from the name of this module
-        :param tts: TTS model
-        :type tts: Tts
-        :return: TTS module instance
-
-        .. seealso::  TTS
-        .. warnings:: Class Method and Public
-        """
-        logger.debug("get TTS module \"%s\" with parameters %s" % (tts.name, tts.parameters))
-        return Utils.get_dynamic_class_instantiation(package_name="tts",
-                                                     module_name=tts.name,
-                                                     parameters=tts.parameters)

+ 1 - 0
kalliope/core/TTS/__init__.py

@@ -0,0 +1 @@
+from TTSModule import TTSModule

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

@@ -87,7 +87,8 @@ class Utils(object):
         :return:
         """
         import json
-        pipe_print(json.dumps(to_print, indent=2))
+        line = json.dumps(to_print, indent=2)
+        return line.encode('utf-8')
 
     ##################
     #

+ 3 - 3
kalliope/core/__init__.py

@@ -6,6 +6,6 @@ from kalliope.core.Utils import FileManager
 from kalliope.core.ResourcesManager import ResourcesManager
 from kalliope.core.NeuronLauncher import NeuronLauncher
 from kalliope.core.SynapseLauncher import SynapseLauncher
-
-
-
+from kalliope.core.LIFOBuffer import LIFOBuffer
+from kalliope.core.NeuronParameterLoader import NeuronParameterLoader
+from kalliope.core.NeuronModule import NeuronModule

+ 14 - 5
kalliope/neurons/neurotransmitter/neurotransmitter.py

@@ -14,6 +14,8 @@ class Neurotransmitter(NeuronModule):
         self.from_answer_link = kwargs.get('from_answer_link', None)
         self.default = kwargs.get('default', None)
         self.direct_link = kwargs.get('direct_link', None)
+        self.is_api_call = kwargs.get('is_api_call', False)
+        self.answer = kwargs.get('answer', None)
 
         # do some check
         if self._is_parameters_ok():
@@ -21,8 +23,14 @@ class Neurotransmitter(NeuronModule):
                 logger.debug("Neurotransmitter directly call to the synapse name: %s" % self.direct_link)
                 self.run_synapse_by_name(self.direct_link)
             else:
-                # the user is using a from_answer_link, we call the stt to get an audio
-                self.get_audio_from_stt(callback=self.callback)
+                if self.is_api_call:
+                    if self.answer is not None:
+                        self.callback(self.answer)
+                    else:
+                        self.is_waiting_for_answer = True
+                else:
+                    # the user is using a from_answer_link, we call the stt to get an audio
+                    self.get_audio_from_stt(callback=self.callback)
 
     def callback(self, audio):
         """
@@ -41,9 +49,10 @@ class Neurotransmitter(NeuronModule):
                 for answer in el["answers"]:
                     if self.is_order_matching(audio, answer):
                         logger.debug("Neurotransmitter: match answer: %s" % answer)
-                        found = self.run_synapse_by_name_with_order(order=audio,
-                                                                    synapse_name=el["synapse"],
-                                                                    order_template=answer)
+                        self.run_synapse_by_name(synapse_name=el["synapse"],
+                                                 user_order=audio,
+                                                 synapse_order=answer)
+                        found = True
                         break
             if not found:  # the answer do not correspond to any answer. We run the default synapse
                 self.run_synapse_by_name(self.default)

+ 23 - 29
kalliope/neurons/neurotransmitter/tests/test_neurotransmitter.py

@@ -2,7 +2,7 @@ import unittest
 
 from mock import mock
 
-from kalliope.core.NeuronModule import NeuronModule, MissingParameterException, InvalidParameterException
+from kalliope.core.NeuronModule import MissingParameterException, InvalidParameterException
 from kalliope.neurons.neurotransmitter import Neurotransmitter
 
 
@@ -31,23 +31,23 @@ class TestNeurotransmitter(unittest.TestCase):
         """
         Testing the Parameters checking
         """
-        def run_test_InvalidParameterException(parameters_to_test):
+        def run_test_invalid_parameter_exception(parameters_to_test):
             with self.assertRaises(InvalidParameterException):
                 Neurotransmitter(**parameters_to_test)
 
-        def run_test_MissingParameterException(parameters_to_test):
+        def run_test_missing_parameter_exception(parameters_to_test):
             with self.assertRaises(MissingParameterException):
                 Neurotransmitter(**parameters_to_test)
 
         # empty
         parameters = dict()
-        run_test_MissingParameterException(parameters)
+        run_test_missing_parameter_exception(parameters)
 
         # missing direct_link and from_answer_link
         parameters = {
             "default": self.default
         }
-        run_test_MissingParameterException(parameters)
+        run_test_missing_parameter_exception(parameters)
 
         # missing direct_link and from_answer_link
         parameters = {
@@ -55,14 +55,14 @@ class TestNeurotransmitter(unittest.TestCase):
             "from_answer_link": self.from_answer_link,
             "direct_link": self.direct_link
         }
-        run_test_InvalidParameterException(parameters)
+        run_test_invalid_parameter_exception(parameters)
 
         # missing default
         parameters = {
             "from_answer_link": self.from_answer_link,
             "direct_link": self.direct_link
         }
-        run_test_InvalidParameterException(parameters)
+        run_test_invalid_parameter_exception(parameters)
 
         # Missing answer in from_answer_link
         self.from_answer_link = [
@@ -75,7 +75,7 @@ class TestNeurotransmitter(unittest.TestCase):
             "default": self.default,
             "from_answer_link": self.from_answer_link
         }
-        run_test_MissingParameterException(parameters)
+        run_test_missing_parameter_exception(parameters)
 
         # Missing synapse in from_answer_link
         self.from_answer_link = [
@@ -88,7 +88,7 @@ class TestNeurotransmitter(unittest.TestCase):
             "default": self.default,
             "from_answer_link": self.from_answer_link
         }
-        run_test_MissingParameterException(parameters)
+        run_test_missing_parameter_exception(parameters)
 
     def testCallback(self):
         """
@@ -98,57 +98,51 @@ class TestNeurotransmitter(unittest.TestCase):
             "default": self.default,
             "from_answer_link": self.from_answer_link
         }
-        with mock.patch.object(NeuronModule, 'get_audio_from_stt', create=True) as mock_get_audio_from_stt:
-            with mock.patch.object(NeuronModule, 'run_synapse_by_name', create=True) as mock_run_synapse_by_name:
+        with mock.patch("kalliope.core.NeuronModule.get_audio_from_stt") as mock_get_audio_from_stt:
+            with mock.patch("kalliope.core.NeuronModule.run_synapse_by_name") as mock_run_synapse_by_name:
                 # testing running the default when no order matching
                 nt = Neurotransmitter(**parameters)
                 mock_get_audio_from_stt.assert_called_once()
                 mock_get_audio_from_stt.reset_mock()
+
                 # testing running the default when audio None
                 audio_text = None
                 nt.callback(audio=audio_text)
                 mock_run_synapse_by_name.assert_called_once_with(self.default)
                 mock_run_synapse_by_name.reset_mock()
+
                 # testing running the default when no order matching
                 audio_text = "try test audio "
                 nt.callback(audio=audio_text)
                 mock_run_synapse_by_name.assert_called_once_with(self.default)
                 mock_run_synapse_by_name.reset_mock()
 
-                with mock.patch.object(NeuronModule,
-                                       'run_synapse_by_name_with_order',
-                                       create=True) as mock_run_synapse_by_name_with_order:
-
-                    audio_text="answer one"
-                    nt.callback(audio=audio_text)
-                    mock_run_synapse_by_name_with_order.assert_called_once_with(order=audio_text,
-                                                                                synapse_name="synapse2",
-                                                                                order_template="answer one")
+                # Testing calling the right synapse
+                audio_text = "answer one"
+                nt.callback(audio=audio_text)
+                mock_run_synapse_by_name.assert_called_once_with(synapse_name="synapse2",
+                                                                 user_order=audio_text,
+                                                                 synapse_order="answer one")
 
     def testInit(self):
         """
         Testing the init method of the neurontransmitter.
         """
 
-        with mock.patch.object(NeuronModule, 'run_synapse_by_name', create=True) as mock_run_synapse_by_name:
+        with mock.patch("kalliope.core.NeuronModule.run_synapse_by_name") as mock_run_synapse_by_name:
             # Test direct link
             parameters = {
                 "default": self.default,
                 "direct_link": self.direct_link
             }
-            nt = Neurotransmitter(**parameters)
+            Neurotransmitter(**parameters)
             mock_run_synapse_by_name.assert_called_once_with(self.direct_link)
 
-        with mock.patch.object(NeuronModule, 'get_audio_from_stt', create=True) as mock_get_audio_from_stt:
+        with mock.patch("kalliope.core.NeuronModule.get_audio_from_stt") as mock_get_audio_from_stt:
             # Test get_audio_from_stt
             parameters = {
                 "default": self.default,
                 "from_answer_link": self.from_answer_link,
             }
-            nt = Neurotransmitter(**parameters)
+            Neurotransmitter(**parameters)
             mock_get_audio_from_stt.assert_called_once()
-
-
-
-
-