Эх сурвалжийг харах

Merge pull request #149 from kalliope-project/resource-directory

Resource directory
Nicolas Marcq 8 жил өмнө
parent
commit
b6244a010a
84 өөрчлөгдсөн 974 нэмэгдсэн , 1584 устгасан
  1. 28 18
      Docs/neuron_list.md
  2. 5 0
      Docs/neuron_template.md
  3. 18 0
      Docs/neurons.md
  4. 19 0
      Docs/settings.md
  5. 9 0
      Tests/settings/settings_test.yml
  6. 33 11
      Tests/test_launchers.py
  7. 32 15
      Tests/test_order_analyser.py
  8. 41 2
      Tests/test_settings_loader.py
  9. 12 3
      Tests/test_utils.py
  10. 0 1
      install/files/deb-packages_requirements.txt
  11. 2 8
      install/files/python_requirements.txt
  12. 13 2
      kalliope/__init__.py
  13. 1 1
      kalliope/_version.py
  14. 21 6
      kalliope/core/ConfigurationManager/ConfigurationChecker.py
  15. 61 1
      kalliope/core/ConfigurationManager/SettingLoader.py
  16. 41 0
      kalliope/core/Models/Resources.py
  17. 2 0
      kalliope/core/Models/Settings.py
  18. 1 0
      kalliope/core/Models/__init__.py
  19. 10 3
      kalliope/core/NeuronLauncher.py
  20. 3 2
      kalliope/core/NeuronModule.py
  21. 42 21
      kalliope/core/OrderAnalyser.py
  22. 2 2
      kalliope/core/OrderListener.py
  23. 266 0
      kalliope/core/ResourcesManager.py
  24. 3 1
      kalliope/core/TTS/TTSLauncher.py
  25. 3 3
      kalliope/core/TriggerLauncher.py
  26. 19 7
      kalliope/core/Utils/Utils.py
  27. 42 15
      kalliope/neurons/ansible_playbook/README.md
  28. 40 8
      kalliope/neurons/ansible_playbook/ansible_playbook.py
  29. 0 68
      kalliope/neurons/gmail_checker/README.md
  30. 0 1
      kalliope/neurons/gmail_checker/__init__.py
  31. 0 92
      kalliope/neurons/gmail_checker/gmail_checker.py
  32. 0 0
      kalliope/neurons/gmail_checker/tests/__init__.py
  33. 0 37
      kalliope/neurons/gmail_checker/tests/test_gmail_checker.py
  34. 4 0
      kalliope/neurons/kill_switch/README.md
  35. 4 0
      kalliope/neurons/neurotransmitter/README.md
  36. 8 6
      kalliope/neurons/neurotransmitter/neurotransmitter.py
  37. 154 0
      kalliope/neurons/neurotransmitter/tests/test_neurotransmitter.py
  38. 0 93
      kalliope/neurons/openweathermap/README.md
  39. 0 1
      kalliope/neurons/openweathermap/__init__.py
  40. 0 127
      kalliope/neurons/openweathermap/openweathermap.py
  41. 0 0
      kalliope/neurons/openweathermap/tests/__init__.py
  42. 0 36
      kalliope/neurons/openweathermap/tests/test_openweathermap.py
  43. 0 49
      kalliope/neurons/push_message/Readme.md
  44. 0 2
      kalliope/neurons/push_message/__init__.py
  45. 0 43
      kalliope/neurons/push_message/push_message.py
  46. 0 0
      kalliope/neurons/push_message/tests/__init__.py
  47. 0 46
      kalliope/neurons/push_message/tests/test_push_message.py
  48. 0 59
      kalliope/neurons/rss_reader/README.md
  49. 0 1
      kalliope/neurons/rss_reader/__init__.py
  50. 0 45
      kalliope/neurons/rss_reader/rss_reader.py
  51. 0 0
      kalliope/neurons/rss_reader/tests/__init__.py
  52. 0 23
      kalliope/neurons/rss_reader/tests/test_rss_reader.py
  53. 4 0
      kalliope/neurons/say/README.md
  54. 4 0
      kalliope/neurons/script/README.md
  55. 3 0
      kalliope/neurons/shell/README.md
  56. 4 0
      kalliope/neurons/sleep/README.md
  57. 4 0
      kalliope/neurons/systemdate/README.md
  58. 0 83
      kalliope/neurons/tasker_autoremote/Readme.md
  59. 0 1
      kalliope/neurons/tasker_autoremote/__init__.py
  60. BIN
      kalliope/neurons/tasker_autoremote/images/profile_auto_remote.png
  61. BIN
      kalliope/neurons/tasker_autoremote/images/profile_display_unlocked.png
  62. BIN
      kalliope/neurons/tasker_autoremote/images/task_play_music.png
  63. BIN
      kalliope/neurons/tasker_autoremote/images/task_stop_music.png
  64. 0 41
      kalliope/neurons/tasker_autoremote/tasker_autoremote.py
  65. 0 0
      kalliope/neurons/tasker_autoremote/tests/__init__.py
  66. 0 36
      kalliope/neurons/tasker_autoremote/tests/test_tasker_autoremote.py
  67. 0 51
      kalliope/neurons/twitter/README.md
  68. 0 1
      kalliope/neurons/twitter/__init__.py
  69. 0 0
      kalliope/neurons/twitter/tests/__init__.py
  70. 0 68
      kalliope/neurons/twitter/tests/test_twitter_neuron.py
  71. 0 53
      kalliope/neurons/twitter/twitter.py
  72. 4 0
      kalliope/neurons/uri/README.md
  73. 0 50
      kalliope/neurons/wake_on_lan/README.md
  74. 0 1
      kalliope/neurons/wake_on_lan/__init__.py
  75. 0 0
      kalliope/neurons/wake_on_lan/tests/__init__.py
  76. 0 67
      kalliope/neurons/wake_on_lan/tests/test_wake_on_lan.py
  77. 0 47
      kalliope/neurons/wake_on_lan/wake_on_lan.py
  78. 0 69
      kalliope/neurons/wikipedia_searcher/README.md
  79. 0 1
      kalliope/neurons/wikipedia_searcher/__init__.py
  80. 0 0
      kalliope/neurons/wikipedia_searcher/tests/__init__.py
  81. 0 67
      kalliope/neurons/wikipedia_searcher/tests/test_wikipedia_searcher.py
  82. 0 80
      kalliope/neurons/wikipedia_searcher/wikipedia_searcher.py
  83. 9 0
      kalliope/settings.yml
  84. 3 9
      setup.py

+ 28 - 18
Docs/neuron_list.md

@@ -2,22 +2,32 @@
 
 
 A neuron is a module that will perform some actions attached to an order. You can use it in your synapses. See the [complete neuron documentation](neurons.md) for more information.
 A neuron is a module that will perform some actions attached to an order. You can use it in your synapses. See the [complete neuron documentation](neurons.md) for more information.
 
 
