浏览代码

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

Resource directory
Nicolas Marcq 8 年之前
父节点
当前提交
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. 二进制
      kalliope/neurons/tasker_autoremote/images/profile_auto_remote.png
  61. 二进制
      kalliope/neurons/tasker_autoremote/images/profile_display_unlocked.png
  62. 二进制
      kalliope/neurons/tasker_autoremote/images/task_play_music.png
  63. 二进制
      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.
 
-| 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.
 
+## Installation
+```
+kalliope install --git-url "https://github.com/my_user/my_neuron.git"
+```
+
 ## Options
 
 (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.
 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
 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
@@ -22,6 +38,8 @@ neurons:
 ```
 > **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.
 Full list of [available neuron here](neuron_list.md)
 

+ 19 - 0
Docs/settings.md

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

+ 32 - 15
Tests/test_order_analyser.py

@@ -364,11 +364,13 @@ class TestOrderAnalyser(unittest.TestCase):
                             synapse2,
                             synapse3]
 
-        expected_result = [synapse1]
+
 
         # 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,
                           "Fail matching 'the expected synapse' from the complete synapse list and the order")
 
@@ -384,10 +386,14 @@ class TestOrderAnalyser(unittest.TestCase):
 
         expected_result = [synapse1,
                            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
         order_to_match = "this is not the correct word"
@@ -413,10 +419,15 @@ class TestOrderAnalyser(unittest.TestCase):
         expected_result = [synapse1,
                            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):
 
@@ -537,11 +548,16 @@ class TestOrderAnalyser(unittest.TestCase):
         st = Settings()
         # Find synapse
         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,
                           "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
         order = "No default synapse"
         expected_result = []
@@ -552,8 +568,9 @@ class TestOrderAnalyser(unittest.TestCase):
         # Default synapse
         st = Settings(default_synapse="Synapse2")
         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,
                           "Fail to run the default synapse")
 

+ 41 - 2
Tests/test_settings_loader.py

@@ -1,9 +1,12 @@
 import os
+import inspect
 import platform
+import shutil
 import unittest
 
 from kalliope.core.ConfigurationManager import SettingLoader
 from kalliope.core.Models import Singleton
+from kalliope.core.Models import Resources
 from kalliope.core.Models.RestAPI import RestAPI
 from kalliope.core.Models.Settings import Settings
 from kalliope.core.Models.Stt import Stt
@@ -14,8 +17,12 @@ from kalliope.core.Models.Tts import Tts
 class TestSettingLoader(unittest.TestCase):
 
     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 = {
             'rest_api':
@@ -36,10 +43,25 @@ class TestSettingLoader(unittest.TestCase):
                 {'pico2wave': {'cache': True, 'language': 'fr-FR'}},
                 {'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):
+        # Cleanup
+        shutil.rmtree('/tmp/kalliope/tests/kalliope_resources_dir')
+
         Singleton._instances = {}
 
     def test_singleton(self):
@@ -72,6 +94,12 @@ class TestSettingLoader(unittest.TestCase):
                                            login="admin", password="secret", port=5000)
         settings_object.cache_path = '/tmp/kalliope_tts_cache'
         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()
 
         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)
         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__':
     unittest.main()

+ 12 - 3
Tests/test_utils.py

@@ -1,10 +1,16 @@
 import unittest
 import os
+import mock
 
 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.core.Utils.Utils import Utils
 
+from kalliope.core.ConfigurationManager import SettingLoader
+from kalliope.core.ConfigurationManager import BrainLoader
+
 
 class TestUtils(unittest.TestCase):
     """
@@ -121,11 +127,14 @@ class TestUtils(unittest.TestCase):
         """
         Test that an instance as been instantiate properly.
         """
+        sl = SettingLoader()
+        sl.settings.resource_dir = '/var/tmp/test/resources'
 
         neuron = Neuron(name='Say', parameters={'message': 'test dynamic class instantiate'})
-        self.assertTrue(isinstance(Utils.get_dynamic_class_instantiation("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),
                         "Fail instantiate a class")
 

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

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

+ 2 - 8
install/files/python_requirements.txt

@@ -6,18 +6,12 @@ ansible==2.2.0.0
 python2-pythondialog==3.4.0
 jinja2==2.8
 cffi==1.9.1
-pygmail==0.0.5.4
-pushetta==1.0.15
-wakeonlan==0.2.2
 ipaddress==1.0.17
-pyowm==2.5.0
-python-twitter==3.1
 flask==0.11.1
 Flask-Restful==0.3.5
-wikipedia==1.4.0
-requests==2.12.1
+requests==2.12.4
 httpretty==0.8.14
 mock==2.0.0
-feedparser==5.2.1
 Flask-Testing==0.6.1
 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 sys
 
+from kalliope.core.ResourcesManager import ResourcesManager
 from kalliope.core.SynapseLauncher import SynapseLauncher
 
 logging.basicConfig()
@@ -28,7 +29,7 @@ def signal_handler(signal, frame):
     sys.exit(0)
 
 # actions available
-ACTION_LIST = ["start", "gui"]
+ACTION_LIST = ["start", "gui", "install"]
 
 
 def main():
@@ -37,10 +38,11 @@ def main():
     """
     # create arguments
     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("--brain-file", help="Full path of a brain file")
     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
     args = parser.parse_args()
@@ -89,6 +91,15 @@ def main():
     if args.action == "gui":
         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):
     """

+ 1 - 1
kalliope/_version.py

@@ -1,2 +1,2 @@
 # 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 os
+import imp
 
 from kalliope.core.Utils.Utils import ModuleNotFoundError
-
+from kalliope.core.ConfigurationManager.SettingLoader import SettingLoader
 
 class InvalidSynapeName(Exception):
     """
@@ -139,13 +141,26 @@ class ConfigurationChecker:
             :type neuron_module_name: str
             :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:
-                getattr(mod, neuron_module_name)
+                mod = __import__(package_name, fromlist=[neuron_module_name.capitalize()])
+                getattr(mod, neuron_module_name.capitalize())
             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
 
         if isinstance(neuron_dict, dict):

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

@@ -1,7 +1,9 @@
 import logging
+import os
 
 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.RestAPI import RestAPI
 from kalliope.core.Models.Settings import Settings
@@ -106,6 +108,7 @@ class SettingLoader(object):
         rest_api = self._get_rest_api(settings)
         cache_path = self._get_cache_path(settings)
         default_synapse = self._get_default_synapse(settings)
+        resources = self._get_resources(settings)
 
         # Load the setting singleton with the parameters
         setting_object.default_tts_name = default_tts_name
@@ -119,6 +122,7 @@ class SettingLoader(object):
         setting_object.rest_api = rest_api
         setting_object.cache_path = cache_path
         setting_object.default_synapse = default_synapse
+        setting_object.resources = resources
 
         return setting_object
 
@@ -507,4 +511,60 @@ class SettingLoader(object):
 
         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,
                  cache_path=None,
                  default_synapse=None,
+                 resources=None,
                  machine=None):
 
         self.default_tts_name = default_tts_name
@@ -32,6 +33,7 @@ class Settings(object):
         self.rest_api = rest_api
         self.cache_path = cache_path
         self.default_synapse = default_synapse
+        self.resources = resources
         self.machine = platform.machine()   # can be x86_64 or armv7l
 
     def __eq__(self, other):

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

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

+ 10 - 3
kalliope/core/NeuronLauncher.py

@@ -1,6 +1,7 @@
 import logging
 
 from kalliope.core.Utils.Utils import Utils
+from kalliope.core.ConfigurationManager.SettingLoader import SettingLoader
 
 logging.basicConfig()
 logger = logging.getLogger("kalliope")
@@ -20,6 +21,12 @@ class NeuronLauncher:
         :return:
         """
         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)
 
             # 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
             tts_module_instance.say(tts_message)
 

+ 42 - 21
kalliope/core/OrderAnalyser.py

@@ -1,5 +1,6 @@
 # coding: utf8
 import re
+import collections
 from collections import Counter
 
 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.
     """
+
     def __init__(self, order, brain=None):
         """
         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):
         """
-        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 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
             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
-        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
-            self._start_list_neurons(list_neurons=synapse.neurons,
+            self._start_list_neurons(list_neurons=tuple.synapse.neurons,
                                      params=params)
+            synapses_launched.append(tuple.synapse)
 
         # return the list of launched synapse
-        return synapses_to_run
+        return synapses_launched
 
     @classmethod
     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 settings: the settings
         :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)
@@ -83,7 +96,10 @@ class OrderAnalyser:
                 if default_synapse is not None:
                     logger.debug("Default synapse found %s" % default_synapse)
                     Utils.print_info("Default synapse found: %s, running it" % default_synapse.name)
-                    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
 
@@ -92,17 +108,22 @@ class OrderAnalyser:
         """
         Class method to return all the matching synapses with the order from the complete of synapses.
 
+        Note: we use named tuples:
+        tuple_synapse_matchingOrder = collections.namedtuple('tuple_synapse_matchingOrder',['synapse', 'order'])
+
         :param all_synapses_list: the complete list of all synapses
         :param order_to_match: the order to match
         :type order_to_match: str
-        :return: the list of matching synapses
+        :return: the list of matching synapses (named tuples)
         """
+        tuple_synapse_order = collections.namedtuple('tuple_synapse_matchingOrder', ['synapse', 'order'])
         matching_synapses_list = list()
         for synapse in all_synapses_list:
             for signal in synapse.signals:
                 if type(signal) == Order:
                     if cls.spelt_order_match_brain_order_via_table(signal.sentence, order_to_match):
-                        matching_synapses_list.append(synapse)
+                        matching_synapses_list.append(tuple_synapse_order(synapse=synapse,
+                                                                                  order=signal.sentence))
                         logger.debug("Order found! Run neurons: %s" % synapse.neurons)
                         Utils.print_success("Order matched in the brain. Running synapse \"%s\"" % synapse.name)
         return matching_synapses_list

+ 2 - 2
kalliope/core/OrderListener.py

@@ -55,8 +55,8 @@ class OrderListener(Thread):
         for stt_object in self.settings.stts:
             if stt_object.name == self.stt_module_name:
                 stt_object.parameters["callback"] = self.callback
-                Utils.get_dynamic_class_instantiation('stt',
-                                                      stt_object.name.capitalize(),
+                Utils.get_dynamic_class_instantiation(package_name='stt',
+                                                      module_name=stt_object.name.capitalize(),
                                                       parameters=stt_object.parameters)
 
     @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
         """
         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
         trigger.parameters["callback"] = callback
         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 os
 import inspect
+import imp
 
 logging.basicConfig()
 logger = logging.getLogger("kalliope")
@@ -84,8 +85,8 @@ class Utils(object):
     # 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
 
@@ -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 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 resources_dir: the resource directory to check for external resources
         :return:
         """
         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:
-            klass = getattr(mod, module_name)
+            klass = getattr(mod, module_name.capitalize())
         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:
             # 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).
 
+## Installation
+
+CORE NEURON : No installation needed.  
 
 ## 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
 
+### Playbook without admin privileges
+
 Call the playbook named playbook.yml
 ```
   - name: "Ansible-test"
@@ -28,7 +36,7 @@ Call the playbook named playbook.yml
       - ansible_playbook: 
           task_file: "playbook.yml"
       - 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.
@@ -54,23 +62,42 @@ Content of the playbook. This playbook will use the [URI module](http://docs.ans
             {"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
 
-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
 - [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
 
 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
+
+import logging
 from ansible.parsing.dataloader import DataLoader
 from ansible.vars import VariableManager
 from ansible.inventory import Inventory
@@ -6,27 +8,28 @@ from ansible.executor.playbook_executor import PlaybookExecutor
 
 from kalliope.core.NeuronModule import NeuronModule, MissingParameterException
 
+logging.basicConfig()
+logger = logging.getLogger("kalliope")
+
 
 class Ansible_playbook(NeuronModule):
     def __init__(self, **kwargs):
         super(Ansible_playbook, self).__init__(**kwargs)
 
         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
         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()
             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)
             playbooks = [self.task_file]
 
@@ -43,4 +46,33 @@ class Ansible_playbook(NeuronModule):
     def _is_parameters_ok(self):
         if self.task_file is None:
             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
+
+    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.
 
+## Installation
+
+CORE NEURON : No installation needed.  
+
 ## Options
 
 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.
 
+## Installation
+
+CORE NEURON : No installation needed.  
+
 ## Options
 
 | parameter        | required | default | choices | comment                                                                                           |

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

@@ -1,6 +1,6 @@
 import logging
 
-from kalliope.core.NeuronModule import NeuronModule, InvalidParameterException
+from kalliope.core.NeuronModule import NeuronModule, MissingParameterException, InvalidParameterException
 
 logging.basicConfig()
 logger = logging.getLogger("kalliope")
@@ -40,7 +40,9 @@ class Neurotransmitter(NeuronModule):
             for el in self.from_answer_link:
                 for answer in el["answers"]:
                     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
                 self.run_synapse_by_name(self.default)
 
@@ -49,7 +51,7 @@ class Neurotransmitter(NeuronModule):
         Check if received links are ok to perform operations
         :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,
         #  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")
 
         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.default is None:
                 raise InvalidParameterException("default parameter is required and must contain a valid synapse name")
             for el in self.from_answer_link:
                 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:
-                    raise InvalidParameterException("Links must contain answers: %s" % el)
+                    raise MissingParameterException("Links must contain answers: %s" % el)
 
         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.
 
+## Installation
+
+CORE NEURON : No installation needed.  
+
 ## Options
 
 | 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.
 
+## Installation
+
+CORE NEURON : No installation needed.  
+
 ## Options
 
 | 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.
 
+## Installation
+
+CORE NEURON : No installation needed.  
 
 ## Options
 

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

@@ -4,6 +4,10 @@
 
 This neuron sleeps the system for a given time in seconds.
 
+## Installation
+
+CORE NEURON : No installation needed.  
+
 ## Options
 
 | 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.
 
+## Installation
+
+CORE NEURON : No installation needed.  
+
 ## Options
 
 | 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

二进制
kalliope/neurons/tasker_autoremote/images/profile_auto_remote.png


二进制
kalliope/neurons/tasker_autoremote/images/profile_display_unlocked.png


二进制
kalliope/neurons/tasker_autoremote/images/task_play_music.png


二进制
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.
 
+## Installation
+
+CORE NEURON : No installation needed.  
+
 ## Options
 
 | 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.
 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',
         'jinja2==2.8',
         'cffi==1.9.1',
-        'pygmail==0.0.5.4',
-        'pushetta==1.0.15',
-        'wakeonlan==0.2.2',
         'ipaddress==1.0.17',
-        'pyowm==2.5.0',
-        'python-twitter==3.1',
         'flask==0.11.1',
         'Flask-Restful==0.3.5',
-        'wikipedia==1.4.0',
-        'requests==2.12.1',
+        'requests==2.12.4',
         'httpretty==0.8.14',
         'mock==2.0.0',
-        'feedparser==5.2.1',
         'Flask-Testing==0.6.1',
-        'apscheduler==3.3.0'
+        'apscheduler==3.3.0',
+        'GitPython==2.1.1'
     ],