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

Merge pull request #396 from kalliope-project/signals

Signals
Nicolas Marcq 7 жил өмнө
parent
commit
86f89bcf5f

+ 1 - 1
Tests/test_brain_loader.py

@@ -47,7 +47,7 @@ class TestBrainLoader(unittest.TestCase):
         brain_loader = BrainLoader(file_path=self.brain_to_test)
         self.assertEqual(brain_loader.yaml_config, self.expected_result)
 
-    def test_get_brain(self):
+    def test_load_brain(self):
         """
         Test the class return a valid brain object
         """

+ 3 - 3
kalliope/core/ConfigurationManager/BrainLoader.py

@@ -42,7 +42,7 @@ class BrainLoader(with_metaclass(Singleton, object)):
         if self.file_path is None:
             raise BrainNotFound("brain file not found")
         self.yaml_config = self.get_yaml_config()
-        self.brain = self.get_brain()
+        self.brain = self.load_brain()
 
     def get_yaml_config(self):
         """
@@ -61,7 +61,7 @@ class BrainLoader(with_metaclass(Singleton, object)):
             brain_file_path = self.file_path
         return YAMLLoader.get_config(brain_file_path)
 
-    def get_brain(self):
+    def load_brain(self):
         """
         Class Methods which loads default or the provided YAML file and return a Brain
         :return: The loaded Brain
@@ -69,7 +69,7 @@ class BrainLoader(with_metaclass(Singleton, object)):
 
         :Example:
 
-            brain = BrainLoader.get_brain(file_path="/var/tmp/brain.yml")
+            brain = BrainLoader.load_brain(file_path="/var/tmp/brain.yml")
 
         .. seealso:: Brain
         .. warnings:: Class Method

+ 1 - 1
kalliope/core/NeuronModule.py

@@ -248,7 +248,7 @@ class NeuronModule(object):
         :param is_api_call: If true, the current call comes from the api
         :param overriding_parameter_dict: dict of value to add to neuron parameters
         """
-        synapse = BrainLoader().get_brain().get_synapse_by_name(synapse_name)
+        synapse = BrainLoader().brain.get_synapse_by_name(synapse_name)
         matched_synapse = MatchedSynapse(matched_synapse=synapse,
                                          matched_order=synapse_order,
                                          user_order=user_order,

+ 1 - 1
kalliope/core/RestAPI/FlaskAPI.py

@@ -158,7 +158,7 @@ class FlaskAPI(threading.Thread):
         """
         # get a synapse object from the name
         logger.debug("[FlaskAPI] run_synapse_by_name: synapse name -> %s" % synapse_name)
-        synapse_target = BrainLoader().get_brain().get_synapse_by_name(synapse_name=synapse_name)
+        synapse_target = BrainLoader().brain.get_synapse_by_name(synapse_name=synapse_name)
 
         # get no_voice_flag if present
         no_voice = self.get_boolean_flag_from_request(request, boolean_flag_to_find="no_voice")

+ 43 - 0
kalliope/core/SignalModule.py

@@ -0,0 +1,43 @@
+import logging
+from kalliope.core import Utils
+
+from kalliope.core.ConfigurationManager import BrainLoader
+
+logging.basicConfig()
+logger = logging.getLogger("kalliope")
+
+
+class MissingParameter(Exception):
+    """
+    An exception when parameters are missing from signals.
+
+    """
+    pass
+
+
+class SignalModule(object):
+
+    def __init__(self, **kwargs):
+        super(SignalModule, self).__init__(**kwargs)
+        # get the child who called the class
+        self.signal_name = self.__class__.__name__
+
+        Utils.print_info('Init Signal :' + self.signal_name)
+        self.brain = BrainLoader().brain
+
+    def get_list_synapse(self):
+        for synapse in self.brain.synapses:
+            for signal in synapse.signals:
+                # if the signal is a child we add it to the synapses list
+                if signal.name == self.signal_name.lower(): # Lowercase !
+                    if not self.check_parameters(parameters=signal.parameters):
+                        logger.debug(
+                            "[SignalModule] The signal " + self.signal_name + " is missing mandatory parameters, check documentation")
+                        raise MissingParameter()
+                    else:
+                        yield synapse
+                        break # if there is multiple signals in the synapse, we only add it once !
+
+    @staticmethod
+    def check_parameters(parameters):
+        raise NotImplementedError("[SignalModule] Must override check_parameters method !")