-| Name                                                          | Description                                                                             |
-|---------------------------------------------------------------|-----------------------------------------------------------------------------------------|
-| [ansible_playbook](../kalliope/neurons/ansible_playbook/)     | Run an ansible playbook                                                                 |
-| [gmail_checker](../kalliope/neurons/gmail_checker/)           | Get the number of unread email and their subjects from a gmail account                  |
-| [kill_switch](../kalliope/neurons/kill_switch/)               | Stop Kalliope process                                                                   |
-| [neurotransmitter](../kalliope/neurons/neurotransmitter/)     | Link synapse together                                                                   |
-| [push_message](../kalliope/neurons/push_message/)             | Send a push message to a remote device like Android/iOS/Windows Phone or Chrome browser |
-| [rss_reader](../kalliope/neurons/rss_reader/)                 | get rss feed from website                                                               |
-| [say](../kalliope/neurons/say/)                               | Make Kalliope talk by using TTS                                                         |
-| [script](../kalliope/neurons/script/)                         | Run an executable script                                                                |
-| [shell](../kalliope/neurons/shell/)                           | Run a shell command                                                                     |
-| [sleep](../kalliope/neurons/sleep/)                           | Make Kalliope sleep for a while before continuing                                       |
-| [systemdate](../kalliope/neurons/systemdate/)                 | Give the local system date and time                                                     |
-| [tasker_autoremote](../kalliope/neurons/tasker_autoremote/)   | Send a message to Android tasker app                                                    |
-| [twitter](../kalliope/neurons/twitter/)                       | Send a Twit from kalliope                                                               |
-| [uri](../kalliope/neurons/uri/)                               | Interacts with HTTP and HTTPS web services.                                             |
-| [wake_on_lan](../kalliope/neurons/wake_on_lan/)               | Wake on lan a computer                                                                  |
-| [wikipedia_searcher](../kalliope/neurons/wikipedia_searcher/) | Search for a page on Wikipedia                                                          |
+## Core neuron
 
 
+| Name                                                      | Description                                       |
+|-----------------------------------------------------------|---------------------------------------------------|
+| [ansible_playbook](../kalliope/neurons/ansible_playbook/) | Run an ansible playbook                           |
+| [kill_switch](../kalliope/neurons/kill_switch/)           | Stop Kalliope process                             |
+| [neurotransmitter](../kalliope/neurons/neurotransmitter/) | Link synapse together                             |
+| [say](../kalliope/neurons/say/)                           | Make Kalliope talk by using TTS                   |
+| [script](../kalliope/neurons/script/)                     | Run an executable script                          |
+| [shell](../kalliope/neurons/shell/)                       | Run a shell command                               |
+| [sleep](../kalliope/neurons/sleep/)                       | Make Kalliope sleep for a while before continuing |
+| [systemdate](../kalliope/neurons/systemdate/)             | Give the local system date and time               |
+| [uri](../kalliope/neurons/uri/)                           | Interacts with HTTP and HTTPS web services.       |
+
+## Community neuron
+
+| Name                                                                           | Description                                                                             |
+|--------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------|
+| [gmail_checker](https://github.com/kalliope-project/kalliope_neuron_gmail)     | Get the number of unread email and their subjects from a gmail account                  |
+| [pushetta](https://github.com/kalliope-project/kalliope_neuron_pushetta)       | Send a push message to a remote device like Android/iOS/Windows Phone or Chrome browser |
+| [rss_reader](https://github.com/kalliope-project/kalliope_neuron_rss_reader)   | get rss feed from website                                                               |
+| [tasker](https://github.com/kalliope-project/kalliope_neuron_tasker)           | Send a message to Android tasker app                                                    |
+| [twitter](https://github.com/kalliope-project/kalliope_neuron_twitter)         | Send a Twit from kalliope                                                               |
+| [wake_on_lan](https://github.com/kalliope-project/kalliope_neuron_wake_on_lan) | Wake on lan a computer                                                                  |
+| [wikipedia](https://github.com/kalliope-project/kalliope_neuron_wikipedia)     | Search for a page on Wikipedia                                                          |
+
+Wanna add your neuron in the list? Open [an issue](../../issues) with the link of your neuron or send a pull request to update the list directly.
+
+To know how to install a community neuron, read the "Installation" section of the [neuron documentation](neurons.md).

+ 5 - 0
Docs/neuron_template.md

@@ -4,6 +4,11 @@
 
 
 Little description of what the neuron does.
 Little description of what the neuron does.
 
 
+## Installation
+```
+kalliope install --git-url "https://github.com/my_user/my_neuron.git"
+```
+
 ## Options
 ## Options
 
 
 (usage of a [table generator](http://www.tablesgenerator.com/markdown_tables) is recommended)
 (usage of a [table generator](http://www.tablesgenerator.com/markdown_tables) is recommended)

+ 18 - 0
Docs/neurons.md

@@ -3,6 +3,22 @@
 A neuron is a plugin that performs a specific action. You use it to create a synapse.
 A neuron is a plugin that performs a specific action. You use it to create a synapse.
 You can add as many neurons as you want to a synapse. The neurons are executed one by one when the input order is triggered.
 You can add as many neurons as you want to a synapse. The neurons are executed one by one when the input order is triggered.
 
 
+## Installation
+
+Core neurons are already packaged with the installation of kalliope an can be used out of the box. Community neuron need to be installed manually.
+
+Use the CLI
+```
+kalliope install --git-url "<git_url>"
+```
+
+E.g:
+```
+kalliope install --git-url "https://github.com/kalliope-project/kalliope_neuron_wikipedia.git"
+```
+
+You may be prompted to type your `sudo` password during the process. You can see the list of [available neuron here](neuron_list.md)
+
 ## Usage
 ## Usage
 Neurons are declared in the `neurons` section of a synapse in your brain file.
 Neurons are declared in the `neurons` section of a synapse in your brain file.
 The `neurons` section is a list (because it starts with a "-") which contains neuron modules names
 The `neurons` section is a list (because it starts with a "-") which contains neuron modules names
@@ -22,6 +38,8 @@ neurons:
 ```
 ```
 > **note:** parameters are indented with two spaces bellow the neuron's name following the YAML syntax.
 > **note:** parameters are indented with two spaces bellow the neuron's name following the YAML syntax.
 
 
+> **note:** Kalliope will try to load the neuron from your resources directory, then from core neuron packages.
+
 To know the list of required parameters, check of documentation of the neuron.
 To know the list of required parameters, check of documentation of the neuron.
 Full list of [available neuron here](neuron_list.md)
 Full list of [available neuron here](neuron_list.md)
 
 

+ 19 - 0
Docs/settings.md

@@ -225,5 +225,24 @@ E.g
 default_synapse: "Default-response"
 default_synapse: "Default-response"
 ```
 ```
 
 
+## Resources directory
+
+The resources directory is the path where Kalliope will try to load community modules like Neurons, STTs or TTSs.
+Set a valid path is required if you want to install community neuron. The path can be relative or absolute.
+
+```
+resource_directory:
+  resource_name: "path"
+```
+
+E.g
+```
+resource_directory:
+  neuron: "resources/neurons"
+  stt: "resources/stt"
+  tts: "resources/tts"
+  trigger: "/full/path/to/trigger"
+```
+
 ## Next: configure the brain of Kalliope
 ## Next: configure the brain of Kalliope
 Now your settings are ok, you can start creating the [brain](brain.md) of your assistant.
 Now your settings are ok, you can start creating the [brain](brain.md) of your assistant.

+ 9 - 0
Tests/settings/settings_test.yml

@@ -78,3 +78,12 @@ rest_api:
 # ---------------------------
 # ---------------------------
 # Specify an optional default synapse response in case your order is not found.
 # Specify an optional default synapse response in case your order is not found.
 default_synapse: "Default-synapse"
 default_synapse: "Default-synapse"
+
+# ---------------------------
+# resource directory path
+# ---------------------------
+resource_directory:
+  neuron: "/tmp/kalliope/tests/kalliope_resources_dir/neurons"
+  stt: "/tmp/kalliope/tests/kalliope_resources_dir/stt"
+  tts: "/tmp/kalliope/tests/kalliope_resources_dir/tts"
+  trigger: "/tmp/kalliope/tests/kalliope_resources_dir/trigger"

+ 33 - 11
Tests/test_launchers.py

@@ -1,10 +1,11 @@
 import unittest
 import unittest
 import mock
 import mock
-import os
 
 
+from kalliope.core.Models.Resources import Resources
 from kalliope.core.NeuronLauncher import NeuronLauncher
 from kalliope.core.NeuronLauncher import NeuronLauncher
 from kalliope.core.SynapseLauncher import SynapseLauncher, SynapseNameNotFound
 from kalliope.core.SynapseLauncher import SynapseLauncher, SynapseNameNotFound
 from kalliope.core.TriggerLauncher import TriggerLauncher
 from kalliope.core.TriggerLauncher import TriggerLauncher
+from kalliope.core.ConfigurationManager import SettingLoader
 
 
 from kalliope.core.Models.Trigger import Trigger
 from kalliope.core.Models.Trigger import Trigger
 from kalliope.core.Models.Neuron import Neuron
 from kalliope.core.Models.Neuron import Neuron
@@ -32,9 +33,9 @@ class TestLaunchers(unittest.TestCase):
             TriggerLauncher.get_trigger(trigger=trigger,
             TriggerLauncher.get_trigger(trigger=trigger,
                                         callback=None)
                                         callback=None)
 
 
-            mock_get_class_instantiation.assert_called_once_with("trigger",
-                                                                 trigger.name.capitalize(),
-                                                                 trigger.parameters)
+            mock_get_class_instantiation.assert_called_once_with(package_name="trigger",
+                                                                 module_name=trigger.name,
+                                                                 parameters=trigger.parameters)
             mock_get_class_instantiation.reset_mock()
             mock_get_class_instantiation.reset_mock()
 
 
     ####
     ####
@@ -63,12 +64,20 @@ class TestLaunchers(unittest.TestCase):
 
 
         br = Brain(synapses=all_synapse_list)
         br = Brain(synapses=all_synapse_list)
 
 
+        sl = SettingLoader()
+        sl.settings.resource_dir = '/var/tmp/test/resources'
         with mock.patch("kalliope.core.Utils.get_dynamic_class_instantiation") as mock_get_class_instantiation:
         with mock.patch("kalliope.core.Utils.get_dynamic_class_instantiation") as mock_get_class_instantiation:
             # Success
             # Success
             SynapseLauncher.start_synapse("Synapse1", brain=br)
             SynapseLauncher.start_synapse("Synapse1", brain=br)
 
 
-            calls = [mock.call("neurons", neuron1.name.capitalize(), neuron1.parameters),
-                     mock.call("neurons", neuron2.name.capitalize(), neuron2.parameters)]
+            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.assert_has_calls(calls=calls)
             mock_get_class_instantiation.reset_mock()
             mock_get_class_instantiation.reset_mock()
 
 
@@ -85,11 +94,20 @@ class TestLaunchers(unittest.TestCase):
         signal1 = Order(sentence="this is the sentence")
         signal1 = Order(sentence="this is the sentence")
         synapse1 = Synapse(name="Synapse1", neurons=[neuron1, neuron2], signals=[signal1])
         synapse1 = Synapse(name="Synapse1", neurons=[neuron1, neuron2], signals=[signal1])
         synapse_empty = Synapse(name="Synapse_empty", neurons=[], 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:
         with mock.patch("kalliope.core.Utils.get_dynamic_class_instantiation") as mock_get_class_instantiation:
             SynapseLauncher._run_synapse(synapse=synapse1)
             SynapseLauncher._run_synapse(synapse=synapse1)
 
 
-            calls = [mock.call("neurons",neuron1.name.capitalize(),neuron1.parameters),
-                     mock.call("neurons",neuron2.name.capitalize(),neuron2.parameters)]
+            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.assert_has_calls(calls=calls)
             mock_get_class_instantiation.reset_mock()
             mock_get_class_instantiation.reset_mock()
 
 
@@ -105,11 +123,15 @@ class TestLaunchers(unittest.TestCase):
         Test the Neuron Launcher trying to start a Neuron
         Test the Neuron Launcher trying to start a Neuron
         """
         """
         neuron = Neuron(name='neurone1', parameters={'var1': 'val1'})
         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:
         with mock.patch("kalliope.core.Utils.get_dynamic_class_instantiation") as mock_get_class_instantiation:
             NeuronLauncher.start_neuron(neuron=neuron)
             NeuronLauncher.start_neuron(neuron=neuron)
 
 
-            mock_get_class_instantiation.assert_called_once_with("neurons",
-                                                                 neuron.name.capitalize(),
-                                                                 neuron.parameters)
+            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()
             mock_get_class_instantiation.reset_mock()
 
 

+ 32 - 15
Tests/test_order_analyser.py

@@ -364,11 +364,13 @@ class TestOrderAnalyser(unittest.TestCase):
                             synapse2,
                             synapse2,
                             synapse3]
                             synapse3]
 
 
-        expected_result = [synapse1]
+
 
 
         # Success
         # Success
-        self.assertEquals(OrderAnalyser._get_matching_synapse_list(all_synapses_list=all_synapse_list,
-                                                                   order_to_match=order_to_match),
+        expected_result = synapse1
+        oa_tuple_list = OrderAnalyser._get_matching_synapse_list(all_synapses_list=all_synapse_list,
+                                                                   order_to_match=order_to_match)
+        self.assertEquals(oa_tuple_list[0].synapse,
                           expected_result,
                           expected_result,
                           "Fail matching 'the expected synapse' from the complete synapse list and the order")
                           "Fail matching 'the expected synapse' from the complete synapse list and the order")
 
 
@@ -384,10 +386,14 @@ class TestOrderAnalyser(unittest.TestCase):
 
 
         expected_result = [synapse1,
         expected_result = [synapse1,
                            synapse2]
                            synapse2]
-        self.assertEquals(OrderAnalyser._get_matching_synapse_list(all_synapses_list=all_synapse_list,
-                                                                   order_to_match=order_to_match),
-                          expected_result,
-                          "Fail 'Multiple Matching synapses' from the complete synapse list and the order")
+        oa_tuple_list = OrderAnalyser._get_matching_synapse_list(all_synapses_list=all_synapse_list,
+                                                                 order_to_match=order_to_match)
+        self.assertEquals(oa_tuple_list[0].synapse,
+                          expected_result[0],
+                          "Fail 'Multiple Matching synapses' from the complete synapse list and the order (first element)")
+        self.assertEquals(oa_tuple_list[1].synapse,
+                          expected_result[1],
+                          "Fail 'Multiple Matching synapses' from the complete synapse list and the order (second element)")
 
 
         # matching no synapses
         # matching no synapses
         order_to_match = "this is not the correct word"
         order_to_match = "this is not the correct word"
@@ -413,10 +419,15 @@ class TestOrderAnalyser(unittest.TestCase):
         expected_result = [synapse1,
         expected_result = [synapse1,
                            synapse2]
                            synapse2]
 
 
-        self.assertEquals(OrderAnalyser._get_matching_synapse_list(all_synapses_list=all_synapse_list,
-                                                                   order_to_match=order_to_match),
-                          expected_result,
-                          "Fail matching 'synapse with all key worlds' from the complete synapse list and the order")
+        oa_tuple_list = OrderAnalyser._get_matching_synapse_list(all_synapses_list=all_synapse_list,
+                                                                 order_to_match=order_to_match)
+
+        self.assertEquals(oa_tuple_list[0].synapse,
+                          expected_result[0],
+                          "Fail matching 'synapse with all key worlds' from the complete synapse list and the order (first element)")
+        self.assertEquals(oa_tuple_list[1].synapse,
+                          expected_result[1],
+                          "Fail matching 'synapse with all key worlds' from the complete synapse list and the order (second element)")
 
 
     def test_get_params_from_order(self):
     def test_get_params_from_order(self):
 
 
@@ -537,11 +548,16 @@ class TestOrderAnalyser(unittest.TestCase):
         st = Settings()
         st = Settings()
         # Find synapse
         # Find synapse
         order = "this is the sentence"
         order = "this is the sentence"
-        expected_result = [synapse1]
-        self.assertEquals(OrderAnalyser._find_synapse_to_run(brain=br,settings=st, order=order),
+        expected_result = synapse1
+        oa_tuple_list = OrderAnalyser._find_synapse_to_run(brain=br,settings=st, order=order)
+        self.assertEquals(oa_tuple_list[0].synapse,
                           expected_result,
                           expected_result,
                           "Fail to run the proper synapse matching the order")
                           "Fail to run the proper synapse matching the order")
 
 
+        expected_result = signal1.sentence
+        self.assertEquals(oa_tuple_list[0].order,
+                        expected_result,
+                        "Fail to run the proper synapse matching the order")
         # No Default synapse
         # No Default synapse
         order = "No default synapse"
         order = "No default synapse"
         expected_result = []
         expected_result = []
@@ -552,8 +568,9 @@ class TestOrderAnalyser(unittest.TestCase):
         # Default synapse
         # Default synapse
         st = Settings(default_synapse="Synapse2")
         st = Settings(default_synapse="Synapse2")
         order = "default synapse"
         order = "default synapse"
-        expected_result = [synapse2]
-        self.assertEquals(OrderAnalyser._find_synapse_to_run(brain=br, settings=st, order=order),
+        expected_result = synapse2
+        oa_tuple_list = OrderAnalyser._find_synapse_to_run(brain=br, settings=st, order=order)
+        self.assertEquals(oa_tuple_list[0].synapse,
                           expected_result,
                           expected_result,
                           "Fail to run the default synapse")
                           "Fail to run the default synapse")
 
 

+ 41 - 2
Tests/test_settings_loader.py

@@ -1,9 +1,12 @@
 import os
 import os
+import inspect
 import platform
 import platform
+import shutil
 import unittest
 import unittest
 
 
 from kalliope.core.ConfigurationManager import SettingLoader
 from kalliope.core.ConfigurationManager import SettingLoader
 from kalliope.core.Models import Singleton
 from kalliope.core.Models import Singleton
+from kalliope.core.Models import Resources
 from kalliope.core.Models.RestAPI import RestAPI
 from kalliope.core.Models.RestAPI import RestAPI
 from kalliope.core.Models.Settings import Settings
 from kalliope.core.Models.Settings import Settings
 from kalliope.core.Models.Stt import Stt
 from kalliope.core.Models.Stt import Stt
@@ -14,8 +17,12 @@ from kalliope.core.Models.Tts import Tts
 class TestSettingLoader(unittest.TestCase):
 class TestSettingLoader(unittest.TestCase):
 
 
     def setUp(self):
     def setUp(self):
+        # get current script directory path. We are in /an/unknown/path/kalliope/core/Tests
+        cur_script_directory = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
+        # get parent dir. Now we are in /an/unknown/path/kalliope
+        root_dir = os.path.normpath(cur_script_directory + os.sep + os.pardir)
 
 
-        self.settings_file_to_test = os.getcwd() + os.sep + "/Tests/settings/settings_test.yml"
+        self.settings_file_to_test = root_dir + os.sep + "Tests/settings/settings_test.yml"
 
 
         self.settings_dict = {
         self.settings_dict = {
             'rest_api':
             'rest_api':
@@ -36,10 +43,25 @@ class TestSettingLoader(unittest.TestCase):
                 {'pico2wave': {'cache': True, 'language': 'fr-FR'}},
                 {'pico2wave': {'cache': True, 'language': 'fr-FR'}},
                 {'voxygen': {'voice': 'Agnes', 'cache': True}}
                 {'voxygen': {'voice': 'Agnes', 'cache': True}}
             ],
             ],
-            'default_synapse': 'Default-synapse'
+            'default_synapse': 'Default-synapse',
+            'resource_directory':{
+                'neuron': "/tmp/kalliope/tests/kalliope_resources_dir/neurons",
+                'stt': "/tmp/kalliope/tests/kalliope_resources_dir/stt",
+                'tts': "/tmp/kalliope/tests/kalliope_resources_dir/tts",
+                'trigger': "/tmp/kalliope/tests/kalliope_resources_dir/trigger"
+            }
         }
         }
 
 
+        # Init the folders, otherwise it raises an exceptions
+        os.makedirs("/tmp/kalliope/tests/kalliope_resources_dir/neurons")
+        os.makedirs("/tmp/kalliope/tests/kalliope_resources_dir/stt")
+        os.makedirs("/tmp/kalliope/tests/kalliope_resources_dir/tts")
+        os.makedirs("/tmp/kalliope/tests/kalliope_resources_dir/trigger")
+
     def tearDown(self):
     def tearDown(self):
+        # Cleanup
+        shutil.rmtree('/tmp/kalliope/tests/kalliope_resources_dir')
+
         Singleton._instances = {}
         Singleton._instances = {}
 
 
     def test_singleton(self):
     def test_singleton(self):
@@ -72,6 +94,12 @@ class TestSettingLoader(unittest.TestCase):
                                            login="admin", password="secret", port=5000)
                                            login="admin", password="secret", port=5000)
         settings_object.cache_path = '/tmp/kalliope_tts_cache'
         settings_object.cache_path = '/tmp/kalliope_tts_cache'
         settings_object.default_synapse = 'Default-synapse'
         settings_object.default_synapse = 'Default-synapse'
+        resources = Resources(neuron_folder="/tmp/kalliope/tests/kalliope_resources_dir/neurons",
+                              stt_folder="/tmp/kalliope/tests/kalliope_resources_dir/stt",
+                              tts_folder="/tmp/kalliope/tests/kalliope_resources_dir/tts",
+                              trigger_folder="/tmp/kalliope/tests/kalliope_resources_dir/trigger")
+
+        settings_object.resources=resources
         settings_object.machine = platform.machine()
         settings_object.machine = platform.machine()
 
 
         sl = SettingLoader(file_path=self.settings_file_to_test)
         sl = SettingLoader(file_path=self.settings_file_to_test)
@@ -133,5 +161,16 @@ class TestSettingLoader(unittest.TestCase):
         sl = SettingLoader(file_path=self.settings_file_to_test)
         sl = SettingLoader(file_path=self.settings_file_to_test)
         self.assertEqual(expected_default_synapse, sl._get_default_synapse(self.settings_dict))
         self.assertEqual(expected_default_synapse, sl._get_default_synapse(self.settings_dict))
 
 
+    def test_get_resources(self):
+
+        resources = Resources(neuron_folder="/tmp/kalliope/tests/kalliope_resources_dir/neurons",
+                              stt_folder="/tmp/kalliope/tests/kalliope_resources_dir/stt",
+                              tts_folder="/tmp/kalliope/tests/kalliope_resources_dir/tts",
+                              trigger_folder="/tmp/kalliope/tests/kalliope_resources_dir/trigger")
+        expected_resource = resources
+        sl = SettingLoader(file_path=self.settings_file_to_test)
+        self.assertEquals(expected_resource, sl._get_resources(self.settings_dict))
+
+
 if __name__ == '__main__':
 if __name__ == '__main__':
     unittest.main()
     unittest.main()

+ 12 - 3
Tests/test_utils.py

@@ -1,10 +1,16 @@
 import unittest
 import unittest
 import os
 import os
+import mock
 
 
 from kalliope.core.Models.Neuron import Neuron
 from kalliope.core.Models.Neuron import Neuron
+from kalliope.core.Models.Order import Order
+from kalliope.core.Models.Synapse import Synapse
 from kalliope.neurons.say.say import Say
 from kalliope.neurons.say.say import Say
 from kalliope.core.Utils.Utils import Utils
 from kalliope.core.Utils.Utils import Utils
 
 
+from kalliope.core.ConfigurationManager import SettingLoader
+from kalliope.core.ConfigurationManager import BrainLoader
+
 
 
 class TestUtils(unittest.TestCase):
 class TestUtils(unittest.TestCase):
     """
     """
@@ -121,11 +127,14 @@ class TestUtils(unittest.TestCase):
         """
         """
         Test that an instance as been instantiate properly.
         Test that an instance as been instantiate properly.
         """
         """
+        sl = SettingLoader()
+        sl.settings.resource_dir = '/var/tmp/test/resources'
 
 
         neuron = Neuron(name='Say', parameters={'message': 'test dynamic class instantiate'})
         neuron = Neuron(name='Say', parameters={'message': 'test dynamic class instantiate'})
-        self.assertTrue(isinstance(Utils.get_dynamic_class_instantiation("neurons",
-                                                                         neuron.name.capitalize(),
-                                                                         neuron.parameters),
+        self.assertTrue(isinstance(Utils.get_dynamic_class_instantiation(package_name="neurons",
+                                                                         module_name=neuron.name.capitalize(),
+                                                                         parameters=neuron.parameters,
+                                                                         resources_dir='/var/tmp/test/resources'),
                                    Say),
                                    Say),
                         "Fail instantiate a class")
                         "Fail instantiate a class")
 
 

+ 0 - 1
install/files/deb-packages_requirements.txt

@@ -10,7 +10,6 @@ python-pycparser
 python-paramiko
 python-paramiko
 python-markupsafe
 python-markupsafe
 apt-transport-https
 apt-transport-https
-python-pip
 python-dev
 python-dev
 libsmpeg0
 libsmpeg0
 libttspico-utils
 libttspico-utils

+ 2 - 8
install/files/python_requirements.txt

@@ -6,18 +6,12 @@ ansible==2.2.0.0
 python2-pythondialog==3.4.0
 python2-pythondialog==3.4.0
 jinja2==2.8
 jinja2==2.8
 cffi==1.9.1
 cffi==1.9.1
-pygmail==0.0.5.4
-pushetta==1.0.15
-wakeonlan==0.2.2
 ipaddress==1.0.17
 ipaddress==1.0.17
-pyowm==2.5.0
-python-twitter==3.1
 flask==0.11.1
 flask==0.11.1
 Flask-Restful==0.3.5
 Flask-Restful==0.3.5
-wikipedia==1.4.0
-requests==2.12.1
+requests==2.12.4
 httpretty==0.8.14
 httpretty==0.8.14
 mock==2.0.0
 mock==2.0.0
-feedparser==5.2.1
 Flask-Testing==0.6.1
 Flask-Testing==0.6.1
 apscheduler==3.3.0
 apscheduler==3.3.0
+GitPython==2.1.1

+ 13 - 2
kalliope/__init__.py

@@ -11,6 +11,7 @@ from kalliope.core.MainController import MainController
 import signal
 import signal
 import sys
 import sys
 
 
+from kalliope.core.ResourcesManager import ResourcesManager
 from kalliope.core.SynapseLauncher import SynapseLauncher
 from kalliope.core.SynapseLauncher import SynapseLauncher
 
 
 logging.basicConfig()
 logging.basicConfig()
@@ -28,7 +29,7 @@ def signal_handler(signal, frame):
     sys.exit(0)
     sys.exit(0)
 
 
 # actions available
 # actions available
-ACTION_LIST = ["start", "gui"]
+ACTION_LIST = ["start", "gui", "install"]
 
 
 
 
 def main():
 def main():
@@ -37,10 +38,11 @@ def main():
     """
     """
     # create arguments
     # create arguments
     parser = argparse.ArgumentParser(description='Kalliope')
     parser = argparse.ArgumentParser(description='Kalliope')
-    parser.add_argument("action", help="[start|gui]")
+    parser.add_argument("action", help="[start|gui|install]")
     parser.add_argument("--run-synapse", help="Name of a synapse to load surrounded by quote")
     parser.add_argument("--run-synapse", help="Name of a synapse to load surrounded by quote")
     parser.add_argument("--brain-file", help="Full path of a brain file")
     parser.add_argument("--brain-file", help="Full path of a brain file")
     parser.add_argument("--debug", action='store_true', help="Show debug output")
     parser.add_argument("--debug", action='store_true', help="Show debug output")
+    parser.add_argument("--git-url", help="Git URL of the neuron to install")
 
 
     # parse arguments from script parameters
     # parse arguments from script parameters
     args = parser.parse_args()
     args = parser.parse_args()
@@ -89,6 +91,15 @@ def main():
     if args.action == "gui":
     if args.action == "gui":
         ShellGui(brain=brain)
         ShellGui(brain=brain)
 
 
+    if args.action == "install":
+        if not args.git_url:
+            Utils.print_danger("You must specify the git url")
+        else:
+            parameters = {
+                "git_url": args.git_url
+            }
+            ResourcesManager("install", **parameters)
+
 
 
 def configure_logging(debug=None):
 def configure_logging(debug=None):
     """
     """

+ 1 - 1
kalliope/_version.py

@@ -1,2 +1,2 @@
 # https://www.python.org/dev/peps/pep-0440/
 # https://www.python.org/dev/peps/pep-0440/
-version_str = "0.3.0"
+version_str = "0.4.0"

+ 21 - 6
kalliope/core/ConfigurationManager/ConfigurationChecker.py

@@ -1,7 +1,9 @@
 import re
 import re
+import os
+import imp
 
 
 from kalliope.core.Utils.Utils import ModuleNotFoundError
 from kalliope.core.Utils.Utils import ModuleNotFoundError
-
+from kalliope.core.ConfigurationManager.SettingLoader import SettingLoader
 
 
 class InvalidSynapeName(Exception):
 class InvalidSynapeName(Exception):
     """
     """
@@ -139,13 +141,26 @@ class ConfigurationChecker:
             :type neuron_module_name: str
             :type neuron_module_name: str
             :return:
             :return:
             """
             """
-            package_name = "kalliope.neurons"
-            mod = __import__(package_name, fromlist=[neuron_module_name])
+            sl = SettingLoader()
+            settings = sl.settings
+            package_name = "kalliope.neurons" + "." + neuron_module_name.lower() + "." + neuron_module_name.lower()
+            if settings.resources is not None:
+                neuron_resource_path = settings.resources.neuron_folder + \
+                                       os.sep + neuron_module_name.lower() + os.sep + \
+                                       neuron_module_name.lower()+".py"
+                if os.path.exists(neuron_resource_path):
+                    imp.load_source(neuron_module_name.capitalize(), neuron_resource_path)
+                    package_name = neuron_module_name.capitalize()
+
             try:
             try:
-                getattr(mod, neuron_module_name)
+                mod = __import__(package_name, fromlist=[neuron_module_name.capitalize()])
+                getattr(mod, neuron_module_name.capitalize())
             except AttributeError:
             except AttributeError:
-                raise ModuleNotFoundError("The module %s does not exist in package %s" % (neuron_module_name,
-                                                                                          package_name))
+                raise ModuleNotFoundError("[AttributeError] The module %s does not exist in the package %s " % (neuron_module_name.capitalize(),
+                                                                                                                package_name))
+            except ImportError:
+                raise ModuleNotFoundError("[ImportError] The module %s does not exist in the package %s " % (neuron_module_name.capitalize(),
+                                                                                                             package_name))
             return True
             return True
 
 
         if isinstance(neuron_dict, dict):
         if isinstance(neuron_dict, dict):

+ 61 - 1
kalliope/core/ConfigurationManager/SettingLoader.py

@@ -1,7 +1,9 @@
 import logging
 import logging
+import os
 
 
 from YAMLLoader import YAMLLoader
 from YAMLLoader import YAMLLoader
-from kalliope.core.Utils import Utils
+from kalliope.core.Models.Resources import Resources
+from kalliope.core.Utils.Utils import Utils
 from kalliope.core.Models import Singleton
 from kalliope.core.Models import Singleton
 from kalliope.core.Models.RestAPI import RestAPI
 from kalliope.core.Models.RestAPI import RestAPI
 from kalliope.core.Models.Settings import Settings
 from kalliope.core.Models.Settings import Settings
@@ -106,6 +108,7 @@ class SettingLoader(object):
         rest_api = self._get_rest_api(settings)
         rest_api = self._get_rest_api(settings)
         cache_path = self._get_cache_path(settings)
         cache_path = self._get_cache_path(settings)
         default_synapse = self._get_default_synapse(settings)
         default_synapse = self._get_default_synapse(settings)
+        resources = self._get_resources(settings)
 
 
         # Load the setting singleton with the parameters
         # Load the setting singleton with the parameters
         setting_object.default_tts_name = default_tts_name
         setting_object.default_tts_name = default_tts_name
@@ -119,6 +122,7 @@ class SettingLoader(object):
         setting_object.rest_api = rest_api
         setting_object.rest_api = rest_api
         setting_object.cache_path = cache_path
         setting_object.cache_path = cache_path
         setting_object.default_synapse = default_synapse
         setting_object.default_synapse = default_synapse
+        setting_object.resources = resources
 
 
         return setting_object
         return setting_object
 
 
@@ -507,4 +511,60 @@ class SettingLoader(object):
 
 
         return default_synapse
         return default_synapse
 
 
+    @staticmethod
+    def _get_resources(settings):
+        """
+        Return a resources object that contains path of third party modules
+
+        :param settings: The YAML settings file
+        :type settings: dict
+        :return: the resource object
+        :rtype: Resources
+
+        :Example:
+
+            resource_directory = cls._get_resource_dir(settings)
+
+        .. seealso::
+        .. raises:: SettingNotFound, NullSettingException, SettingInvalidException
+        .. warnings:: Class Method and Private
+        """
+        try:
+            resource_dir = settings["resource_directory"]
+            logger.debug("Resource directory synapse: %s" % resource_dir)
+
+            neuron_folder = None
+            stt_folder = None
+            tts_folder = None
+            trigger_folder = None
+            if "neuron" in resource_dir:
+                neuron_folder = resource_dir["neuron"]
+                if not os.path.exists(neuron_folder):
+                    raise SettingInvalidException("The path %s does not exist on the system" % neuron_folder)
+
+            if "stt" in resource_dir:
+                stt_folder = resource_dir["stt"]
+                if not os.path.exists(stt_folder):
+                    raise SettingInvalidException("The path %s does not exist on the system" % stt_folder)
+
+            if "tts" in resource_dir:
+                tts_folder = resource_dir["tts"]
+                if not os.path.exists(tts_folder):
+                    raise SettingInvalidException("The path %s does not exist on the system" % tts_folder)
+
+            if "trigger" in resource_dir:
+                trigger_folder = resource_dir["trigger"]
+                if not os.path.exists(trigger_folder):
+                    raise SettingInvalidException("The path %s does not exist on the system" % trigger_folder)
+
+            resource_object = Resources(neuron_folder=neuron_folder,
+                                        stt_folder=stt_folder,
+                                        tts_folder=tts_folder,
+                                        trigger_folder=trigger_folder)
+        except KeyError:
+            logger.debug("Resource directory not found in settings")
+            resource_object = None
+
+        return resource_object
+
 
 

+ 41 - 0
kalliope/core/Models/Resources.py

@@ -0,0 +1,41 @@
+
+
+class Resources(object):
+    """
+
+    """
+    def __init__(self, neuron_folder=None, stt_folder=None, tts_folder=None, trigger_folder=None):
+        self.neuron_folder = neuron_folder
+        self.stt_folder = stt_folder
+        self.tts_folder = tts_folder
+        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)
+
+    def serialize(self):
+        """
+        This method allows to serialize in a proper way this object
+
+        :return: A dict of order
+        :rtype: Dict
+        """
+
+        return {
+            'neuron_folder': self.neuron_folder,
+            'stt_folder': self.stt_folder,
+            'tts_folder': self.tts_folder,
+            'trigger_folder': self.trigger_folder
+        }
+
+    def __eq__(self, other):
+        """
+        This is used to compare 2 objects
+        :param other:
+        :return:
+        """
+        return self.__dict__ == other.__dict__

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

@@ -19,6 +19,7 @@ class Settings(object):
                  rest_api=None,
                  rest_api=None,
                  cache_path=None,
                  cache_path=None,
                  default_synapse=None,
                  default_synapse=None,
+                 resources=None,
                  machine=None):
                  machine=None):
 
 
         self.default_tts_name = default_tts_name
         self.default_tts_name = default_tts_name
@@ -32,6 +33,7 @@ class Settings(object):
         self.rest_api = rest_api
         self.rest_api = rest_api
         self.cache_path = cache_path
         self.cache_path = cache_path
         self.default_synapse = default_synapse
         self.default_synapse = default_synapse
+        self.resources = resources
         self.machine = platform.machine()   # can be x86_64 or armv7l
         self.machine = platform.machine()   # can be x86_64 or armv7l
 
 
     def __eq__(self, other):
     def __eq__(self, other):

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

@@ -1,5 +1,6 @@
 from Singleton import Singleton
 from Singleton import Singleton
 from Event import Event
 from Event import Event
+from Resources import Resources
 from Brain import Brain
 from Brain import Brain
 from Order import Order
 from Order import Order
 from Synapse import Synapse
 from Synapse import Synapse

+ 10 - 3
kalliope/core/NeuronLauncher.py

@@ -1,6 +1,7 @@
 import logging
 import logging
 
 
 from kalliope.core.Utils.Utils import Utils
 from kalliope.core.Utils.Utils import Utils
+from kalliope.core.ConfigurationManager.SettingLoader import SettingLoader
 
 
 logging.basicConfig()
 logging.basicConfig()
 logger = logging.getLogger("kalliope")
 logger = logging.getLogger("kalliope")
@@ -20,6 +21,12 @@ class NeuronLauncher:
         :return:
         :return:
         """
         """
         logger.debug("Run plugin \"%s\" with parameters %s" % (neuron.name, neuron.parameters))
         logger.debug("Run plugin \"%s\" with parameters %s" % (neuron.name, neuron.parameters))
-        return Utils.get_dynamic_class_instantiation("neurons",
-                                                     neuron.name.capitalize(),
-                                                     neuron.parameters)
+        sl = SettingLoader()
+        settings = sl.settings
+        neuron_folder = None
+        if settings.resources:
+            neuron_folder = settings.resources.neuron_folder
+        return Utils.get_dynamic_class_instantiation(package_name="neurons",
+                                                     module_name=neuron.name,
+                                                     parameters=neuron.parameters,
+                                                     resources_dir=neuron_folder)

+ 3 - 2
kalliope/core/NeuronModule.py

@@ -129,8 +129,9 @@ class NeuronModule(object):
             logger.debug("NeuroneModule: TTS args: %s" % tts_object)
             logger.debug("NeuroneModule: TTS args: %s" % tts_object)
 
 
             # get the instance of the TTS module
             # get the instance of the TTS module
-            tts_module_instance = Utils.get_dynamic_class_instantiation("tts", tts_object.name.capitalize(),
-                                                                        tts_object.parameters)
+            tts_module_instance = Utils.get_dynamic_class_instantiation(package_name="tts",
+                                                                        module_name=tts_object.name,
+                                                                        parameters=tts_object.parameters)
             # generate the audio file and play it
             # generate the audio file and play it
             tts_module_instance.say(tts_message)
             tts_module_instance.say(tts_message)
 
 

+ 42 - 21
kalliope/core/OrderAnalyser.py

@@ -1,5 +1,6 @@
 # coding: utf8
 # coding: utf8
 import re
 import re
+import collections
 from collections import Counter
 from collections import Counter
 
 
 from kalliope.core.Utils.Utils import Utils
 from kalliope.core.Utils.Utils import Utils
@@ -17,6 +18,7 @@ class OrderAnalyser:
     """
     """
     This Class is used to compare the incoming message to the Signal/Order sentences.
     This Class is used to compare the incoming message to the Signal/Order sentences.
     """
     """
+
     def __init__(self, order, brain=None):
     def __init__(self, order, brain=None):
         """
         """
         Class used to load brain and run neuron attached to the received order
         Class used to load brain and run neuron attached to the received order
@@ -33,43 +35,54 @@ class OrderAnalyser:
 
 
     def start(self, synapses_to_run=None, external_order=None):
     def start(self, synapses_to_run=None, external_order=None):
         """
         """
-        This method matches the incoming messages to the signals/order sentences provided in the Brain
+        This method matches the incoming messages to the signals/order sentences provided in the Brain.
+
+        Note: we use named tuples:
+        tuple_synapse_order = collections.namedtuple('tuple_synapse_matchingOrder',['synapse', 'order'])
         """
         """
 
 
+        synapse_order_tuple = collections.namedtuple('tuple_synapse_matchingOrder', ['synapse', 'order'])
+        synapses_order_tuple_list = list()
+
+        if synapses_to_run is not None and external_order is not None:
+            for synapse in synapses_to_run:
+                synapses_order_tuple_list.append(synapse_order_tuple(synapse=synapse,
+                                                                     order=external_order))
+
         # if list of synapse is not provided, let's find one
         # if list of synapse is not provided, let's find one
-        if synapses_to_run is None and external_order is None:
+        else:  # synapses_to_run is None or external_order is None:
             # create a dict of synapses that have been launched
             # create a dict of synapses that have been launched
             logger.debug("[orderAnalyser.start]-> No Synapse provided, let's find one")
             logger.debug("[orderAnalyser.start]-> No Synapse provided, let's find one")
-            synapses_to_run = self._find_synapse_to_run(brain=self.brain,
-                                                        settings=self.settings,
-                                                        order=self.order)
+            synapses_order_tuple_list = self._find_synapse_to_run(brain=self.brain,
+                                                                  settings=self.settings,
+                                                                  order=self.order)
 
 
         # retrieve params
         # retrieve params
-        for synapse in synapses_to_run:
-            # If no external orders has been provided then run signals sentences
-            if external_order is None:
-                logger.debug("[orderAnalyser.start]-> No external order provided, run the signals from the synapse")
-                for signal in synapse.signals:
-                    params = self._get_params_from_order(signal.sentence, self.order)
-            else:
-                logger.debug("[orderAnalyser.start]-> provided external order")
-                params = self._get_params_from_order(external_order, self.order)
+        synapses_launched = list()
+        for tuple in synapses_order_tuple_list:
+            logger.debug("[orderAnalyser.start]-> Grab the params")
+            params = self._get_params_from_order(tuple.order, self.order)
 
 
             # Start a neuron list with params
             # Start a neuron list with params
-            self._start_list_neurons(list_neurons=synapse.neurons,
+            self._start_list_neurons(list_neurons=tuple.synapse.neurons,
                                      params=params)
                                      params=params)
+            synapses_launched.append(tuple.synapse)
 
 
         # return the list of launched synapse
         # return the list of launched synapse
-        return synapses_to_run
+        return synapses_launched
 
 
     @classmethod
     @classmethod
     def _find_synapse_to_run(cls, brain, settings, order):
     def _find_synapse_to_run(cls, brain, settings, order):
         """
         """
-        Find the list of the synapse matching the order
+        Find the list of the synapse matching the order.
+
+        Note: we use named tuples:
+        tuple_synapse_order = collections.namedtuple('tuple_synapse_matchingOrder',['synapse', 'order'])
+
         :param brain: the brain
         :param brain: the brain
         :param settings: the settings
         :param settings: the settings
         :param order: the provided order to match
         :param order: the provided order to match
-        :return: the list of synapses launched
+        :return: the list of synapses launched (named tuples)
         """
         """
 
 
         synapse_to_run = cls._get_matching_synapse_list(brain.synapses, order)
         synapse_to_run = cls._get_matching_synapse_list(brain.synapses, order)
@@ -83,7 +96,10 @@ class OrderAnalyser:
                 if default_synapse is not None:
                 if default_synapse is not None:
                     logger.debug("Default synapse found %s" % default_synapse)
                     logger.debug("Default synapse found %s" % default_synapse)
                     Utils.print_info("Default synapse found: %s, running it" % default_synapse.name)
                     Utils.print_info("Default synapse found: %s, running it" % default_synapse.name)
-                    synapse_to_run.append(default_synapse)
+                    tuple_synapse_order = collections.namedtuple('tuple_synapse_matchingOrder',
+                                                                         ['synapse', 'order'])
+                    synapse_to_run.append(tuple_synapse_order(synapse=default_synapse,
+                                                                      order=""))
 
 
         return synapse_to_run
         return synapse_to_run
 
 
@@ -92,17 +108,22 @@ class OrderAnalyser:
         """
         """
         Class method to return all the matching synapses with the order from the complete of synapses.
         Class method to return all the matching synapses with the order from the complete of synapses.
 
 
+        Note: we use named tuples:
+        tuple_synapse_matchingOrder = collections.namedtuple('tuple_synapse_matchingOrder',['synapse', 'order'])
+
         :param all_synapses_list: the complete list of all synapses
         :param all_synapses_list: the complete list of all synapses
         :param order_to_match: the order to match
         :param order_to_match: the order to match
         :type order_to_match: str
         :type order_to_match: str
-        :return: the list of matching synapses
+        :return: the list of matching synapses (named tuples)
         """
         """
+        tuple_synapse_order = collections.namedtuple('tuple_synapse_matchingOrder', ['synapse', 'order'])
         matching_synapses_list = list()
         matching_synapses_list = list()
         for synapse in all_synapses_list:
         for synapse in all_synapses_list:
             for signal in synapse.signals:
             for signal in synapse.signals:
                 if type(signal) == Order:
                 if type(signal) == Order:
                     if cls.spelt_order_match_brain_order_via_table(signal.sentence, order_to_match):
                     if cls.spelt_order_match_brain_order_via_table(signal.sentence, order_to_match):
-                        matching_synapses_list.append(synapse)
+                        matching_synapses_list.append(tuple_synapse_order(synapse=synapse,
+                                                                                  order=signal.sentence))
                         logger.debug("Order found! Run neurons: %s" % synapse.neurons)
                         logger.debug("Order found! Run neurons: %s" % synapse.neurons)
                         Utils.print_success("Order matched in the brain. Running synapse \"%s\"" % synapse.name)
                         Utils.print_success("Order matched in the brain. Running synapse \"%s\"" % synapse.name)
         return matching_synapses_list
         return matching_synapses_list

+ 2 - 2
kalliope/core/OrderListener.py

@@ -55,8 +55,8 @@ class OrderListener(Thread):
         for stt_object in self.settings.stts:
         for stt_object in self.settings.stts:
             if stt_object.name == self.stt_module_name:
             if stt_object.name == self.stt_module_name:
                 stt_object.parameters["callback"] = self.callback
                 stt_object.parameters["callback"] = self.callback
-                Utils.get_dynamic_class_instantiation('stt',
-                                                      stt_object.name.capitalize(),
+                Utils.get_dynamic_class_instantiation(package_name='stt',
+                                                      module_name=stt_object.name.capitalize(),
                                                       parameters=stt_object.parameters)
                                                       parameters=stt_object.parameters)
 
 
     @staticmethod
     @staticmethod

+ 266 - 0
kalliope/core/ResourcesManager.py

@@ -0,0 +1,266 @@
+import getpass
+import os
+
+import logging
+
+import shutil
+from git import Repo
+from kalliope.core.Models import Neuron
+
+from kalliope import Utils
+from kalliope.core.ConfigurationManager import YAMLLoader
+from kalliope.core.ConfigurationManager import SettingLoader
+from kalliope.core.NeuronLauncher import NeuronLauncher
+
+logging.basicConfig()
+logger = logging.getLogger("kalliope")
+
+# Global values for processing:
+LOCAL_TMP_FOLDER = "/tmp/kalliope/resources/"
+TMP_GIT_FOLDER = "kalliope_new_module_temp_name"
+DNA_FILE_NAME = "dna.yml"
+INSTALL_FILE_NAME = "install.yml"
+
+# Global values for required parameters in DNA:
+DNA_NAME = "name"
+DNA_TYPE = "type"
+
+# Global_Names for 'types' to match:
+TYPE_NEURON = "neuron"
+TYPE_TTS = "tts"
+TYPE_STT = "stt"
+TYPE_TRIGGER = "trigger"
+
+
+class ResourcesManager(object):
+    def __init__(self, action, **kwargs):
+        super(ResourcesManager, self).__init__()
+        # get settings
+        sl = SettingLoader()
+        self.settings = sl.settings
+
+        # action to perform (delete, install, update)
+        self.action = action
+
+        # in case of update or install, url where
+        self.git_url = kwargs.get('git_url', None)
+
+        # temp path where we install the new module
+        self.tmp_path = LOCAL_TMP_FOLDER + TMP_GIT_FOLDER
+        self.dna_file_path = self.tmp_path + os.sep + DNA_FILE_NAME
+        self.install_file_path = self.tmp_path + os.sep + INSTALL_FILE_NAME
+        self.dna_file = None
+
+        if self.action == "install":
+            self.install()
+
+    def install(self):
+        """
+        Module installation method.
+        """
+        if self.is_settings_ok(resources=self.settings.resources,
+                               folder_path=self.settings.resources.neuron_folder):
+            # first, we clone the repo
+            self._clone_repo(path=self.tmp_path,
+                             git_url=self.git_url)
+
+            # check the content of the cloned repo
+            if self.is_repo_ok(dna_file_path=self.dna_file_path,
+                               install_file_path=self.install_file_path):
+
+                # Load the dna.yml file
+                self._set_dna_file()
+                if self._check_dna(dna_file=self.dna_file,
+                                   tmp_path=self.tmp_path):
+
+                    # Let's find the target folder depending the type
+                    module_type = self.dna_file["type"].lower()
+                    target_folder = self._get_target_folder(resources=self.settings.resources,
+                                                            module_type=module_type)
+                    if target_folder is not None:
+                        # let's move the tmp folder in the right folder and get a new path for the module
+                        module_name = self.dna_file["name"].lower()
+                        target_path = self._rename_temp_folder(name=module_name,
+                                                               target_folder=target_folder,
+                                                               tmp_path=self.tmp_path)
+
+                        # if the target_path exists, then run the install file within the new repository
+                        if target_path is not None:
+                            self.install_file_path = target_path + os.sep + INSTALL_FILE_NAME
+                            self.run_ansible_playbook_module(install_file_path=self.install_file_path)
+                            Utils.print_success("Module: %s installed" % module_name)
+
+    def _set_dna_file(self):
+        """
+        load the dna file from the module.
+        :return: set loading
+        """
+        # get the content of the DNA file
+        self.dna_file = YAMLLoader().get_config(self.dna_file_path)
+        logger.debug("[ResourcesManager] DNA file content: " + str(self.dna_file))
+
+
+    @staticmethod
+    def _check_dna(dna_file, tmp_path):
+        """
+        Check the dna_file values
+        :param dna_file: the dna_file to check
+        :param tmp_path: the temporary file path of the repo
+        :return: True if ok, False otherwise
+        """
+        success_loading = True
+        if "name" not in dna_file:
+            Utils.print_danger("The DNA of does not contains a \"name\" tag")
+            shutil.rmtree(tmp_path)
+            success_loading = False
+
+        if "type" not in dna_file:
+            Utils.print_danger("The DNA of does not contains a \"type\" tag")
+            shutil.rmtree(tmp_path)
+            success_loading = False
+
+        return success_loading
+
+    @staticmethod
+    def is_settings_ok(resources, folder_path):
+        """
+        Test if required settings files in config of Kalliope are ok.
+        :param resources: the Resources model
+        :param folder_path: the folder associate to the resource
+        :return:
+        """
+        settings_ok = True
+        if resources is None:
+            message = "Resources folder not set in settings, cannot install."
+            logger.debug(message)
+            Utils.print_danger(message)
+            settings_ok = False
+
+        if folder_path is None:
+            message = "No folder %s set in settings, cannot install." % folder_path
+            logger.debug(message)
+            Utils.print_danger(message)
+            settings_ok = False
+
+        return settings_ok
+
+    @staticmethod
+    def is_repo_ok(dna_file_path, install_file_path):
+        """
+        Check if the git cloned repo is fine to be installed
+        :return: True if repo is ok to be installed, False otherwise
+        """
+        repo_ok = True
+        # check that a install.yml file is present
+        if not os.path.exists(install_file_path):
+            Utils.print_danger("Missing %s file" % INSTALL_FILE_NAME)
+            repo_ok = False
+
+        Utils.print_info("Checking repository...")
+        if not os.path.exists(dna_file_path):
+            Utils.print_danger("Missing %s file" % DNA_FILE_NAME)
+            repo_ok = False
+
+        return repo_ok
+
+    @staticmethod
+    def _get_target_folder(resources, module_type):
+        """
+        Return the folder from the resources and given a module type
+        :param resources: Resource
+        :param module_type: type of the module
+        :return: path of the folder
+        """
+        folder_path = None
+        message = "Does this type really exists ? No %s folder set in settings, cannot install." % module_type
+
+        # Let's find the right path depending of the type
+        if module_type == TYPE_NEURON:
+            if resources.neuron_folder is not None:
+                folder_path = resources.neuron_folder
+            else:
+                message = "No %s folder set in settings, cannot install." % TYPE_NEURON
+
+        elif module_type == TYPE_STT:
+            if resources.stt_folder is not None:
+                folder_path = resources.stt_folder
+            else:
+                message = "No %s folder set in settings, cannot install." % TYPE_STT
+
+        elif module_type == TYPE_TTS:
+            if resources.tts_folder is not None:
+                folder_path = resources.stt_folder
+            else:
+                message = "No %s folder set in settings, cannot install." % TYPE_TTS
+
+        elif module_type == TYPE_TRIGGER:
+            if resources.trigger_folder is not None:
+                folder_path = resources.trigger_folder
+            else:
+                message = "No %s folder set in settings, cannot install." % TYPE_TRIGGER
+
+        # No folder_path has been found
+        if folder_path is None:
+            logger.debug(message)
+            Utils.print_danger(message)
+
+        return folder_path
+
+
+    @staticmethod
+    def _clone_repo(path, git_url):
+        """
+        Use git to clone locally the neuron in a temp folder
+        :return:
+        """
+        # clone the repo
+        logger.debug("GIT clone into folder: %s" % path)
+        Utils.print_info("Cloning repository...")
+        # if the folder already exist we remove it
+        if os.path.exists(path):
+            shutil.rmtree(path)
+        else:
+            os.makedirs(path)
+        Repo.clone_from(git_url, path)
+
+    @staticmethod
+    def _rename_temp_folder(name, target_folder, tmp_path):
+        """
+        Rename the temp folder of the cloned repo
+        Return the name of the path to install
+        :return: path to install, None if already exists
+        """
+
+        new_absolute_neuron_path = target_folder + os.sep + name
+        try:
+            os.rename(tmp_path, new_absolute_neuron_path)
+            return new_absolute_neuron_path
+        except OSError:
+            # the folder already exist
+            Utils.print_warning("The module %s already exist in the path %s" % (name, target_folder))
+            # remove the cloned repo
+            shutil.rmtree(tmp_path)
+
+
+    @staticmethod
+    def run_ansible_playbook_module(install_file_path):
+        """
+        Run the install.yml file through an Ansible playbook using the dedicated neuron !
+
+        :param install_file_path: the path of the Ansible playbook to run.
+        :param target_path:
+        :param name:
+        :return:
+        """
+        Utils.print_info("Starting neuron installation")
+        # ask the sudo password
+        pswd = getpass.getpass('Sudo password:')
+        ansible_neuron_parameters = {
+            "task_file": install_file_path,
+            "sudo": True,
+            "sudo_user": "root",
+            "sudo_password": pswd
+        }
+        neuron = Neuron(name="ansible_playbook", parameters=ansible_neuron_parameters)
+        NeuronLauncher.start_neuron(neuron)
+

+ 3 - 1
kalliope/core/TTS/TTSLauncher.py

@@ -22,4 +22,6 @@ class TTSLauncher(object):
         .. warnings:: Class Method and Public
         .. warnings:: Class Method and Public
         """
         """
         logger.debug("get TTS module \"%s\" with parameters %s" % (tts.name, tts.parameters))
         logger.debug("get TTS module \"%s\" with parameters %s" % (tts.name, tts.parameters))
-        return Utils.get_dynamic_class_instantiation("tts", tts.name.capitalize(), tts.parameters)
+        return Utils.get_dynamic_class_instantiation(package_name="tts",
+                                                     module_name=tts.name,
+                                                     parameters=tts.parameters)

+ 3 - 3
kalliope/core/TriggerLauncher.py

@@ -23,6 +23,6 @@ class TriggerLauncher(object):
         # add the callback method to parameters
         # add the callback method to parameters
         trigger.parameters["callback"] = callback
         trigger.parameters["callback"] = callback
         logger.debug("TriggerLauncher: Start trigger %s with parameters: %s" % (trigger.name, trigger.parameters))
         logger.debug("TriggerLauncher: Start trigger %s with parameters: %s" % (trigger.name, trigger.parameters))
-        return Utils.get_dynamic_class_instantiation("trigger",
-                                                     trigger.name.capitalize(),
-                                                     trigger.parameters)
+        return Utils.get_dynamic_class_instantiation(package_name="trigger",
+                                                     module_name=trigger.name,
+                                                     parameters=trigger.parameters)

+ 19 - 7
kalliope/core/Utils/Utils.py

@@ -1,6 +1,7 @@
 import logging
 import logging
 import os
 import os
 import inspect
 import inspect
+import imp
 
 
 logging.basicConfig()
 logging.basicConfig()
 logger = logging.getLogger("kalliope")
 logger = logging.getLogger("kalliope")
@@ -84,8 +85,8 @@ class Utils(object):
     # Dynamic loading
     # Dynamic loading
     #
     #
     #########
     #########
-    @staticmethod
-    def get_dynamic_class_instantiation(package_name, module_name, parameters=None):
+    @classmethod
+    def get_dynamic_class_instantiation(cls, package_name, module_name, parameters=None, resources_dir=None):
         """
         """
         Load a python class dynamically
         Load a python class dynamically
 
 
@@ -96,16 +97,27 @@ class Utils(object):
         :param package_name: name of the package where we will find the module to load (neurons, tts, stt, trigger)
         :param package_name: name of the package where we will find the module to load (neurons, tts, stt, trigger)
         :param module_name: name of the module from the package_name to load. This one is capitalized. Eg: Snowboy
         :param module_name: name of the module from the package_name to load. This one is capitalized. Eg: Snowboy
         :param parameters:  dict parameters to send as argument to the module
         :param parameters:  dict parameters to send as argument to the module
+        :param resources_dir: the resource directory to check for external resources
         :return:
         :return:
         """
         """
         logger.debug("Run plugin %s with parameter %s" % (module_name, parameters))
         logger.debug("Run plugin %s with parameter %s" % (module_name, parameters))
-        module_name_with_path = "kalliope." + package_name + "." + module_name.lower() + "." + module_name.lower()
-        mod = __import__(module_name_with_path, fromlist=[module_name])
+        package_path = "kalliope." + package_name + "." + module_name.lower() + "." + module_name.lower()
+        if resources_dir is not None:
+            neuron_resource_path = resources_dir + os.sep + module_name.lower() \
+                                   + os.sep + module_name.lower() + ".py"
+            if os.path.exists(neuron_resource_path):
+                imp.load_source(module_name.capitalize(), neuron_resource_path)
+                package_path = module_name.capitalize()
+                logger.debug("[Utils]-> get_dynamic_class_instantiation : loading path : %s, as package %s" % (
+                                                                                neuron_resource_path, package_path))
+
+        mod = __import__(package_path, fromlist=[module_name.capitalize()])
+
         try:
         try:
-            klass = getattr(mod, module_name)
+            klass = getattr(mod, module_name.capitalize())
         except AttributeError:
         except AttributeError:
-            logger.debug("Error: No module named %s " % module_name)
-            raise ModuleNotFoundError("The module %s does not exist in package %s" % (module_name, package_name))
+            logger.debug("Error: No module named %s " % module_name.capitalize())
+            raise ModuleNotFoundError("The module %s does not exist in package %s" % (module_name.capitalize(), package_name))
 
 
         if klass is not None:
         if klass is not None:
             # run the plugin
             # run the plugin

+ 42 - 15
kalliope/neurons/ansible_playbook/README.md

@@ -8,17 +8,25 @@ Playbooks are Ansible’s configuration, deployment, and orchestration language.
 
 
 This neuron can be used to perform complex operation with all [modules available from Ansible](http://docs.ansible.com/ansible/modules.html).
 This neuron can be used to perform complex operation with all [modules available from Ansible](http://docs.ansible.com/ansible/modules.html).
 
 
+## Installation
+
+CORE NEURON : No installation needed.  
 
 
 ## Options
 ## Options
 
 
-| parameter | required | default | choices | comment                                      |
-|-----------|----------|---------|---------|----------------------------------------------|
-| task_file | YES      |         |         | path to the Playbook file that contain tasks |
+| parameter     | required | default | choices      | comment                                                                                                                          |
+|---------------|----------|---------|--------------|----------------------------------------------------------------------------------------------------------------------------------|
+| task_file     | YES      |         |              | path to the Playbook file that contains tasks                                                                                    |
+| sudo          | NO       | FALSE   | True | False | If the playbook will require root privileges (become=true) , this must be set to True and sudo_user and password set accordingly |
+| sudo_user     | NO       |         |              | The target user with admin privileges. In most of case "root"                                                                    |
+| sudo_password | NO       |         |              | The password of the sudo_user                                                                                                    |
 
 
 
 
 
 
 ## Synapses example
 ## Synapses example
 
 
+### Playbook without admin privileges
+
 Call the playbook named playbook.yml
 Call the playbook named playbook.yml
 ```
 ```
   - name: "Ansible-test"
   - name: "Ansible-test"
@@ -28,7 +36,7 @@ Call the playbook named playbook.yml
       - ansible_playbook: 
       - ansible_playbook: 
           task_file: "playbook.yml"
           task_file: "playbook.yml"
       - say:
       - say:
-          message: "Tache terminée"    
+          message: "The task is done"
 ```
 ```
 
 
 Content of the playbook. This playbook will use the [URI module](http://docs.ansible.com/ansible/uri_module.html) to interact with a webservice on a remote server.
 Content of the playbook. This playbook will use the [URI module](http://docs.ansible.com/ansible/uri_module.html) to interact with a webservice on a remote server.
@@ -54,23 +62,42 @@ Content of the playbook. This playbook will use the [URI module](http://docs.ans
             {"app_name": "music", "state": "start"}
             {"app_name": "music", "state": "start"}
 ```
 ```
 
 
+### Playbook with admin privileges
+
+In some cases, a playbook requires sudo right to perform admin operations like installing a package.
+In this case, you must give to the neuron the login and password of the user which has admin privileges.
+```
+  - name: "Ansible-root"
+    signals:
+      - order: "playbook"
+    neurons:
+      - ansible_playbook:
+          task_file: "playbook-root.yml"
+          sudo: true,
+          sudo_user: "root"
+          sudo_password: "secret"
+```
+
+And the playbook would be. Notice that we use `become: True`
+```
+- hosts: localhost
+  gather_facts: no
+  connection: local
+  become: True
+
+  tasks:
+    - name: "Install a useful train package"
+      apt:
+        name: sl
+        state: present
+```
 
 
 ## Note
 ## Note
 
 
-Ansible contain a lot of modules that can be useful for Kalliope
+Ansible contains a lot of modules that can be useful for Kalliope
 
 
 - [Notification](http://docs.ansible.com/ansible/list_of_notification_modules.html): can be used to send a message to Pushbullet, IRC channel, Rocket Chat and a lot of other notification services
 - [Notification](http://docs.ansible.com/ansible/list_of_notification_modules.html): can be used to send a message to Pushbullet, IRC channel, Rocket Chat and a lot of other notification services
 - [Files](http://docs.ansible.com/ansible/list_of_files_modules.html): can be used to perform a backup or synchronize two file path
 - [Files](http://docs.ansible.com/ansible/list_of_files_modules.html): can be used to perform a backup or synchronize two file path
 - [Windows](http://docs.ansible.com/ansible/list_of_windows_modules.html): Can be used to control a Windows Desktop
 - [Windows](http://docs.ansible.com/ansible/list_of_windows_modules.html): Can be used to control a Windows Desktop
 
 
 Shell neuron or script neuron can perform same actions. Ansible is just a way to simplify some execution or enjoy some [already made plugin](http://docs.ansible.com/ansible/modules_by_category.html). 
 Shell neuron or script neuron can perform same actions. Ansible is just a way to simplify some execution or enjoy some [already made plugin](http://docs.ansible.com/ansible/modules_by_category.html). 
-
-Here is the example of synapse you would use to perform a call to a web service without Ansible:
-```
-- name: "start-music"
-    signals:
-      - order: "start music rock"
-    neurons:
-      - shell:
-          cmd: "curl -i --user admin:secret -H \"Content-Type: application/json\" -X POST -d '{\"app_name\":\"music\",\"state\":\"start\"}' http://192.168.0.17:8000/app"      
-```

+ 40 - 8
kalliope/neurons/ansible_playbook/ansible_playbook.py

@@ -1,4 +1,6 @@
 from collections import namedtuple
 from collections import namedtuple
+
+import logging
 from ansible.parsing.dataloader import DataLoader
 from ansible.parsing.dataloader import DataLoader
 from ansible.vars import VariableManager
 from ansible.vars import VariableManager
 from ansible.inventory import Inventory
 from ansible.inventory import Inventory
@@ -6,27 +8,28 @@ from ansible.executor.playbook_executor import PlaybookExecutor
 
 
 from kalliope.core.NeuronModule import NeuronModule, MissingParameterException
 from kalliope.core.NeuronModule import NeuronModule, MissingParameterException
 
 
+logging.basicConfig()
+logger = logging.getLogger("kalliope")
+
 
 
 class Ansible_playbook(NeuronModule):
 class Ansible_playbook(NeuronModule):
     def __init__(self, **kwargs):
     def __init__(self, **kwargs):
         super(Ansible_playbook, self).__init__(**kwargs)
         super(Ansible_playbook, self).__init__(**kwargs)
 
 
         self.task_file = kwargs.get('task_file', None)
         self.task_file = kwargs.get('task_file', None)
+        self.sudo = kwargs.get('sudo', False)
+        self.sudo_user = kwargs.get('sudo_user', False)
+        self.sudo_password = kwargs.get('sudo_password', False)
 
 
         # check if parameters have been provided
         # check if parameters have been provided
         if self._is_parameters_ok():
         if self._is_parameters_ok():
 
 
-            Options = namedtuple('Options',
-                                 ['connection', 'forks', 'become', 'become_method', 'become_user', 'check', 'listhosts',
-                                  'listtasks', 'listtags', 'syntax', 'module_path'])
-
             variable_manager = VariableManager()
             variable_manager = VariableManager()
             loader = DataLoader()
             loader = DataLoader()
-            options = Options(connection='local', forks=100, become=None, become_method=None, become_user=None, check=False,
-                              listhosts=False, listtasks=False, listtags=False, syntax=False, module_path="")
-            passwords = dict(vault_pass='secret')
+            options = self._get_options()
+            passwords = {'become_pass': self.sudo_password}
 
 
-            inventory = Inventory(loader=loader, variable_manager=variable_manager, host_list='localhost')
+            inventory = Inventory(loader=loader, variable_manager=variable_manager, host_list="localhost")
             variable_manager.set_inventory(inventory)
             variable_manager.set_inventory(inventory)
             playbooks = [self.task_file]
             playbooks = [self.task_file]
 
 
@@ -43,4 +46,33 @@ class Ansible_playbook(NeuronModule):
     def _is_parameters_ok(self):
     def _is_parameters_ok(self):
         if self.task_file is None:
         if self.task_file is None:
             raise MissingParameterException("task_file parameter required")
             raise MissingParameterException("task_file parameter required")
+
+        # check if the user want to use sudo for root privileges
+        if self.sudo:
+            # the user must set a login and password
+            if not self.sudo_user:
+                raise MissingParameterException("sudo_user parameter required with sudo True")
+            if not self.sudo_password:
+                raise MissingParameterException("sudo_password parameter required with sudo True")
+
         return True
         return True
+
+    def _get_options(self):
+        """
+        Return a valid dict of option usable by Ansible depending on the sudo value if set
+        :return: dict of option
+        """
+        Options = namedtuple('Options',
+                             ['connection', 'forks', 'become', 'become_method', 'become_user', 'check', 'listhosts',
+                              'listtasks', 'listtags', 'syntax', 'module_path'])
+        if self.sudo:
+            options = Options(connection='local', forks=100, become=True, become_method="sudo",
+                              become_user=self.sudo_user, check=False, listhosts=False, listtasks=False, listtags=False,
+                              syntax=False, module_path="")
+        else:
+            options = Options(connection='local', forks=100, become=None, become_method=None, become_user=None,
+                              check=False, listhosts=False, listtasks=False, listtags=False, syntax=False,
+                              module_path="")
+
+        logger.debug("Ansible options: %s" % str(options))
+        return options

+ 0 - 68
kalliope/neurons/gmail_checker/README.md

@@ -1,68 +0,0 @@
-# gmail_checker
-
-## Synopsis
-
-This neuron access to Gmail and gives the number of unread mails and their titles.
-
-## Options
-
-| parameter | required | default | choices | comment    |
-|-----------|----------|---------|---------|------------|
-| username  | YES      |         |         | User info. |
-| password  | YES      |         |         | User info. |
-
-## Return Values
-
-| Name     | Description                                  | Type | sample                                                       |
-|----------|----------------------------------------------|------|--------------------------------------------------------------|
-| unread   | Number of unread messages                    | int  | 5                                                            |
-| subjects | A List with all the unread messages subjects | list | ['Kalliope commit', 'Beer tonight?', 'cats have superpower'] |
-
-## Synapses example
-
-Simple example : 
-
-```
-  - name: "check-email"
-    signals:
-      - order: "Do I have emails"
-    neurons:
-      - gmail_checker:
-          username: "me@gmail.com"
-          password: "my_password"
-          say_template: 
-            -  "You have {{ unread }} new emails"    
-```
-
-A complex example that read subject emails. This is based on a file_template
-```
-  - name: "check-email"
-    signals:
-      - order: "Do I have emails"
-    neurons:
-      - gmail_checker:
-          username: "me@gmail.com"
-          password: "my_password"
-          file_template: /templates/my_email_template.j2
-```
-
-Here the content of the `my_email_template.j2`
-```
-You have {{ unread }} email
-
-{% set count = 1 %}
-{% if unread > 0 %}
-    {% for subject in subjects %}
-     email number {{ count }}. {{ subject }}
-     {% set count = count + 1 %}
-    {% endfor %}
-{% endif %}
-```
-## Notes
-
-Gmail now prevent some mailbox to be accessed from tier application. If you receive a mail like the following:
-```
-Sign-in attempt prevented ... Someone just tried to sign in to your Google Account mail@gmail.com from an app that doesn't meet modern security standards.
-```
-
-You can allow this neuron to get un access to your email in your [Gmail account settings](https://www.google.com/settings/security/lesssecureapps).

+ 0 - 1
kalliope/neurons/gmail_checker/__init__.py

@@ -1 +0,0 @@
-from gmail_checker import Gmail_checker

+ 0 - 92
kalliope/neurons/gmail_checker/gmail_checker.py

@@ -1,92 +0,0 @@
-# -*- coding: utf-8 -*-
-import logging
-
-from gmail import Gmail
-from email.header import decode_header
-from kalliope.core.NeuronModule import NeuronModule, MissingParameterException
-
-logging.basicConfig()
-logger = logging.getLogger("kalliope")
-
-
-class Gmail_checker(NeuronModule):
-    def __init__(self, **kwargs):
-        super(Gmail_checker, self).__init__(**kwargs)
-
-        self.username = kwargs.get('username', None)
-        self.password = kwargs.get('password', None)
-
-        # check if parameters have been provided
-        if self._is_parameters_ok():
-
-            # prepare a returned dict
-            returned_dict = dict()
-
-            g = Gmail()
-            g.login(self.username, self.password)
-
-            # check if login succeed
-            logging.debug("Gmail loggin ok: %s" % g.logged_in)  # Should be True, AuthenticationError if login fails
-
-            # get unread mail
-            unread = g.inbox().mail(unread=True)
-
-            returned_dict["unread"] = len(unread)
-
-            if len(unread) > 0:
-                # add a list of subject
-                subject_list = list()
-                for email in unread:
-                    email.fetch()
-                    encoded_subject = email.subject
-                    subject = self._parse_subject(encoded_subject)
-                    subject_list.append(subject)
-
-                returned_dict["subjects"] = subject_list
-
-            logger.debug("gmail neuron returned dict: %s" % str(returned_dict))
-
-            # logout of gmail
-            g.logout()
-            self.say(returned_dict)
-
-    def _parse_subject(self, encoded_subject):
-        dh = decode_header(encoded_subject)
-
-        return ''.join([self.try_parse(t[0], t[1]) for t in dh])
-
-    @staticmethod
-    def try_parse(header, encoding):
-        """
-        Verifying the Encoding and return unicode
-
-        :param header: the header to decode
-        :param encoding: the targeted encoding
-        :return: either 'ASCII' or 'ISO-8859-1' or 'UTF-8'
-
-        .. raises:: UnicodeDecodeError
-        """
-        if encoding is None:
-            encoding = 'ASCII'
-        try:
-            return unicode(header, encoding)
-        except UnicodeDecodeError:
-            try:
-                return unicode(header, 'ISO-8859-1')
-            except UnicodeDecodeError:
-                return unicode(header, 'UTF-8')
-
-    def _is_parameters_ok(self):
-        """
-        Check if received parameters are ok to perform operations in the neuron
-        :return: true if parameters are ok, raise an exception otherwise
-
-        .. raises:: MissingParameterException
-        """
-        if self.username is None:
-            raise MissingParameterException("Username parameter required")
-
-        if self.password is None:
-            raise MissingParameterException("Password parameter required")
-
-        return True

+ 0 - 0
kalliope/neurons/gmail_checker/tests/__init__.py


+ 0 - 37
kalliope/neurons/gmail_checker/tests/test_gmail_checker.py

@@ -1,37 +0,0 @@
-import unittest
-
-from kalliope.core.NeuronModule import MissingParameterException
-from kalliope.neurons.gmail_checker.gmail_checker import Gmail_checker
-
-
-class TestGmail_Checker(unittest.TestCase):
-
-    def setUp(self):
-        self.username="username"
-        self.password="password"
-
-    def testParameters(self):
-        def run_test(parameters_to_test):
-            with self.assertRaises(MissingParameterException):
-                Gmail_checker(**parameters_to_test)
-
-        # empty
-        parameters = dict()
-        run_test(parameters)
-
-        # missing password
-        parameters = {
-            "username": self.username
-        }
-        run_test(parameters)
-
-        # missing username
-        parameters = {
-           "password": self.password
-        }
-        run_test(parameters)
-
-
-if __name__ == '__main__':
-    unittest.main()
-

+ 4 - 0
kalliope/neurons/kill_switch/README.md

@@ -4,6 +4,10 @@
 
 
 This neuron exits the Kalliope process.
 This neuron exits the Kalliope process.
 
 
+## Installation
+
+CORE NEURON : No installation needed.  
+
 ## Options
 ## Options
 
 
 No parameters
 No parameters

+ 4 - 0
kalliope/neurons/neurotransmitter/README.md

@@ -4,6 +4,10 @@
 
 
 Link synapses together. Call a synapse directly or depending on the captured speech from the user.
 Link synapses together. Call a synapse directly or depending on the captured speech from the user.
 
 
+## Installation
+
+CORE NEURON : No installation needed.  
+
 ## Options
 ## Options
 
 
 | parameter        | required | default | choices | comment                                                                                           |
 | parameter        | required | default | choices | comment                                                                                           |

+ 8 - 6
kalliope/neurons/neurotransmitter/neurotransmitter.py

@@ -1,6 +1,6 @@
 import logging
 import logging
 
 
-from kalliope.core.NeuronModule import NeuronModule, InvalidParameterException
+from kalliope.core.NeuronModule import NeuronModule, MissingParameterException, InvalidParameterException
 
 
 logging.basicConfig()
 logging.basicConfig()
 logger = logging.getLogger("kalliope")
 logger = logging.getLogger("kalliope")
@@ -40,7 +40,9 @@ class Neurotransmitter(NeuronModule):
             for el in self.from_answer_link:
             for el in self.from_answer_link:
                 for answer in el["answers"]:
                 for answer in el["answers"]:
                     if self.is_order_matching(audio, answer):
                     if self.is_order_matching(audio, answer):
-                        found = self.run_synapse_by_name_with_order(audio, el["synapse"], order_template=answer)
+                        found = self.run_synapse_by_name_with_order(order=audio,
+                                                                    synapse_name=el["synapse"],
+                                                                    order_template=answer)
             if not found: # the answer do not correspond to any answer. We run the default synapse
             if not found: # the answer do not correspond to any answer. We run the default synapse
                 self.run_synapse_by_name(self.default)
                 self.run_synapse_by_name(self.default)
 
 
@@ -49,7 +51,7 @@ class Neurotransmitter(NeuronModule):
         Check if received links are ok to perform operations
         Check if received links are ok to perform operations
         :return: true if the neuron is well configured, raise an exception otherwise
         :return: true if the neuron is well configured, raise an exception otherwise
 
 
-        .. raises:: MissingParameterException
+        .. raises:: MissingParameterException, InvalidParameterException
         """
         """
         # with the neuron the user has the choice of a direct link that call another synapse,
         # with the neuron the user has the choice of a direct link that call another synapse,
         #  or a link with an answer caught from the STT engine
         #  or a link with an answer caught from the STT engine
@@ -59,15 +61,15 @@ class Neurotransmitter(NeuronModule):
             raise InvalidParameterException("neurotransmitter cannot be used with both direct_link and from_answer_link")
             raise InvalidParameterException("neurotransmitter cannot be used with both direct_link and from_answer_link")
 
 
         if self.direct_link is None and self.from_answer_link is None:
         if self.direct_link is None and self.from_answer_link is None:
-            raise InvalidParameterException("neurotransmitter must be used with direct_link or from_answer_link")
+            raise MissingParameterException("neurotransmitter must be used with direct_link or from_answer_link")
 
 
         if self.from_answer_link is not None:
         if self.from_answer_link is not None:
             if self.default is None:
             if self.default is None:
                 raise InvalidParameterException("default parameter is required and must contain a valid synapse name")
                 raise InvalidParameterException("default parameter is required and must contain a valid synapse name")
             for el in self.from_answer_link:
             for el in self.from_answer_link:
                 if "synapse" not in el:
                 if "synapse" not in el:
-                    raise InvalidParameterException("Links must contain a synapse name: %s" % el)
+                    raise MissingParameterException("Links must contain a synapse name: %s" % el)
                 if "answers" not in el:
                 if "answers" not in el:
-                    raise InvalidParameterException("Links must contain answers: %s" % el)
+                    raise MissingParameterException("Links must contain answers: %s" % el)
 
 
         return True
         return True

+ 154 - 0
kalliope/neurons/neurotransmitter/tests/test_neurotransmitter.py

@@ -0,0 +1,154 @@
+import unittest
+
+from mock import mock
+
+from kalliope.core.NeuronModule import NeuronModule, MissingParameterException, InvalidParameterException
+from kalliope.neurons.neurotransmitter import Neurotransmitter
+
+
+class TestNeurotransmitter(unittest.TestCase):
+
+    def setUp(self):
+        self.from_answer_link = [
+            {
+                "synapse": "synapse2",
+                "answers": [
+                    "answer one"
+                ]
+            },
+            {
+                "synapse": "synapse3",
+                "answers": [
+                    "answer two",
+                    "answer three"
+                ]
+            },
+        ]
+        self.direct_link = "direct_link"
+        self.default = "default"
+
+    def testParameters(self):
+        """
+        Testing the Parameters checking
+        """
+        def run_test_InvalidParameterException(parameters_to_test):
+            with self.assertRaises(InvalidParameterException):
+                Neurotransmitter(**parameters_to_test)
+
+        def run_test_MissingParameterException(parameters_to_test):
+            with self.assertRaises(MissingParameterException):
+                Neurotransmitter(**parameters_to_test)
+
+        # empty
+        parameters = dict()
+        run_test_MissingParameterException(parameters)
+
+        # missing direct_link and from_answer_link
+        parameters = {
+            "default": self.default
+        }
+        run_test_MissingParameterException(parameters)
+
+        # missing direct_link and from_answer_link
+        parameters = {
+            "default": self.default,
+            "from_answer_link": self.from_answer_link,
+            "direct_link": self.direct_link
+        }
+        run_test_InvalidParameterException(parameters)
+
+        # missing default
+        parameters = {
+            "from_answer_link": self.from_answer_link,
+            "direct_link": self.direct_link
+        }
+        run_test_InvalidParameterException(parameters)
+
+        # Missing answer in from_answer_link
+        self.from_answer_link = [
+            {
+                "synapse": "synapse2",
+            }
+        ]
+
+        parameters = {
+            "default": self.default,
+            "from_answer_link": self.from_answer_link
+        }
+        run_test_MissingParameterException(parameters)
+
+        # Missing synapse in from_answer_link
+        self.from_answer_link = [
+            {
+                "answer": "blablablbla",
+            }
+        ]
+
+        parameters = {
+            "default": self.default,
+            "from_answer_link": self.from_answer_link
+        }
+        run_test_MissingParameterException(parameters)
+
+    def testCallback(self):
+        """
+        Testing the callback provided when audio has been provided by the User as an answer.
+        """
+        parameters = {
+            "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:
+                # 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")
+
+    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:
+            # Test direct link
+            parameters = {
+                "default": self.default,
+                "direct_link": self.direct_link
+            }
+            nt = 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:
+            # Test get_audio_from_stt
+            parameters = {
+                "default": self.default,
+                "from_answer_link": self.from_answer_link,
+            }
+            nt = Neurotransmitter(**parameters)
+            mock_get_audio_from_stt.assert_called_once()
+
+
+
+
+

+ 0 - 93
kalliope/neurons/openweathermap/README.md

@@ -1,93 +0,0 @@
-# OpenWeatherMap API
-
-## Synopsis 
-
-Give the today and tomorrow weather with the related data (humidity, temperature, etc ...) for a given location. 
-
-## Options
-
-| parameter | required | default | choices                     | comment                                                                                           |
-|-----------|----------|---------|-----------------------------|---------------------------------------------------------------------------------------------------|
-| api_key   | YES      | None    |                             | User API key of the OWM API                                                                       |
-| location  | YES      | None    |                             | The location                                                                                      |
-| lang      | No       | en      | multiple                    | First 2 letters cf : section Multilingual support in : [lang](https://openweathermap.org/current) |
-| temp_unit | No       | Kelvin  | Celsius, Kelvin, Fahrenheit |                                                                                                   |
-| country   | No       | US      | multiple                    |  Frist 2 letters of the country cf API doc                                                        |
-
-## Return Values
-
-| Name                        | Description                                | Type   | sample                 |
-|-----------------------------|--------------------------------------------|--------|------------------------|
-| location                    | The current location                       | String | Grenoble               |
-| weather_today               | Today : The weather sentence               | String | cloudy                 |
-| sunset_today_time           | Today : The sunset time (iso)              | String | 2016-10-15 20:07:57+00 |
-| sunrise_today_time          | Today : The sunrise time (iso)             | String | 2016-10-15 07:07:57+00 |
-| temp_today_temp             | Today : Average temperature                | float  | 25                     |
-| temp_today_temp_max         | Today : Max temperature                    | float  | 45                     |
-| temp_today_temp_min         | Today : Min temperatue                     | float  | 5                      |
-| pressure_today_press        | Today : Pressure                           | float  | 1009                   |
-| pressure_today_sea_level    | Today : Pressure at the Sea level          | float  | 1038.381               |
-| humidity_today              | Today : % of humidity                      | float  | 60                     |
-| wind_today_deg              | Today : Direction of the wind in degree    | float  | 45                     |
-| wind_today_speed            | Today : Wind speed                         | float  | 2.66                   |
-| snow_today                  | Today : Volume of snow                     | float  | 0                      |
-| rain_today                  | Today : Rain volume                        | float  | 0                      |
-| clouds_coverage_today       | Today : % Cloud coverage                   | float  | 65                     |
-| weather_tomorrow            | Tomorrow : The weather sentence            | String | sunny                  |
-| sunset_time_tomorrow        | Tomorrow : The sunset time (iso)           | String | 2016-10-16 20:07:57+00 |
-| sunrise_time_tomorrow       | Tomorrow : The sunrise time (iso)          | String | 2016-10-16 07:07:57+00 |
-| temp_tomorrow_temp          | Tomorrow : Average temperature             | float  | 25                     |
-| temp_tomorrow_temp_max      | Tomorrow : Max temperature                 | float  | 45                     |
-| temp_tomorrow_temp_min      | Tomorrow : Min temperatue                  | float  | 5                      |
-| pressure_tomorrow_press     | Tomorrow : Pressure                        | float  | 1009                   |
-| pressure_tomorrow_sea_level | Tomorrow : Pressure at the Sea level       | float  | 1038.381               |
-| humidity_tomorrow           | Tomorrow : % of humidity                   | float  | 60                     |
-| wind_tomorrow_deg           | Tomorrow : Direction of the wind in degree | float  | 45                     |
-| wind_tomorrow_speed         | Tomorrow : Wind speed                      | float  | 2.66                   |
-| snow_tomorrow               | Tomorrow : Volume of snow                  | float  | 0                      |
-| rain_tomorrow               | Tomorrow : Rain volume                     | float  | 0                      |
-| clouds_coverage_tomorrow    | Tomorrow : % Cloud coverage                | float  | 65                     |
-
-## Synapses example
-
-```
-  - name: "getthe-weather"
-    signals:
-      - order: "what is the weather in {{ location }}"
-    neurons:
-      - openweathermap:
-          api_key: "fdfba4097c318aed7836b2a85a6a05ef"
-          lang: "en"
-          temp_unit: "celsius"
-          say_template:
-          - "Today in {{ location }} the weather is {{ weather_today }} with a temperature of {{ temp_today_temp }} degree and tomorrow the weather will be {{ weather_tomorrow }} with a temperature of {{ temp_tomorrow_temp }} degree"
-          args:
-          - location
-```
-
-You also can define the "location" args directly in neuron argument list. 
-```
-  - name: "get-the-weather"
-    signals:
-      - order: "quel temps fait-il"
-    neurons:
-      - openweathermap:
-          api_key: "fdfba4097c318aed7836b2a85a6a05ef"
-          lang: "fr"
-          temp_unit: "celsius"
-          location : "grenoble"
-          country: "FR"
-          say_template:
-          - "Aujourd'hui a {{ location }} le temps est {{ weather_today }} avec une température de {{ temp_today_temp }} degrés et demain le temps sera {{ weather_tomorrow }} avec une température de {{ temp_tomorrow_temp }} degrés"
-```
-
-## Templates example 
-
-```
-Today in {{ location }} the weather is {{ weather_today }} with a temperature of {{ temp_today_temp }} degree
-```
-
-
-## Notes
-
-> **Note:** You need to create a free account on [openweathermap.org](http://openweathermap.org/) to get your API key.

+ 0 - 1
kalliope/neurons/openweathermap/__init__.py

@@ -1 +0,0 @@
-from openweathermap import Openweathermap

+ 0 - 127
kalliope/neurons/openweathermap/openweathermap.py

@@ -1,127 +0,0 @@
-import pyowm
-
-from kalliope.core.NeuronModule import NeuronModule, MissingParameterException
-
-
-class Openweathermap(NeuronModule):
-    def __init__(self, **kwargs):
-        # get message to spell out loud
-        super(Openweathermap, self).__init__(**kwargs)
-
-        self.api_key = kwargs.get('api_key', None)
-        self.location = kwargs.get('location', None)
-        self.lang = kwargs.get('lang', 'en')
-        self.temp_unit = kwargs.get('temp_unit', 'celsius')
-        self.country = kwargs.get('country', None)
-
-        # check if parameters have been provided
-        if self._is_parameters_ok():
-            extended_location = self.location
-            if self.country is not None:
-                extended_location = self.location + "," + self.country
-
-
-            owm = pyowm.OWM(API_key=self.api_key, language=self.lang)
-
-            # Tomorrow
-            forecast = owm.daily_forecast(extended_location)
-            tomorrow = pyowm.timeutils.tomorrow()
-            weather_tomorrow = forecast.get_weather_at(tomorrow)
-            weather_tomorrow_status = weather_tomorrow.get_detailed_status()
-            sunset_time_tomorrow = weather_tomorrow.get_sunset_time('iso')
-            sunrise_time_tomorrow = weather_tomorrow.get_sunrise_time('iso')
-
-            temp_tomorrow = weather_tomorrow.get_temperature(unit=self.temp_unit)
-            temp_tomorrow_temp = temp_tomorrow['day']
-            temp_tomorrow_temp_max = temp_tomorrow['max']
-            temp_tomorrow_temp_min = temp_tomorrow['min']
-
-            pressure_tomorrow = weather_tomorrow.get_pressure()
-            pressure_tomorrow_press = pressure_tomorrow['press']
-            pressure_tomorrow_sea_level = pressure_tomorrow['sea_level']
-
-            humidity_tomorrow = weather_tomorrow.get_humidity()
-
-            wind_tomorrow = weather_tomorrow.get_wind()
-            # wind_tomorrow_deg = wind_tomorrow['deg']
-            wind_tomorrow_speed = wind_tomorrow['speed']
-
-            snow_tomorrow = weather_tomorrow.get_snow()
-            rain_tomorrow = weather_tomorrow.get_rain()
-            clouds_coverage_tomorrow = weather_tomorrow.get_clouds()
-
-            # Today
-            observation = owm.weather_at_place(extended_location)
-            weather_today = observation.get_weather()
-            weather_today_status = weather_today.get_detailed_status()
-            sunset_time_today = weather_today.get_sunset_time('iso')
-            sunrise_time_today = weather_today.get_sunrise_time('iso')
-
-            temp_today = weather_today.get_temperature(unit=self.temp_unit)
-            temp_today_temp = temp_today['temp']
-            temp_today_temp_max = temp_today['temp_max']
-            temp_today_temp_min = temp_today['temp_min']
-
-            pressure_today = weather_today.get_pressure()
-            pressure_today_press = pressure_today['press']
-            pressure_today_sea_level = pressure_today['sea_level']
-
-            humidity_today = weather_today.get_humidity()
-
-            wind_today= weather_today.get_wind()
-            wind_today_deg = wind_today['deg']
-            wind_today_speed = wind_today['speed']
-
-            snow_today = weather_today.get_snow()
-            rain_today = weather_today.get_rain()
-            clouds_coverage_today = weather_today.get_clouds()
-
-            message = {
-                "location": self.location,
-
-                "weather_today": weather_today_status,
-                "sunset_today_time": sunset_time_today,
-                "sunrise_today_time": sunrise_time_today,
-                "temp_today_temp": temp_today_temp,
-                "temp_today_temp_max": temp_today_temp_max,
-                "temp_today_temp_min": temp_today_temp_min,
-                "pressure_today_press": pressure_today_press,
-                "pressure_today_sea_level": pressure_today_sea_level,
-                "humidity_today": humidity_today,
-                "wind_today_deg": wind_today_deg,
-                "wind_today_speed": wind_today_speed,
-                "snow_today": snow_today,
-                "rain_today": rain_today,
-                "clouds_coverage_today": clouds_coverage_today,
-
-                "weather_tomorrow": weather_tomorrow_status,
-                "sunset_time_tomorrow": sunset_time_tomorrow,
-                "sunrise_time_tomorrow": sunrise_time_tomorrow,
-                "temp_tomorrow_temp": temp_tomorrow_temp,
-                "temp_tomorrow_temp_max": temp_tomorrow_temp_max,
-                "temp_tomorrow_temp_min": temp_tomorrow_temp_min,
-                "pressure_tomorrow_press": pressure_tomorrow_press,
-                "pressure_tomorrow_sea_level": pressure_tomorrow_sea_level,
-                "humidity_tomorrow": humidity_tomorrow,
-                # "wind_tomorrow_deg": wind_tomorrow_deg,
-                "wind_tomorrow_speed": wind_tomorrow_speed,
-                "snow_tomorrow": snow_tomorrow,
-                "rain_tomorrow": rain_tomorrow,
-                "clouds_coverage_tomorrow": clouds_coverage_tomorrow
-            }
-
-            self.say(message)
-
-    def _is_parameters_ok(self):
-        """
-        Check if received parameters are ok to perform operations in the neuron
-        :return: true if parameters are ok, raise an exception otherwise
-
-        .. raises:: NotImplementedError
-        """
-        if self.api_key is None:
-            raise MissingParameterException("OpenWeatherMap neuron needs an api_key")
-        if self.location is None:
-            raise MissingParameterException("OpenWeatherMap neuron needs a location")
-
-        return True

+ 0 - 0
kalliope/neurons/openweathermap/tests/__init__.py


+ 0 - 36
kalliope/neurons/openweathermap/tests/test_openweathermap.py

@@ -1,36 +0,0 @@
-import unittest
-
-from kalliope.core.NeuronModule import MissingParameterException
-from kalliope.neurons.openweathermap.openweathermap import Openweathermap
-
-
-class TestOpenWeatherMap(unittest.TestCase):
-
-    def setUp(self):
-        self.location="location"
-        self.api_key="api_key"
-
-    def testParameters(self):
-        def run_test(parameters_to_test):
-            with self.assertRaises(MissingParameterException):
-                Openweathermap(**parameters_to_test)
-
-        # empty
-        parameters = dict()
-        run_test(parameters)
-
-        # missing api_key
-        parameters = {
-            "location": self.location
-        }
-        run_test(parameters)
-
-        # missing location
-        parameters = {
-           "api_key": self.api_key
-        }
-        run_test(parameters)
-
-
-if __name__ == '__main__':
-    unittest.main()

+ 0 - 49
kalliope/neurons/push_message/Readme.md

@@ -1,49 +0,0 @@
-# Push notification
-
-## Synopsis
-
-Send broadcast communications to groups of subscribers.
-
-Available client are:
-- Android phone
-- iOS phone/
-- Windows Phone
-- Chrome Browser
-
-This neuron is based on [Pushetta API](http://www.pushetta.com/). 
-You need to [create a free account](http://www.pushetta.com/accounts/signup/) and a chanel before using it.
-You need to install a [client App](http://www.pushetta.com/pushetta-downloads/) on the target device.
-
-## Options
-
-| parameter    | requiered | default | choices | comment                                                                                               |
-|--------------|-----------|---------|---------|-------------------------------------------------------------------------------------------------------|
-| message      | yes       |         |         | Message that will be send to the android phone                                                        |
-| api_key      | yes       |         |         | Token API key availlable from [Pushetta dashboard](http://www.pushetta.com/my/dashboard/) |
-| channel_name | yes       |         |         | Name of the subscribed [channel](http://www.pushetta.com/pushetta-docs/#create)                       |
-
-
-## Return Values
-
-No returned value
-
-
-## Synapses example
-
-The following synapse will send a push message to device that have subscribed to the channel name "my_chanel_name" when you say "push message".
-```
- - name: "send-push-message"
-   signals:
-      - order: "push message"
-   neurons:
-     - android_pushetta:
-         message: "Message to send"
-         api_key: "TOEKENEXAMPLE1234"
-         channel_name: "my_chanel_name"    
-```
-
-## Notes
-
-> **Note:** You must install a [client App](http://www.pushetta.com/pushetta-downloads/) on the target device.
-
-> **Note:** You must create a channel an get a token key on [Pushetta website](http://www.pushetta.com/) before using the neuron.

+ 0 - 2
kalliope/neurons/push_message/__init__.py

@@ -1,2 +0,0 @@
-from push_message import Push_message
-

+ 0 - 43
kalliope/neurons/push_message/push_message.py

@@ -1,43 +0,0 @@
-from __future__ import absolute_import
-from pushetta import Pushetta
-
-from kalliope.core.NeuronModule import NeuronModule, MissingParameterException
-
-
-class Push_message(NeuronModule):
-    """
-    Neuron based on pushetta api. http://www.pushetta.com/
-    """
-    def __init__(self, **kwargs):
-        """
-        Send a push message to an android phone via Pushetta API
-        :param message: Message to send
-        :param api_key: The Pushetta service secret token
-        :param channel_name: Pushetta channel name
-        """
-        super(Push_message, self).__init__(**kwargs)
-
-        self.message = kwargs.get('message', None)
-        self.api_key = kwargs.get('api_key', None)
-        self.channel_name = kwargs.get('channel_name', None)
-
-        # check if parameters have been provided
-        if self._is_parameters_ok():
-            p = Pushetta(self.api_key)
-            p.pushMessage(self.channel_name, self.message)
-
-    def _is_parameters_ok(self):
-        """
-        Check if received parameters are ok to perform operations in the neuron
-        :return: true if parameters are ok, raise an exception otherwise
-
-        .. raises:: NotImplementedError
-        """
-        if self.message is None:
-            raise MissingParameterException("Pushetta neuron needs message to send")
-        if self.api_key is None:
-            raise MissingParameterException("Pushetta neuron needs api_key")
-        if self.channel_name is None:
-            raise MissingParameterException("Pushetta neuron needs channel_name")
-
-        return True

+ 0 - 0
kalliope/neurons/push_message/tests/__init__.py


+ 0 - 46
kalliope/neurons/push_message/tests/test_push_message.py

@@ -1,46 +0,0 @@
-import unittest
-
-from kalliope.core.NeuronModule import MissingParameterException
-from kalliope.neurons.push_message.push_message import Push_message
-
-
-class TestPush_Message(unittest.TestCase):
-
-    def setUp(self):
-        self.message="message"
-        self.api_key="api_key"
-        self.channel_name = "channel_name"
-
-    def testParameters(self):
-        def run_test(parameters_to_test):
-            with self.assertRaises(MissingParameterException):
-                Push_message(**parameters_to_test)
-
-        # empty
-        parameters = dict()
-        run_test(parameters)
-
-        # missing api_key
-        parameters = {
-            "message": self.message,
-            "channel_name": self.channel_name
-        }
-        run_test(parameters)
-
-        # missing channel_name
-        parameters = {
-           "api_key": self.api_key,
-            "message":self.message
-        }
-        run_test(parameters)
-
-        # missing message
-        parameters = {
-            "api_key": self.api_key,
-            "channel_name": self.channel_name
-        }
-        run_test(parameters)
-
-
-if __name__ == '__main__':
-    unittest.main()

+ 0 - 59
kalliope/neurons/rss_reader/README.md

@@ -1,59 +0,0 @@
-# rss_reader
-
-## Synopsis
-
-This neuron access to a RSS feed and gives their items.
-
-## Options
-
-| parameter | required | default | choices | comment               |
-|-----------|----------|---------|---------|-----------------------|
-| feed_url  | YES      |         |         | Url of the feed.      |
-| max_items | NO       | 30      |         | Max items to returns. |
-
-## Return Values
-
-| Name     | Description                                                                            | Type    | sample                          |
-|----------|----------------------------------------------------------------------------------------|---------|---------------------------------|
-| feed     | Title of the feed                                                                      | string  | The Verge                       |
-| items    | A List with feed items (see [RSS spec](https://validator.w3.org/feed/docs/rss2.html))  | list    |                                 |
-
-## Synapses example
-
-Simple example. This is based on a file_template
-
-```
-  - name: "news-theVerge"
-    signals:
-      - order: "What are the news from the verge ?"
-    neurons:
-      - rss_reader:
-          feed_url: "http://www.theverge.com/rss/index.xml"
-          file_template: templates/en_rss.j2
-          
-```
-
-A example with max items set to 10. This is based on a file_template
-```
-  - name: "news-sport"
-    signals:
-      - order: "What are the sport news ?"
-    neurons:
-      - rss_reader:
-          feed_url: "https://sports.yahoo.com/top/rss.xml"
-          max_items: 10
-          file_template: templates/en_rss.j2    
-```
-
-Here the content of the `en_rss.j2`
-```
-Here's the news from {{ feed }}
-
-{% set count = 1 %}
-{% for item in items %}
-News {{ count }}. {{ item.title }}.
-{% set count = count + 1 %}
-{% endfor %}
-```
-## Notes
-

+ 0 - 1
kalliope/neurons/rss_reader/__init__.py

@@ -1 +0,0 @@
-from rss_reader import Rss_reader

+ 0 - 45
kalliope/neurons/rss_reader/rss_reader.py

@@ -1,45 +0,0 @@
-# -*- coding: utf-8 -*-
-import logging
-import feedparser
-
-from kalliope.core.NeuronModule import NeuronModule, MissingParameterException
-
-logging.basicConfig()
-logger = logging.getLogger("kalliope")
-
-
-class Rss_reader(NeuronModule):
-    def __init__(self, **kwargs):
-        super(Rss_reader, self).__init__(**kwargs)
-
-        self.feedUrl = kwargs.get('feed_url', None)
-        self.limit = kwargs.get('max_items', 30)
-
-        # check if parameters have been provided
-        if self._is_parameters_ok():
-
-            # prepare a returned dict
-            returned_dict = dict()
-
-            logging.debug("Reading feed from: %s" % self.feedUrl)
-
-            feed = feedparser.parse( self.feedUrl )
-
-            logging.debug("Read title from feed: %s" % feed["channel"]["title"])
-
-            returned_dict["feed"] = feed["channel"]["title"]
-            returned_dict["items"] = feed["items"][:self.limit]
-            
-            self.say(returned_dict)
-
-    def _is_parameters_ok(self):
-        """
-        Check if received parameters are ok to perform operations in the neuron
-        :return: true if parameters are ok, raise an exception otherwise
-
-        .. raises:: MissingParameterException
-        """
-        if self.feedUrl is None:
-            raise MissingParameterException("feed url parameter required")
-
-        return True

+ 0 - 0
kalliope/neurons/rss_reader/tests/__init__.py


+ 0 - 23
kalliope/neurons/rss_reader/tests/test_rss_reader.py

@@ -1,23 +0,0 @@
-import unittest
-
-from kalliope.core.NeuronModule import MissingParameterException
-from kalliope.neurons.rss_reader.rss_reader import Rss_reader
-
-
-class TestRss_Reader(unittest.TestCase):
-
-    def setUp(self):
-        self.feedUrl="http://www.lemonde.fr/rss/une.xml"
-
-    def testParameters(self):
-        def run_test(parameters_to_test):
-            with self.assertRaises(MissingParameterException):
-                Rss_reader(**parameters_to_test)
-
-        # empty
-        parameters = dict()
-        run_test(parameters)
-
-if __name__ == '__main__':
-    unittest.main()
-

+ 4 - 0
kalliope/neurons/say/README.md

@@ -4,6 +4,10 @@
 
 
 This neuron is the mouth of Kalliope and uses the [TTS](../../Docs/tts.md) to say the given message.
 This neuron is the mouth of Kalliope and uses the [TTS](../../Docs/tts.md) to say the given message.
 
 
+## Installation
+
+CORE NEURON : No installation needed.  
+
 ## Options
 ## Options
 
 
 | parameter | required | default | choices | comment                                |
 | parameter | required | default | choices | comment                                |

+ 4 - 0
kalliope/neurons/script/README.md

@@ -4,6 +4,10 @@
 
 
 This neuron runs a script located on the Kalliope system.
 This neuron runs a script located on the Kalliope system.
 
 
+## Installation
+
+CORE NEURON : No installation needed.  
+
 ## Options
 ## Options
 
 
 | parameter | required | default | choices | comment                                                                    |
 | parameter | required | default | choices | comment                                                                    |

+ 3 - 0
kalliope/neurons/shell/README.md

@@ -4,6 +4,9 @@
 
 
 Run a shell command on the local system where Kalliope is installed.
 Run a shell command on the local system where Kalliope is installed.
 
 
+## Installation
+
+CORE NEURON : No installation needed.  
 
 
 ## Options
 ## Options
 
 

+ 4 - 0
kalliope/neurons/sleep/README.md

@@ -4,6 +4,10 @@
 
 
 This neuron sleeps the system for a given time in seconds.
 This neuron sleeps the system for a given time in seconds.
 
 
+## Installation
+
+CORE NEURON : No installation needed.  
+
 ## Options
 ## Options
 
 
 | parameter | required | default | choices | comment                         |
 | parameter | required | default | choices | comment                         |

+ 4 - 0
kalliope/neurons/systemdate/README.md

@@ -4,6 +4,10 @@
 
 
 Give the current time from the system where Kalliope is installed. Return a dict of parameters that can be used in a template.
 Give the current time from the system where Kalliope is installed. Return a dict of parameters that can be used in a template.
 
 
+## Installation
+
+CORE NEURON : No installation needed.  
+
 ## Options
 ## Options
 
 
 | parameter     | required | default | choices     | comment                                                               |
 | parameter     | required | default | choices     | comment                                                               |

+ 0 - 83
kalliope/neurons/tasker_autoremote/Readme.md

@@ -1,83 +0,0 @@
-# Tasker autoremote
-
-## Synopsis
-
-[Tasker](https://play.google.com/store/apps/details?id=net.dinglisch.android.taskerm) is an application for Android which performs 
-tasks (sets of actions) based on contexts (application, time, date, location, event, gesture) in user-defined profiles or in 
-clickable or timer home screen widgets.
-
-[Tasker autoremote](https://play.google.com/store/apps/details?id=com.joaomgcd.autoremote&hl=fr) is a plugin for Tasker that allow 
-the program to receive push message from the cloud as profile.
-
-This is how it works:
-- Send an AutoRemote message from Kalliope
-- Setup an AutoRemote profile in Tasker to react to the message
-- Do whatever you like with that message!
-
-The example usage is a "find my phone" task. 
-You could send a "Where are you?" message to your phone, and have Tasker respond with a repetitive "I'm here! I'm here!" 
-or play a music.
-
-
-## Options
-
-| parameter | required | default | choices | comment                                                       |
-|-----------|----------|---------|---------|---------------------------------------------------------------|
-| key       | yes      |         |         | API key. Can be found in your personal URL given by the app.  |
-| message   | yes      |         |         | Message to send to your phone                                 |
-
-## Return Values
-
-None
-
-## Synapses example
-
-Description of what the synapse will do
-```
-- name: "find-my-phone"
-  signals:
-    - order: "where is my phone"
-  neurons:
-    - say:
-        message: "I'll make your phone ringing, sir"
-    - tasker_autoremote:
-        key: "MY_VERY_LONG_KEY"
-        message: "lost"
-```
-
-
-## Notes
-
-### How to create a find my phone task
-This walk through will show you how to send a message to your phone so that even if it is set to silent, 
-will play any music file at full volume so you can find your phone if you have lost it in the couch.
-
-First, create a task, that you could call "start_ringing" that will perform:
-- Disable the silent mode
-- Set media volume to the maximum value
-- Play a local music
-
-![task play music](images/task_play_music.png)
-
-Then, create a new task with just one action:
-- Stop the music
-
-![task stop music](images/task_stop_music.png)
-
-Create the input profile. 
-- create a context of type Event > Plugin > Autoremote
-- Set the word you want
-- Attach the event to the task "start_ringing"
-
-![task stop music](images/profile_auto_remote.png)
-
-Finally, create a event, to stop the music when we unlock the phone
-- create a context of type Event > Display > Display Unlocked
-- Attach the event to the stop that stop the music
-
-![task stop music](images/profile_display_unlocked.png)
-
-Exit Tasker with the exit menu to be sure all events and task have been saved.
-
-
- 

+ 0 - 1
kalliope/neurons/tasker_autoremote/__init__.py

@@ -1 +0,0 @@
-from tasker_autoremote import Tasker_autoremote

BIN
kalliope/neurons/tasker_autoremote/images/profile_auto_remote.png


BIN
kalliope/neurons/tasker_autoremote/images/profile_display_unlocked.png


BIN
kalliope/neurons/tasker_autoremote/images/task_play_music.png


BIN
kalliope/neurons/tasker_autoremote/images/task_stop_music.png


+ 0 - 41
kalliope/neurons/tasker_autoremote/tasker_autoremote.py

@@ -1,41 +0,0 @@
-import logging
-
-import requests
-
-from kalliope.core.NeuronModule import NeuronModule, MissingParameterException
-
-logging.basicConfig()
-logger = logging.getLogger("kalliope")
-
-
-class Tasker_autoremote(NeuronModule):
-    def __init__(self, **kwargs):
-        super(Tasker_autoremote, self).__init__(**kwargs)
-
-        # check if parameters have been provided
-        self.key = kwargs.get('key', None)
-        self.message = kwargs.get('message', None)
-
-        # check parameters
-        if self._is_parameters_ok():
-            # create the payload
-            data = {'key': self.key,
-                    'message': self.message}
-            url = "https://autoremotejoaomgcd.appspot.com/sendmessage"
-            # post
-            r = requests.post(url, data=data)
-            logging.debug("Post to tasker automore response: %s" % r.status_code)
-
-    def _is_parameters_ok(self):
-        """
-        Check if received parameters are ok to perform operations in the neuron
-        :return: true if parameters are ok, raise an exception otherwise
-
-        .. raises:: MissingParameterException
-        """
-        if self.key is None:
-            raise MissingParameterException("key parameter required")
-        if self.message is None:
-            raise MissingParameterException("message parameter required")
-
-        return True

+ 0 - 0
kalliope/neurons/tasker_autoremote/tests/__init__.py


+ 0 - 36
kalliope/neurons/tasker_autoremote/tests/test_tasker_autoremote.py

@@ -1,36 +0,0 @@
-import unittest
-
-from kalliope.core.NeuronModule import MissingParameterException
-from kalliope.neurons.sleep.sleep import Sleep
-
-
-class TestSleep(unittest.TestCase):
-
-    def setUp(self):
-        self.key="key"
-        self.message="message"
-
-    def testParameters(self):
-        def run_test(parameters_to_test):
-            with self.assertRaises(MissingParameterException):
-                Sleep(**parameters_to_test)
-
-        # empty
-        parameters = dict()
-        run_test(parameters)
-
-        # missing key
-        parameters = {
-            "message": self.message
-        }
-        run_test(parameters)
-
-        # missing message
-        parameters = {
-            "key": self.key
-        }
-        run_test(parameters)
-
-
-if __name__ == '__main__':
-    unittest.main()

+ 0 - 51
kalliope/neurons/twitter/README.md

@@ -1,51 +0,0 @@
-# Twitter 
-
-## Synopsis
-
-This neuron allows you to send a tweet on your timeline.
-
-## Options
-
-| parameter           | required | default | choices | comment                     |
-|---------------------|----------|---------|---------|-----------------------------|
-| consumer_key        | yes      | None    |         | User info                   |
-| consumer_secret     | yes      | None    |         | User info                   |
-| access_token_key    | yes      | None    |         | User info                   |
-| access_token_secret | yes      | None    |         | User info                   |
-| tweet               | yes      | None    |         | The sentence to be tweeted  |
-
-## Return Values
-
-| Name  | Description                     | Type   | sample          |
-|-------|---------------------------------|--------|-----------------|
-| tweet | The tweet which has been posted | string | coucou kalliopé |
-
-## Synapses example
-
-```
-- name: "post-tweet"
-  neurons:
-    - twitter:
-        consumer_key: ""
-        consumer_secret: ""
-        access_token_key: ""
-        access_token_secret: ""
-        args:
-          - tweet
-  signals:
-    - order: "post on Twitter {{ tweet }}"
-```
-
-## Notes
-
-In order to be able to post on Twitter, you need to grant access of your application on Twitter by creating your own app associate to your profile. 
-
-### How to create my Twitter app
-
-1. Sign in your [Twitter account](https://www.twitter.com)
-2. Let's create your app [apps.twitter.com](https://apps.twitter.com)
-3. click on the button "Create New App"
-4. Fill in your application details
-5. Create your access token (to post a tweet, you need at least "Read and Write" access)
-6. Get your consumer_key, consumer_secret, access_token_key and access_token_secret from the tab "Key and access token" (Keep them secret !)
-7. Post your first message with this neuron !

+ 0 - 1
kalliope/neurons/twitter/__init__.py

@@ -1 +0,0 @@
-from twitter import Twitter

+ 0 - 0
kalliope/neurons/twitter/tests/__init__.py


+ 0 - 68
kalliope/neurons/twitter/tests/test_twitter_neuron.py

@@ -1,68 +0,0 @@
-import unittest
-
-from kalliope.core.NeuronModule import MissingParameterException
-from kalliope.neurons.twitter.twitter import Twitter
-
-
-class TestTwitter(unittest.TestCase):
-
-    def setUp(self):
-        self.consumer_key="kalliokey"
-        self.consumer_secret = "kalliosecret"
-        self.access_token_key = "kalliotokenkey"
-        self.access_token_secret = "kalliotokensecret"
-        self.tweet = "kalliotweet"
-
-    def testParameters(self):
-        def run_test(parameters_to_test):
-            with self.assertRaises(MissingParameterException):
-                Twitter(**parameters_to_test)
-
-        # empty
-        parameters = dict()
-        run_test(parameters)
-
-        # missing tweet
-        parameters = {
-            "consumer_key": self.consumer_key,
-            "consumer_secret": self.consumer_secret,
-            "access_token_key": self.access_token_key,
-            "access_token_secret": self.access_token_secret
-        }
-        run_test(parameters)
-
-        # missing consumer_key
-        parameters = {
-            "consumer_secret": self.consumer_secret,
-            "access_token_key": self.access_token_key,
-            "access_token_secret": self.access_token_secret,
-            "tweet": self.tweet
-        }
-        run_test(parameters)
-
-        # missing consumer_secret
-        parameters = {
-            "consumer_key": self.consumer_key,
-            "access_token_key": self.access_token_key,
-            "access_token_secret": self.access_token_secret,
-            "tweet": self.tweet
-        }
-        run_test(parameters)
-
-        # missing access_token_key
-        parameters = {
-            "consumer_key": self.consumer_key,
-            "consumer_secret": self.consumer_secret,
-            "access_token_secret": self.access_token_secret,
-            "tweet": self.tweet
-        }
-        run_test(parameters)
-
-        # missing access_token_secret
-        parameters = {
-            "consumer_key": self.consumer_key,
-            "consumer_secret": self.consumer_secret,
-            "access_token_key": self.access_token_key,
-            "tweet": self.tweet
-        }
-        run_test(parameters)

+ 0 - 53
kalliope/neurons/twitter/twitter.py

@@ -1,53 +0,0 @@
-import twitter
-
-from kalliope.core.NeuronModule import NeuronModule, MissingParameterException
-
-
-class Twitter(NeuronModule):
-    def __init__(self, **kwargs):
-
-        super(Twitter, self).__init__(**kwargs)
-
-        self.consumer_key = kwargs.get('consumer_key', None)
-        self.consumer_secret = kwargs.get('consumer_secret', None)
-        self.access_token_key = kwargs.get('access_token_key', None)
-        self.access_token_secret = kwargs.get('access_token_secret', None)
-        self.tweet = kwargs.get('tweet', None)
-
-        # check parameters
-        if self._is_parameters_ok():
-            api = twitter.Api(consumer_key=self.consumer_key,
-                              consumer_secret=self.consumer_secret,
-                              access_token_key=self.access_token_key,
-                              access_token_secret=self.access_token_secret)
-
-            status = api.PostUpdate(self.tweet)
-            message = {
-                "tweet" : status.text
-            }
-
-            self.say(message)
-
-    def _is_parameters_ok(self):
-        """
-        Check if received parameters are ok to perform operations in the neuron
-        :return: true if parameters are ok, raise an exception otherwise
-
-        .. raises:: MissingParameterException
-        """
-        if self.consumer_key is None:
-            raise MissingParameterException("Twitter needs a consumer_key")
-        if self.consumer_secret is None:
-            raise MissingParameterException("Twitter needs a consumer_secret")
-        if self.access_token_key is None:
-            raise MissingParameterException("Twitter needs an access_token_key")
-        if self.access_token_secret is None:
-            raise MissingParameterException("Twitter needs and access_token_secret")
-        if self.tweet is None:
-            raise MissingParameterException("You need to provide something to tweet !")
-
-        return True
-
-
-
-

+ 4 - 0
kalliope/neurons/uri/README.md

@@ -4,6 +4,10 @@
 
 
 Interacts with HTTP and HTTPS web services.
 Interacts with HTTP and HTTPS web services.
 
 
+## Installation
+
+CORE NEURON : No installation needed.  
+
 ## Options
 ## Options
 
 
 | parameter      | required | default | choices                                      | comment                                                                    |
 | parameter      | required | default | choices                                      | comment                                                                    |

+ 0 - 50
kalliope/neurons/wake_on_lan/README.md

@@ -1,50 +0,0 @@
-# wake_on_lan
-
-## Synopsis
-
-Allows a computer to be turned on or awakened from the [WOL](https://en.wikipedia.org/wiki/Wake-on-LAN) protocol by Kalliope.
-
-## Options
-
-| parameter         | required | default         | choices  | comment                                                                                                                                               |
-|-------------------|----------|-----------------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------|
-| mac_address       | yes      |                 |          | Mac address of the target PC to wake up. Accepted format: 'ff.ff.ff.ff.ff.ff', '00-00-00-00-00-00', 'FFFFFFFFFFFF'                                    |
-| broadcast_address | no       | 255.255.255.255 |          | Broadcast address where the magic packet will bee sent. By default on most LAN is 255.255.255.255                                                     |
-| port              | no       | 9               |          | The magic packet is typically sent as a UDP datagram to port 0,6 7 or 9. This parameter must be an integer. Do not add 'quotes' in your configuration |
-
-
-## Return Values
-
-None
-
-
-## Synapses example
-
-Kalliope will send a magic packet to the mac address `00-00-00-00-00-00`
-```
-- name: "wake-my-PC"
-  signals:
-    - order: "wake my PC"
-  neurons:
-    - wake_on_lan:
-        mac_address: "00-00-00-00-00-00"
-```
-
-If your broadcast address is not 255.255.255.255, or if your ethernet card does not listen on the standard 9 port, you can override default parameters.
-In the following example, we suppose that kalliope is on a local areal network 172.16.0.0/16. The broadcast address would be 172.16.255.255.
-```
-- name: "wake-my-PC"
-  signals:
-    - order: "wake my PC"
-  neurons:
-    - wake_on_lan:
-        mac_address: "00-00-00-00-00-00"
-        broadcast_address: "172.16.255.255"
-        port: 7
-```
-
-## Notes
-
-> **Note:** The target computer must be on the same local area network as Kalliope.
-
-> **Note:** The target computer must has wake on lan activated in BIOS settings and my be in [OS settings](http://www.groovypost.com/howto/enable-wake-on-lan-windows-10/) too.

+ 0 - 1
kalliope/neurons/wake_on_lan/__init__.py

@@ -1 +0,0 @@
-from wake_on_lan import Wake_on_lan

+ 0 - 0
kalliope/neurons/wake_on_lan/tests/__init__.py


+ 0 - 67
kalliope/neurons/wake_on_lan/tests/test_wake_on_lan.py

@@ -1,67 +0,0 @@
-import unittest
-import ipaddress
-
-from kalliope.core.NeuronModule import InvalidParameterException, MissingParameterException
-from kalliope.neurons.wake_on_lan.wake_on_lan import Wake_on_lan
-
-
-class TestWakeOnLan(unittest.TestCase):
-
-    def setUp(self):
-        self.mac_address="00:0a:95:9d:68:16"
-        self.broadcast_address = "255.255.255.255"
-        self.port = 42
-
-    def testParameters(self):
-        def run_test_invalidParam(parameters_to_test):
-            with self.assertRaises(InvalidParameterException):
-                Wake_on_lan(**parameters_to_test)
-
-        def run_test_missingParam(parameters_to_test):
-            with self.assertRaises(MissingParameterException):
-                Wake_on_lan(**parameters_to_test)
-
-        def run_test_valueError(parameters_to_test):
-            with self.assertRaises(ValueError):
-                Wake_on_lan(**parameters_to_test)
-
-        # empty
-        parameters = dict()
-        run_test_missingParam(parameters)
-
-        # missing mac_address
-        parameters = {
-            "broadcast_address": self.broadcast_address,
-            "port": self.port
-        }
-        run_test_missingParam(parameters)
-
-        # port is not an int
-        self.port = "port"
-        parameters = {
-            "broadcast_address": self.broadcast_address,
-            "mac_address": self.mac_address,
-            "port": self.port
-        }
-        run_test_invalidParam(parameters)
-        self.port = 42
-
-        # is broadcast not a valid format
-        self.broadcast_address = "broadcast"
-        parameters = {
-            "broadcast_address": self.broadcast_address,
-            "mac_address": self.mac_address,
-            "port": self.port
-        }
-        run_test_valueError(parameters)
-        self.broadcast_address = "255.255.255.255"
-
-        # is mac_address not a valid IPv4 or IPv6 format
-        self.mac_address = "mac_address"
-        parameters = {
-            "broadcast_address": self.broadcast_address,
-            "mac_address": self.mac_address,
-            "port": self.port
-        }
-        run_test_valueError(parameters)
-        self.mac_address = "00:0a:95:9d:68:16"

+ 0 - 47
kalliope/neurons/wake_on_lan/wake_on_lan.py

@@ -1,47 +0,0 @@
-import ipaddress
-import logging
-
-from kalliope.core.NeuronModule import NeuronModule, MissingParameterException, InvalidParameterException
-from wakeonlan import wol
-
-logging.basicConfig()
-logger = logging.getLogger("kalliope")
-
-
-class Wake_on_lan(NeuronModule):
-    def __init__(self, **kwargs):
-        super(Wake_on_lan, self).__init__(**kwargs)
-
-        self.mac_address = kwargs.get('mac_address', None)
-        self.broadcast_address = kwargs.get('broadcast_address', '255.255.255.255')
-        self.port = kwargs.get('port', 9)
-
-        # check parameters
-        if self._is_parameters_ok():
-            # convert to unicode for testing
-            broadcast_address_unicode = self.broadcast_address.decode('utf-8')
-            # check the ip address is a valid one
-            ipaddress.ip_address(broadcast_address_unicode)
-
-            logger.debug("Call Wake_on_lan_neuron with parameters: mac_address: %s, broadcast_address: %s, port: %s"
-                         % (self.mac_address, self.broadcast_address, self.port))
-
-            # send the magic packet, the mac address format will be check by the lib
-            wol.send_magic_packet(self.mac_address, ip_address=self.broadcast_address, port=self.port)
-
-    def _is_parameters_ok(self):
-        """
-            Check if received parameters are ok to perform operations in the neuron
-            :return: true if parameters are ok, raise an exception otherwise
-
-            .. raises:: InvalidParameterException, MissingParameterException
-        """
-        # check we provide a mac address
-        if self.mac_address is None:
-            raise MissingParameterException("mac_address parameter required")
-            # check the port
-        if type(self.port) is not int:
-            raise InvalidParameterException(
-                "port argument must be an integer. Remove quotes in your configuration.")
-
-        return True

+ 0 - 69
kalliope/neurons/wikipedia_searcher/README.md

@@ -1,69 +0,0 @@
-# wikipedia_searcher
-
-## Synopsis
-
-Get the summary of a Wikipedia page.
-
-## Options
-
-| parameter | required | default | choices                     | comment                                                                                                                           |
-|-----------|----------|---------|-----------------------------|-----------------------------------------------------------------------------------------------------------------------------------|
-| language  | yes      |         | E.g: "fr", "en", "it", "es" | See the list of available language in the "Note" section                                                                          |
-| query     | yes      |         |                             | The wikipedia page you are looking for.  This parameter can be passed as an argument in the neuron from the order with {{ query}} |
-| sentences | no       | 10      | Integer in range 1-10       | if set, return the first number of sentences(can be no greater than 10) specified in this parameter.                              |
-
-
-## Return Values
-
-| Name       | Description                             | Type   | sample                                                                                                                              |
-|------------|-----------------------------------------|--------|-------------------------------------------------------------------------------------------------------------------------------------|
-| summary    | Plain text summary of the searched page | string |  Wikipedia is a collaboratively edited, multilingual, free Internet encyclopedia supported by the non-profit Wikimedia Foundation.. |
-| returncode | Error code. See bellow                  | string | SummaryFound                                                                                                                        |
-| may_refer  | List of pages that can refer the query  | list   | ['Marc Le Bot', 'Bot', 'Jean-Marc Bot', 'bot', 'pied bot', 'robot', 'Sam Bot', 'Famille Both', 'Yves Bot', 'Ben Bot', 'Botswana']   |
-
-
-| returncode          | Description                             |
-|---------------------|-----------------------------------------|
-| SummaryFound        | A summary hs been found from the querry |
-| DisambiguationError | The query match more than ony one page. |
-| PageError           | No Wikipedia matched a query            |
-
-## Synapses example
-
-This synapse will look for the {{ query }} spelt by the user on Wikipedia
-```
-- name: "wikipedia-search"
-  signals:
-    - order: "look on wikipedia {{ query }}"
-  neurons:
-    - wikipedia_searcher:
-        language: "en"
-        args:
-          - query
-        file_template: "wikipedia_returned_value.j2"
-
-```
-
-## Templates example 
-
-This template will simply make Kalliope speak out loud the summary section of the Wikipédia page of the query.
-If the query match more than one page, Kaliope will give the user all matched pages.
-If the query doesn't match any page on Wikipedia, kalliope will notify the user.
-```
-{% if returncode == "DisambiguationError" %}
-    The query match following pages    
-    {% if may_refer is not none %}
-        {% for page in may_refer %}
-            {{ page }}
-        {% endfor %}
-    {% endif %}
-{% elif returncode == "PageError" %}
-    I haven't  found anything on this
-{% else %}
-    {{ summary }}
-{% endif %}
-```
-
-## Notes
-
-Available languages in [the detailed list of the offical Wikipedia page](https://en.wikipedia.org/wiki/List_of_Wikipedias#Detailed_list). The column is called "Wiki". E.g: "en"

+ 0 - 1
kalliope/neurons/wikipedia_searcher/__init__.py

@@ -1 +0,0 @@
-from wikipedia_searcher import Wikipedia_searcher

+ 0 - 0
kalliope/neurons/wikipedia_searcher/tests/__init__.py


+ 0 - 67
kalliope/neurons/wikipedia_searcher/tests/test_wikipedia_searcher.py

@@ -1,67 +0,0 @@
-import unittest
-
-from kalliope.core.NeuronModule import InvalidParameterException
-from kalliope.neurons.wikipedia_searcher import Wikipedia_searcher
-
-
-class TestWikipediaSearcher(unittest.TestCase):
-    def setUp(self):
-        pass
-
-    # def test_parameters(self):
-    #     def run_test(parameters_to_test):
-    #         with self.assertRaises(InvalidParameterException):
-    #             Wikipedia_searcher(**parameters_to_test)
-    #
-    #     parameters = dict()
-    #     run_test(parameters)
-    #
-    #     # sentences must be an integer
-    #     parameters = {
-    #         "language": "en",
-    #         "query": "this is the query",
-    #         "sentences": "invalid"
-    #
-    #     }
-    #     run_test(parameters)
-    #
-    #     # test non existing language
-    #     parameters = {
-    #         "language": "foo",
-    #         "query": "this is the query",
-    #         "sentences": 1
-    #
-    #     }
-    #     run_test(parameters)
-
-    # def test_get_DisambiguationError(self):
-    #
-    #     parameters = {
-    #         "language": "fr",
-    #         "query": "bot",
-    #         "sentences": 1
-    #     }
-    #
-    #     wiki = Wikipedia_searcher(**parameters)
-    #     self.assertEqual(wiki.returncode, "DisambiguationError")
-    #
-    # def test_page_error(self):
-    #     parameters = {
-    #         "language": "fr",
-    #         "query": "fudu foo bar non exist",
-    #         "sentences": 1
-    #     }
-    #
-    #     wiki = Wikipedia_searcher(**parameters)
-    #     self.assertEqual(wiki.returncode, "PageError")
-    #
-    # def test_summary_found(self):
-    #     parameters = {
-    #         "language": "fr",
-    #         "query": "kalliope"
-    #     }
-    #     wiki = Wikipedia_searcher(**parameters)
-    #     self.assertEqual(wiki.returncode, "SummaryFound")
-
-if __name__ == '__main__':
-    unittest.main()

+ 0 - 80
kalliope/neurons/wikipedia_searcher/wikipedia_searcher.py

@@ -1,80 +0,0 @@
-import logging
-
-from kalliope.core.NeuronModule import NeuronModule, InvalidParameterException
-import wikipedia
-
-logging.basicConfig()
-logger = logging.getLogger("kalliope")
-
-
-class Wikipedia_searcher(NeuronModule):
-    def __init__(self, **kwargs):
-        # we don't need the TTS cache for this neuron
-        cache = kwargs.get('cache', None)
-        if cache is None:
-            cache = False
-            kwargs["cache"] = cache
-        super(Wikipedia_searcher, self).__init__(**kwargs)
-
-        # get parameters form the neuron
-        self.query = kwargs.get('query', None)
-        self.language = kwargs.get('language', None)
-        self.sentences = kwargs.get('sentences', None)
-
-        self.may_refer = None
-        self.returncode = None
-        self.message = None
-
-        # check parameters
-        if self._is_parameters_ok():
-            # set the language
-            wikipedia.set_lang(self.language)
-            # do the summary search
-            try:
-                summary = wikipedia.summary(self.query, auto_suggest=True, sentences=self.sentences)
-                # if we are here, no exception raised, we got a summary
-                self.returncode = "SummaryFound"
-            except wikipedia.exceptions.DisambiguationError, e:
-                # Exception raised when a page resolves to a Disambiguation page.
-                # The options property contains a list of titles of Wikipedia pages that the query may refer to.
-                self.may_refer = e.options
-                # Removing duplicates in lists.
-                self.may_refer = list(set(self.may_refer))
-                self.returncode = "DisambiguationError"
-                summary = ""
-            except wikipedia.exceptions.PageError:
-                # Exception raised when no Wikipedia matched a query.
-                self.returncode = "PageError"
-                summary = ""
-
-            self.message = {
-                "summary": summary,
-                "may_refer": self.may_refer,
-                "returncode": self.returncode
-            }
-            logger.debug("Wikipedia returned message: %s" % str(self.message))
-
-            self.say(self.message)
-
-    def _is_parameters_ok(self):
-        """
-        Check if received parameters are ok to perform operations in the neuron
-        :return: true if parameters are ok, raise an exception otherwise
-
-        .. raises:: InvalidParameterException
-        """
-
-        if self.query is None:
-            raise InvalidParameterException("Wikipedia needs a query")
-        if self.language is None:
-            raise InvalidParameterException("Wikipedia needs a language")
-
-        valid_language = wikipedia.languages().keys()
-        if self.language not in valid_language:
-            raise InvalidParameterException("Wikipedia needs a valid language: %s" % valid_language)
-
-        if self.sentences is not None:
-            if not isinstance(self.sentences, int):
-                raise InvalidParameterException("Number of sentences must be an integer")
-
-        return True

+ 9 - 0
kalliope/settings.yml

@@ -113,3 +113,12 @@ rest_api:
 # ---------------------------
 # ---------------------------
 # Specify an optional default synapse response in case your order is not found.
 # Specify an optional default synapse response in case your order is not found.
 default_synapse: "Default-synapse"
 default_synapse: "Default-synapse"
+
+# ---------------------------
+# resource directory path
+# ---------------------------
+#resource_directory:
+#  neuron: "resources/neurons"
+#  stt: "resources/stt"
+#  tts: "resources/tts"
+#  trigger: "resources/trigger"

+ 3 - 9
setup.py

@@ -65,21 +65,15 @@ setup(
         'python2-pythondialog==3.4.0',
         'python2-pythondialog==3.4.0',
         'jinja2==2.8',
         'jinja2==2.8',
         'cffi==1.9.1',
         'cffi==1.9.1',
-        'pygmail==0.0.5.4',
-        'pushetta==1.0.15',
-        'wakeonlan==0.2.2',
         'ipaddress==1.0.17',
         'ipaddress==1.0.17',
-        'pyowm==2.5.0',
-        'python-twitter==3.1',
         'flask==0.11.1',
         'flask==0.11.1',
         'Flask-Restful==0.3.5',
         'Flask-Restful==0.3.5',
-        'wikipedia==1.4.0',
-        'requests==2.12.1',
+        'requests==2.12.4',
         'httpretty==0.8.14',
         'httpretty==0.8.14',
         'mock==2.0.0',
         'mock==2.0.0',
-        'feedparser==5.2.1',
         'Flask-Testing==0.6.1',
         'Flask-Testing==0.6.1',
-        'apscheduler==3.3.0'
+        'apscheduler==3.3.0',
+        'GitPython==2.1.1'
     ],
     ],