From 74f931237cfc3c36daac03f1fc61519d32794355 Mon Sep 17 00:00:00 2001
From: Stefano Borini <sborini@enthought.com>
Date: Tue, 1 Aug 2017 15:42:15 +0100
Subject: [PATCH] Introduced slots

---
 force_bdss/core/slot.py                       | 11 +++++++
 force_bdss/core/tests/test_slot.py            | 10 +++++++
 .../csv_extractor_data_source.py              |  9 ++++++
 .../dummy_data_source/dummy_data_source.py    |  3 ++
 .../dummy_kpi_calculator.py                   |  3 ++
 .../dummy/kpi_adder/kpi_adder_calculator.py   | 11 +++++++
 force_bdss/data_sources/base_data_source.py   | 14 ++++++---
 .../tests/test_base_data_source.py            |  3 ++
 force_bdss/kpi/base_kpi_calculator.py         | 29 +++++++++++++++++++
 .../kpi/tests/test_base_kpi_calculator.py     |  3 ++
 .../tests/test_core_evaluation_driver.py      |  6 ++++
 11 files changed, 98 insertions(+), 4 deletions(-)
 create mode 100644 force_bdss/core/slot.py
 create mode 100644 force_bdss/core/tests/test_slot.py

diff --git a/force_bdss/core/slot.py b/force_bdss/core/slot.py
new file mode 100644
index 0000000..2218275
--- /dev/null
+++ b/force_bdss/core/slot.py
@@ -0,0 +1,11 @@
+from traits.api import HasStrictTraits, String
+
+
+class Slot(HasStrictTraits):
+    """Describes an input or output slot in the DataSource or
+    KPICalculator"""
+    #: A textual description of the slot
+    description = String("No description")
+
+    #: The CUBA key of the slot
+    type = String()
diff --git a/force_bdss/core/tests/test_slot.py b/force_bdss/core/tests/test_slot.py
new file mode 100644
index 0000000..b6c687e
--- /dev/null
+++ b/force_bdss/core/tests/test_slot.py
@@ -0,0 +1,10 @@
+import unittest
+
+from force_bdss.core.slot import Slot
+
+
+class TestSlot(unittest.TestCase):
+    def test_initialization(self):
+        slot = Slot()
+        self.assertEqual(slot.type, "")
+        self.assertEqual(slot.description, "No description")
diff --git a/force_bdss/core_plugins/dummy/csv_extractor/csv_extractor_data_source.py b/force_bdss/core_plugins/dummy/csv_extractor/csv_extractor_data_source.py
index 471f3ac..36f7297 100644
--- a/force_bdss/core_plugins/dummy/csv_extractor/csv_extractor_data_source.py
+++ b/force_bdss/core_plugins/dummy/csv_extractor/csv_extractor_data_source.py
@@ -1,5 +1,6 @@
 import csv
 from force_bdss.api import BaseDataSource, DataValue
+from force_bdss.core.slot import Slot
 
 
 class CSVExtractorDataSource(BaseDataSource):
@@ -20,3 +21,11 @@ class CSVExtractorDataSource(BaseDataSource):
                     break
 
             raise IndexError("Could not find specified data.")
+
+    def slots(self, model):
+        return (
+            (),
+            (
+                Slot(type=model.cuba_type),
+            )
+        )
diff --git a/force_bdss/core_plugins/dummy/dummy_data_source/dummy_data_source.py b/force_bdss/core_plugins/dummy/dummy_data_source/dummy_data_source.py
index 03c36de..ac4dc61 100644
--- a/force_bdss/core_plugins/dummy/dummy_data_source/dummy_data_source.py
+++ b/force_bdss/core_plugins/dummy/dummy_data_source/dummy_data_source.py
@@ -4,3 +4,6 @@ from force_bdss.api import BaseDataSource
 class DummyDataSource(BaseDataSource):
     def run(self, model, parameters):
         return parameters
+
+    def slots(self, model):
+        return (), ()
diff --git a/force_bdss/core_plugins/dummy/dummy_kpi_calculator/dummy_kpi_calculator.py b/force_bdss/core_plugins/dummy/dummy_kpi_calculator/dummy_kpi_calculator.py
index bd10473..3e035cb 100644
--- a/force_bdss/core_plugins/dummy/dummy_kpi_calculator/dummy_kpi_calculator.py
+++ b/force_bdss/core_plugins/dummy/dummy_kpi_calculator/dummy_kpi_calculator.py
@@ -4,3 +4,6 @@ from force_bdss.api import BaseKPICalculator
 class DummyKPICalculator(BaseKPICalculator):
     def run(self, model, data_source_results):
         return data_source_results
+
+    def slots(self, model):
+        return (), ()
diff --git a/force_bdss/core_plugins/dummy/kpi_adder/kpi_adder_calculator.py b/force_bdss/core_plugins/dummy/kpi_adder/kpi_adder_calculator.py
index 94128b3..ff4b7ce 100644
--- a/force_bdss/core_plugins/dummy/kpi_adder/kpi_adder_calculator.py
+++ b/force_bdss/core_plugins/dummy/kpi_adder/kpi_adder_calculator.py
@@ -1,4 +1,5 @@
 from force_bdss.api import BaseKPICalculator, DataValue