+ 1 - 0
kalliope/core/__init__.py

@@ -9,4 +9,5 @@ from kalliope.core.SynapseLauncher import SynapseLauncher
 from kalliope.core.LIFOBuffer import LIFOBuffer
 from kalliope.core.NeuronParameterLoader import NeuronParameterLoader
 from kalliope.core.NeuronModule import NeuronModule
+from kalliope.core.SignalModule import SignalModule, MissingParameter
 from kalliope.core.PlayerModule import PlayerModule

+ 27 - 35
kalliope/signals/event/event.py

@@ -1,5 +1,6 @@
 from threading import Thread
 
+from kalliope.core import SignalModule, MissingParameter
 from apscheduler.schedulers.background import BackgroundScheduler
 from apscheduler.triggers.cron import CronTrigger
 
@@ -8,22 +9,12 @@ from kalliope.core.SynapseLauncher import SynapseLauncher
 from kalliope.core import Utils
 
 
-class NoEventPeriod(Exception):
-    """
-    An Event must contains a period corresponding to its execution
-
-    .. seealso:: Event
-    """
-    pass
-
-
-class Event(Thread):
-    def __init__(self):
-        super(Event, self).__init__()
-        Utils.print_info('Starting event manager')
+class Event(SignalModule, Thread):
+    def __init__(self, **kwargs):
+        super(Event, self).__init__(**kwargs)
+        Utils.print_info('[Event] Starting manager')
         self.scheduler = BackgroundScheduler()
-        self.brain = BrainLoader().get_brain()
-        self.synapses = self.brain.synapses
+        self.list_synapses_with_event = list(super(Event, self).get_list_synapse())
         self.load_events()
 
     def run(self):
@@ -33,30 +24,30 @@ class Event(Thread):
         """
         For each received synapse that have an event as signal, we add a new job scheduled
         to launch the synapse
-        :return:
         """
-        for synapse in self.synapses:
+        for synapse in self.list_synapses_with_event:
             for signal in synapse.signals:
-                # if the signal is an event we add it to the task list
+                # We need to loop here again if the synapse has multiple event signals.
+                # if the signal is an event we add it to the task list.
                 if signal.name == "event":
-                    if self.check_event_dict(signal.parameters):
-                        my_cron = CronTrigger(year=self.get_parameter_from_dict("year", signal.parameters),
-                                              month=self.get_parameter_from_dict("month", signal.parameters),
-                                              day=self.get_parameter_from_dict("day", signal.parameters),
-                                              week=self.get_parameter_from_dict("week", signal.parameters),
-                                              day_of_week=self.get_parameter_from_dict("day_of_week", signal.parameters),
-                                              hour=self.get_parameter_from_dict("hour", signal.parameters),
-                                              minute=self.get_parameter_from_dict("minute", signal.parameters),
-                                              second=self.get_parameter_from_dict("second", signal.parameters),)
-                        Utils.print_info("Add synapse name \"%s\" to the scheduler: %s" % (synapse.name, my_cron))
-                        self.scheduler.add_job(self.run_synapse_by_name, my_cron, args=[synapse.name])
+                    my_cron = CronTrigger(year=self.get_parameter_from_dict("year", signal.parameters),
+                                          month=self.get_parameter_from_dict("month", signal.parameters),
+                                          day=self.get_parameter_from_dict("day", signal.parameters),
+                                          week=self.get_parameter_from_dict("week", signal.parameters),
+                                          day_of_week=self.get_parameter_from_dict("day_of_week",
+                                                                                   signal.parameters),
+                                          hour=self.get_parameter_from_dict("hour", signal.parameters),
+                                          minute=self.get_parameter_from_dict("minute", signal.parameters),
+                                          second=self.get_parameter_from_dict("second", signal.parameters), )
+                    Utils.print_info("Add synapse name \"%s\" to the scheduler: %s" % (synapse.name, my_cron))
+                    self.scheduler.add_job(self.run_synapse_by_name, my_cron, args=[synapse.name])
 
     @staticmethod
     def run_synapse_by_name(synapse_name):
         """
         This method will run the synapse
         """
-        Utils.print_info("Event triggered, running synapse: %s" % synapse_name)
+        Utils.print_info("[Event] triggered, running synapse: %s" % synapse_name)
         # get a brain
         brain_loader = BrainLoader()
         brain = brain_loader.brain
@@ -77,7 +68,7 @@ class Event(Thread):
             return None
 
     @staticmethod
-    def check_event_dict(event_dict):
+    def check_parameters(parameters):
         """
         Check received event dictionary of parameter is valid:
 
@@ -86,14 +77,15 @@ class Event(Thread):
         :return: True if event are ok
         :rtype: Boolean
         """
+
         def get_key(key_name):
             try:
-                return event_dict[key_name]
+                return parameters[key_name]
             except KeyError:
                 return None
 
-        if event_dict is None or event_dict == "":
-            raise NoEventPeriod("Event must contain at least one of those elements: "
+        if parameters is None or parameters == "":
+            raise MissingParameter("Event must contain at least one of those elements: "
                                 "year, month, day, week, day_of_week, hour, minute, second")
 
         # check content as at least on key
@@ -110,7 +102,7 @@ class Event(Thread):
         number_of_none_object = list_to_check.count(None)
         list_size = len(list_to_check)
         if number_of_none_object >= list_size:
-            raise NoEventPeriod("Event must contain at least one of those elements: "
+            raise MissingParameter("Event must contain at least one of those elements: "
                                 "year, month, day, week, day_of_week, hour, minute, second")
 
         return True

+ 1 - 1
kalliope/signals/geolocation/README.md

@@ -73,4 +73,4 @@ If the syntax is NOT ok, Kalliope will raise an error and log a message:
 
 ### Note
 
-/!\ this feature is supported by the Kalliope official smartphone application.
+/!\ this feature is supported by the [Kalliope official smartphone application.](https://github.com/kalliope-project/kalliope-app)

+ 8 - 39
kalliope/signals/geolocation/geolocation.py

@@ -1,59 +1,28 @@
 import logging
 from threading import Thread
 
-from kalliope.core import Utils
-from kalliope.core.ConfigurationManager import BrainLoader
+from kalliope.core import SignalModule
 
 logging.basicConfig()
 logger = logging.getLogger("kalliope")
 
 
-class MissingParameter(Exception):
-    """
-    A geolocation must contain latitude, longitude, radius.
-
-    .. seealso:: Geolocation
-    """
-    pass
-
-
-class Geolocation(Thread):
-
-    def __init__(self):
-        super(Geolocation, self).__init__()
-        Utils.print_info('Init Geolocation')
-        self.brain = BrainLoader().get_brain()
+class Geolocation(SignalModule, Thread):
+    def __init__(self, **kwargs):
+        super(Geolocation, self).__init__(**kwargs)
 
     def run(self):
         logger.debug("[Geolocalisation] Loading ...")
-        self.list_synapses_with_geolocalion = self._get_list_synapse_with_geolocation(brain=self.brain)
-
-    @classmethod
-    def _get_list_synapse_with_geolocation(cls, brain):
-        """
-        return the list of synapse that use geolocation as signal in the provided brain
-        :param brain: Brain object that contain all synapses loaded
-        :type brain: Brain
-        :return: generator of synapse that use geolocation as signal
-        """
-        for synapse in brain.synapses:
-            for signal in synapse.signals:
-                # if the signal is an event we add it to the task list
-                if signal.name == "geolocation":
-                    if not cls._check_geolocation(parameters=signal.parameters):
-                        logger.debug("[Geolocation] The signal is missing mandatory parameters, check documentation")
-                        raise MissingParameter()
-                    else:
-                        yield synapse
-
+        self.list_synapses_with_geolocalion = list(super(Geolocation, self).get_list_synapse())
 
     @staticmethod
-    def _check_geolocation(parameters):
+    def check_parameters(parameters):
         """
+        Overwritten method
         receive a dict of parameter from a geolocation signal and them
         :param parameters: dict of parameters
         :return: True if parameters are valid
         """
         # check mandatory parameters
         mandatory_parameters = ["latitude", "longitude", "radius"]
-        return all(key in parameters for key in mandatory_parameters)
+        return all(key in parameters for key in mandatory_parameters)

+ 20 - 11
kalliope/signals/geolocation/tests/test_geolocalisation.py

@@ -1,35 +1,35 @@
 import unittest
 
+from kalliope.core.SignalModule import MissingParameter
 
 from kalliope.core.Models import Brain
 from kalliope.core.Models import Neuron
 from kalliope.core.Models import Synapse
 from kalliope.core.Models.Signal import Signal
 
-from kalliope.signals.geolocation.geolocation import Geolocation, MissingParameter
+from kalliope.signals.geolocation.geolocation import Geolocation
 
 
 class Test_Geolocation(unittest.TestCase):
-
     def test_check_geolocation_valid(self):
         expected_parameters = ["latitude", "longitude", "radius"]
-        self.assertTrue(Geolocation._check_geolocation(expected_parameters))
+        self.assertTrue(Geolocation.check_parameters(expected_parameters))
 
     def test_check_geolocation_valid_with_other(self):
         expected_parameters = ["latitude", "longitude", "radius", "kalliope", "random"]
-        self.assertTrue(Geolocation._check_geolocation(expected_parameters))
+        self.assertTrue(Geolocation.check_parameters(expected_parameters))
 
     def test_check_geolocation_no_radius(self):
         expected_parameters = ["latitude", "longitude", "kalliope", "random"]
-        self.assertFalse(Geolocation._check_geolocation(expected_parameters))
+        self.assertFalse(Geolocation.check_parameters(expected_parameters))
 
     def test_check_geolocation_no_latitude(self):
         expected_parameters = ["longitude", "radius", "kalliope", "random"]
-        self.assertFalse(Geolocation._check_geolocation(expected_parameters))
+        self.assertFalse(Geolocation.check_parameters(expected_parameters))
 
     def test_check_geolocation_no_longitude(self):
         expected_parameters = ["latitude", "radius", "kalliope", "random"]
-        self.assertFalse(Geolocation._check_geolocation(expected_parameters))
+        self.assertFalse(Geolocation.check_parameters(expected_parameters))
 
     def test_get_list_synapse_with_geolocation(self):
         # Init
@@ -53,7 +53,13 @@ class Test_Geolocation(unittest.TestCase):
         br = Brain(synapses=synapses_list)
 
         expected_list = [synapse1]
-        self.assertEqual(expected_list, list(Geolocation._get_list_synapse_with_geolocation(brain=br)))
+
+        # Stubbing the Geolocation Signal with the brain
+        geo = Geolocation()
+        geo.brain = br
+        geo.run()
+
+        self.assertEqual(expected_list, geo.list_synapses_with_geolocalion)
 
     def test_get_list_synapse_with_raise_missing_parameters(self):
         # Init
@@ -75,10 +81,13 @@ class Test_Geolocation(unittest.TestCase):
         synapses_list = [synapse1, synapse2]
         br = Brain(synapses=synapses_list)
 
+        # Stubbing the Geolocation Signal with the brain
+        geo = Geolocation()
+        geo.brain = br
+
         with self.assertRaises(MissingParameter):
-            # /!\ Note: impossible to call a generator method directly ! need to use a list !
-            list(Geolocation._get_list_synapse_with_geolocation(brain=br))
+            geo.run()
 
 
 if __name__ == '__main__':
-    unittest.main()
+    unittest.main()

+ 15 - 30
kalliope/signals/mqtt_subscriber/mqtt_subscriber.py

@@ -1,65 +1,50 @@
 import logging
 from threading import Thread
 
+from kalliope.core import SignalModule, MissingParameter
+
 from kalliope.core.ConfigurationManager import BrainLoader
 from kalliope.signals.mqtt_subscriber.MqttClient import MqttClient
 from kalliope.signals.mqtt_subscriber.models import Broker, Topic
 
+from kalliope.core import Utils
+
 CLIENT_ID = "kalliope"
 
 logging.basicConfig()
 logger = logging.getLogger("kalliope")
 
 
-class Mqtt_subscriber(Thread):
+class Mqtt_subscriber(SignalModule, Thread):
 
-    def __init__(self, brain=None):
-        super(Mqtt_subscriber, self).__init__()
-        logger.debug("[Mqtt_subscriber] Mqtt_subscriber class created")
-        # variables
+    def __init__(self, **kwargs):
+        super(Mqtt_subscriber, self).__init__(**kwargs)
+        Utils.print_info('[Mqtt_subscriber] Starting manager')# variables
+        self.list_synapses_with_mqtt = list(super(Mqtt_subscriber, self).get_list_synapse())
         self.broker_ip = None
         self.topic = None
         self.json_message = False
 
-        self.brain = brain
-        if self.brain is None:
-            self.brain = BrainLoader().get_brain()
-
     def run(self):
         logger.debug("[Mqtt_subscriber] Starting Mqtt_subscriber")
-        # get the list of synapse that use Mqtt_subscriber as signal
-        list_synapse_with_mqtt_subscriber = self.get_list_synapse_with_mqtt_subscriber(brain=self.brain)
 
         # we need to sort broker URL by ip, then for each broker, we sort by topic and attach synapses name to run to it
-        list_broker_to_instantiate = self.get_list_broker_to_instantiate(list_synapse_with_mqtt_subscriber)
+        list_broker_to_instantiate = self.get_list_broker_to_instantiate(self.list_synapses_with_mqtt)
 
         # now instantiate a MQTT client for each broker object
         self.instantiate_mqtt_client(list_broker_to_instantiate)
 
-    def get_list_synapse_with_mqtt_subscriber(self, brain):
-        """
-        return the list of synapse that use Mqtt_subscriber as signal in the provided brain
-        :param brain: Brain object that contain all synapses loaded
-        :type brain: Brain
-        :return: list of synapse that use Mqtt_subscriber as signal
-        """
-        for synapse in brain.synapses:
-            for signal in synapse.signals:
-                # if the signal is an event we add it to the task list
-                if signal.name == "mqtt_subscriber":
-                    if self.check_mqtt_dict(signal.parameters):
-                        yield synapse
-
     @staticmethod
-    def check_mqtt_dict(mqtt_signal_parameters):
+    def check_parameters(parameters):
         """
-        receive a dict of parameter from a mqtt_subscriber signal and them
-        :param mqtt_signal_parameters: dict of parameters
+        overwrite method
+        receive a dict of parameter from a mqtt_subscriber signal
+        :param parameters: dict of mqtt_signal_parameters
         :return: True if parameters are valid
         """
         # check mandatory parameters
         mandatory_parameters = ["broker_ip", "topic"]
-        if not all(key in mqtt_signal_parameters for key in mandatory_parameters):
+        if not all(key in parameters for key in mandatory_parameters):
             return False
 
         return True

+ 16 - 10
kalliope/signals/mqtt_subscriber/test_mqtt_subscriber.py

@@ -18,8 +18,8 @@ class TestMqtt_subscriber(unittest.TestCase):
             "topic": "my_topic"
         }
 
-        self.assertTrue(Mqtt_subscriber.check_mqtt_dict(valid_dict_of_parameters))
-        self.assertFalse(Mqtt_subscriber.check_mqtt_dict(invalid_dict_of_parameters))
+        self.assertTrue(Mqtt_subscriber.check_parameters(valid_dict_of_parameters))
+        self.assertFalse(Mqtt_subscriber.check_parameters(invalid_dict_of_parameters))
 
     def test_get_list_synapse_with_mqtt_subscriber(self):
 
@@ -33,9 +33,10 @@ class TestMqtt_subscriber(unittest.TestCase):
 
         expected_result = synapses
 
-        mq = Mqtt_subscriber(brain=brain)
+        mq = Mqtt_subscriber()
+        mq.brain = brain
 
-        generator = mq.get_list_synapse_with_mqtt_subscriber(brain)
+        generator = mq.get_list_synapse()
 
         self.assertEqual(expected_result, list(generator))
 
@@ -52,8 +53,9 @@ class TestMqtt_subscriber(unittest.TestCase):
 
         expected_result = [synapse2]
 
-        mq = Mqtt_subscriber(brain=brain)
-        generator = mq.get_list_synapse_with_mqtt_subscriber(brain)
+        mq = Mqtt_subscriber()
+        mq.brain = brain
+        generator = mq.get_list_synapse()
 
         self.assertEqual(expected_result, list(generator))
 
@@ -81,7 +83,8 @@ class TestMqtt_subscriber(unittest.TestCase):
 
         expected_retuned_list = [expected_broker]
 
-        mq = Mqtt_subscriber(brain=brain)
+        mq = Mqtt_subscriber()
+        mq.brain = brain
 
         self.assertListEqual(expected_retuned_list,
                              mq.get_list_broker_to_instantiate(list_synapse_with_mqtt_subscriber))
@@ -124,7 +127,8 @@ class TestMqtt_subscriber(unittest.TestCase):
 
         expected_retuned_list = [expected_broker1, expected_broker2]
 
-        mq = Mqtt_subscriber(brain=brain)
+        mq = Mqtt_subscriber()
+        mq.brain = brain
 
         self.assertEqual(expected_retuned_list, mq.get_list_broker_to_instantiate(list_synapse_with_mqtt_subscriber))
 
@@ -162,7 +166,8 @@ class TestMqtt_subscriber(unittest.TestCase):
 
         expected_retuned_list = [expected_broker1]
 
-        mq = Mqtt_subscriber(brain=brain)
+        mq = Mqtt_subscriber()
+        mq.brain = brain
 
         self.assertEqual(expected_retuned_list, mq.get_list_broker_to_instantiate(list_synapse_with_mqtt_subscriber))
 
@@ -196,7 +201,8 @@ class TestMqtt_subscriber(unittest.TestCase):
 
         expected_retuned_list = [expected_broker1]
 
-        mq = Mqtt_subscriber(brain=brain)
+        mq = Mqtt_subscriber()
+        mq.brain = brain
 
         self.assertEqual(expected_retuned_list, mq.get_list_broker_to_instantiate(list_synapse_with_mqtt_subscriber))
 

+ 1 - 1
kalliope/signals/order/order.py

@@ -40,7 +40,7 @@ class Order(Thread):
         # load settings and brain from singleton
         sl = SettingLoader()
         self.settings = sl.settings
-        self.brain = BrainLoader().get_brain()
+        self.brain = BrainLoader().brain
 
         # keep in memory the order to process
         self.order_to_process = None