+from force_bdss.core.slot import Slot
 
 
 class KPIAdderCalculator(BaseKPICalculator):
@@ -16,3 +17,13 @@ class KPIAdderCalculator(BaseKPICalculator):
                 type=model.cuba_type_out,
                 value=sum
             )]
+
+    def slots(self, model):
+        return (
+            (
+                Slot(type=model.cuba_type_in),
+            ),
+            (
+                Slot(type=model.cuba_type_out),
+            )
+        )
diff --git a/force_bdss/data_sources/base_data_source.py b/force_bdss/data_sources/base_data_source.py
index e0283b5..be9fb39 100644
--- a/force_bdss/data_sources/base_data_source.py
+++ b/force_bdss/data_sources/base_data_source.py
@@ -55,8 +55,14 @@ class BaseDataSource(ABCHasStrictTraits):
 
         Returns
         -------
-        list[tuple, tuple]
-            A list containing two tuples, the first element is the input slots,
-            the second element is the output slots. Each slot must be an
-            instance of the Slot class.
+        (input_slots, output_slots): tuple[tuple, tuple]
+            A tuple containing two tuples.
+            The first element is the input slots, the second element is
+            the output slots. Each slot must be an instance of the Slot class.
+            It is possible for each of the two inside tuples to be empty.
+            The case of an empty input slot is common: the DataSource does
+            not need any information from the MCO to operate.
+            The case of an empty output slot is uncommon, but supported:
+            the DataSource does not produce any output and is therefore
+            useless.
         """
diff --git a/force_bdss/data_sources/tests/test_base_data_source.py b/force_bdss/data_sources/tests/test_base_data_source.py
index 810b5f6..a4de041 100644
--- a/force_bdss/data_sources/tests/test_base_data_source.py
+++ b/force_bdss/data_sources/tests/test_base_data_source.py
@@ -13,6 +13,9 @@ class DummyDataSource(BaseDataSource):
     def run(self, *args, **kwargs):
         pass
 
+    def slots(self, model):
+        return (), ()
+
 
 class TestBaseDataSource(unittest.TestCase):
     def test_initialization(self):
diff --git a/force_bdss/kpi/base_kpi_calculator.py b/force_bdss/kpi/base_kpi_calculator.py
index 1bb1bf4..b757dd4 100644
--- a/force_bdss/kpi/base_kpi_calculator.py
+++ b/force_bdss/kpi/base_kpi_calculator.py
@@ -38,3 +38,32 @@ class BaseKPICalculator(ABCHasStrictTraits):
         List[DataValue]:
             The result of this KPI evaluation, as a list of DataValues.
         """
+
+    @abc.abstractmethod
+    def slots(self, model):
+        """Returns the input (and output) slots of the KPI Calculator.
+        Slots are the entities that are needed (and produced) by this
+        KPI Calculator.
+
+        The slots may depend on the configuration options, and thus the model.
+        This allows, for example, to change the slots depending if an option
+        is enabled or not.
+
+        Parameters
+        ----------
+        model: BaseKPICalculatorModel
+            The model of the KPICalculator, instantiated through create_model()
+
+        Returns
+        -------
+        (input_slots, output_slots): tuple[tuple, tuple]
+            A tuple containing two tuples.
+            The first element is the input slots, the second element is
+            the output slots. Each slot must be an instance of the Slot class.
+            It is possible for each of the two inside tuples to be empty.
+            The case of an empty input slot is common: the KPICalculator does
+            not need any information from the MCO to operate.
+            The case of an empty output slot is uncommon, but supported:
+            the KPICalculator does not produce any output and is therefore
+            useless.
+        """
diff --git a/force_bdss/kpi/tests/test_base_kpi_calculator.py b/force_bdss/kpi/tests/test_base_kpi_calculator.py
index 5483835..334c191 100644
--- a/force_bdss/kpi/tests/test_base_kpi_calculator.py
+++ b/force_bdss/kpi/tests/test_base_kpi_calculator.py
@@ -12,6 +12,9 @@ class DummyKPICalculator(BaseKPICalculator):
     def run(self, *args, **kwargs):
         pass
 
+    def slots(self, model):
+        return (), ()
+
 
 class TestBaseKPICalculator(unittest.TestCase):
     def test_initialization(self):
diff --git a/force_bdss/tests/test_core_evaluation_driver.py b/force_bdss/tests/test_core_evaluation_driver.py
index 3ce8480..fc79b48 100644
--- a/force_bdss/tests/test_core_evaluation_driver.py
+++ b/force_bdss/tests/test_core_evaluation_driver.py
@@ -80,6 +80,9 @@ class NullKPICalculator(BaseKPICalculator):
     def run(self, model, data_source_results):
         return []
 
+    def slots(self, model):
+        return (), ()
+
 
 class NullKPICalculatorBundle(BaseKPICalculatorBundle):
     def create_model(self, model_data=None):
@@ -97,6 +100,9 @@ class NullDataSource(BaseDataSource):
     def run(self, model, parameters):
         return []
 
+    def slots(self, model):
+        return (), ()
+
 
 class NullDataSourceBundle(BaseDataSourceBundle):
     def create_model(self, model_data=None):
-- 
GitLab