diff --git a/force_bdss/base_core_driver.py b/force_bdss/base_core_driver.py new file mode 100644 index 0000000000000000000000000000000000000000..7f0fa6db586df4ecf71a8a388cc746f910d87a02 --- /dev/null +++ b/force_bdss/base_core_driver.py @@ -0,0 +1,63 @@ +from envisage.extension_point import ExtensionPoint +from envisage.plugin import Plugin +from traits.api import List + +from force_bdss.data_sources.i_data_source_bundle import ( + IDataSourceBundle) +from force_bdss.kpi.i_kpi_calculator_bundle import IKPICalculatorBundle +from force_bdss.mco.i_multi_criteria_optimizer_bundle import ( + IMultiCriteriaOptimizerBundle) + + +class BaseCoreDriver(Plugin): + """Main plugin that handles the execution of the MCO + or the evaluation. + """ + + # Note: we are forced to declare these extensions points here instead + # of the application object, and this is why we have to use this plugin. + # It is a workaround to an envisage bug that does not find the extension + # points if declared on the application. + + #: A List of the available Multi Criteria Optimizers. + #: This will be populated by MCO plugins. + mco_bundles = ExtensionPoint( + List(IMultiCriteriaOptimizerBundle), + id='force.bdss.mco.bundles') + + #: A list of the available Data Sources. + #: It will be populated by plugins. + data_source_bundles = ExtensionPoint( + List(IDataSourceBundle), + id='force.bdss.data_sources.bundles') + + #: A list of the available Key Performance Indicator calculators. + #: It will be populated by plugins. + kpi_calculator_bundles = ExtensionPoint( + List(IKPICalculatorBundle), + id='force.bdss.kpi_calculators.bundles') + + def _data_source_bundle_by_name(self, name): + for ds in self.data_source_bundles: + if ds.name == name: + return ds + + raise Exception("Requested data source {} but don't know " + "to find it.".format(name)) + + def _kpi_calculator_bundle_by_name(self, name): + for kpic in self.kpi_calculator_bundles: + if kpic.name == name: + return kpic + + raise Exception( + "Requested kpi calculator {} but don't know " + "to find it.".format(name)) + + def _mco_bundle_by_name(self, name): + for mco in self.mco_bundles: + if mco.name == name: + return mco + + raise Exception("Requested MCO {} but it's not available" + "to compute it.".format(name)) diff --git a/force_bdss/bdss_application.py b/force_bdss/bdss_application.py index e6abb68250d434c03d1f88ff68fae5b9b6c91141..64537cfa1a72ed6ab9a6db1aca9a99ca48a54342 100644 --- a/force_bdss/bdss_application.py +++ b/force_bdss/bdss_application.py @@ -1,6 +1,13 @@ import json +from stevedore import extension +from stevedore.exception import NoMatches from envisage.api import Application +from envisage.core_plugin import CorePlugin + +from force_bdss.core_evaluation_driver import CoreEvaluationDriver +from force_bdss.core_mco_driver import CoreMCODriver + from traits.api import Unicode, Bool, Instance from force_bdss.workspecs.workflow import Workflow @@ -22,6 +29,33 @@ class BDSSApplication(Application): #: coordination of the MCO itself. See design notes for more details. evaluate = Bool() + def __init__(self, evaluate, workflow_filepath): + self.evaluate = evaluate + self.workflow_filepath = workflow_filepath + + plugins = [CorePlugin()] + + if self.evaluate: + plugins.append(CoreEvaluationDriver()) + else: + plugins.append(CoreMCODriver()) + + mgr = extension.ExtensionManager( + namespace='force.bdss.extensions', + invoke_on_load=True + ) + + def import_extensions(ext): + print("Found extension {}".format(ext.name)) + plugins.append(ext.obj) + + try: + mgr.map(import_extensions) + except NoMatches: + print("No extensions found") + + super().__init__(plugins=plugins) + def _workflow_default(self): with open(self.workflow_filepath) as f: return Workflow.from_json(json.load(f)) diff --git a/force_bdss/cli/force_bdss.py b/force_bdss/cli/force_bdss.py index c4a26d123dd76722fb572cded61a63171675de91..5a389a96458265ac607cb1f752f40aeb6e3d7b3b 100644 --- a/force_bdss/cli/force_bdss.py +++ b/force_bdss/cli/force_bdss.py @@ -1,10 +1,6 @@ import click -from stevedore import extension -from stevedore.exception import NoMatches -from envisage.core_plugin import CorePlugin from force_bdss.bdss_application import BDSSApplication -from force_bdss.core_mco_driver import CoreMCODriver @click.command() @@ -12,27 +8,7 @@ from force_bdss.core_mco_driver import CoreMCODriver @click.argument('workflow_filepath', type=click.Path(exists=True)) def run(evaluate, workflow_filepath): - plugins = [ - CorePlugin(), - CoreMCODriver(), - ] - - mgr = extension.ExtensionManager( - namespace='force.bdss.extensions', - invoke_on_load=True - ) - - def import_extensions(ext): - print("Found extension {}".format(ext.name)) - plugins.append(ext.obj) - - try: - mgr.map(import_extensions) - except NoMatches: - print("No extensions found") - application = BDSSApplication( - plugins=plugins, evaluate=evaluate, workflow_filepath=workflow_filepath ) diff --git a/force_bdss/core_evaluation_driver.py b/force_bdss/core_evaluation_driver.py new file mode 100644 index 0000000000000000000000000000000000000000..831c539cf39ba31b68a97ea21c6f3eb43ad8cc32 --- /dev/null +++ b/force_bdss/core_evaluation_driver.py @@ -0,0 +1,43 @@ +from traits.has_traits import on_trait_change + +from force_bdss.base_core_driver import BaseCoreDriver + + +class CoreEvaluationDriver(BaseCoreDriver): + """Main plugin that handles the execution of the MCO + or the evaluation. + """ + + @on_trait_change("application:started") + def application_started(self): + workflow = self.application.workflow + + mco_data = workflow.multi_criteria_optimizer + mco_bundle = self._mco_bundle_by_name(mco_data.name) + mco_model = mco_bundle.create_model(mco_data.model_data) + mco_communicator = mco_bundle.create_communicator( + self.application, + mco_model) + + parameters = mco_communicator.receive_from_mco() + + ds_results = [] + for requested_ds in workflow.data_sources: + ds_bundle = self._data_source_bundle_by_name( + requested_ds.name) + ds_model = ds_bundle.create_model(requested_ds.model_data) + data_source = ds_bundle.create_data_source( + self.application, ds_model) + ds_results.append(data_source.run(parameters)) + + kpi_results = [] + for requested_kpic in workflow.kpi_calculators: + kpic_bundle = self._kpi_calculator_bundle_by_name( + requested_kpic.name) + ds_model = kpic_bundle.create_model( + requested_kpic.model_data) + kpi_calculator = kpic_bundle.create_data_source( + self.application, ds_model) + kpi_results.append(kpi_calculator.run(ds_results)) + + mco_communicator.send_to_mco(kpi_results) diff --git a/force_bdss/core_mco_driver.py b/force_bdss/core_mco_driver.py index 2b57471cb91e9aed1b9d20b7b753c3d7bbd9bea3..cc296dae4a20930f41d0d5a54755965db848661e 100644 --- a/force_bdss/core_mco_driver.py +++ b/force_bdss/core_mco_driver.py @@ -1,43 +1,13 @@ -from envisage.extension_point import ExtensionPoint -from envisage.plugin import Plugin -from traits.has_traits import on_trait_change -from traits.trait_types import List +from traits.api import on_trait_change -from force_bdss.data_sources.i_data_source_bundle import ( - IDataSourceBundle) -from force_bdss.kpi.i_kpi_calculator_bundle import IKPICalculatorBundle -from force_bdss.mco.i_multi_criteria_optimizer_bundle import ( - IMultiCriteriaOptimizerBundle) +from force_bdss.base_core_driver import BaseCoreDriver -class CoreMCODriver(Plugin): +class CoreMCODriver(BaseCoreDriver): """Main plugin that handles the execution of the MCO or the evaluation. """ - # Note: we are forced to declare these extensions points here instead - # of the application object, and this is why we have to use this plugin. - # It is a workaround to an envisage bug that does not find the extension - # points if declared on the application. - - #: A List of the available Multi Criteria Optimizers. - #: This will be populated by MCO plugins. - mco_bundles = ExtensionPoint( - List(IMultiCriteriaOptimizerBundle), - id='force.bdss.mco.bundles') - - #: A list of the available Data Sources. - #: It will be populated by plugins. - data_source_bundles = ExtensionPoint( - List(IDataSourceBundle), - id='force.bdss.data_sources.bundles') - - #: A list of the available Key Performance Indicator calculators. - #: It will be populated by plugins. - kpi_calculator_bundles = ExtensionPoint( - List(IKPICalculatorBundle), - id='force.bdss.kpi_calculators.bundles') - @on_trait_change("application:started") def application_started(self): workflow = self.application.workflow @@ -46,58 +16,5 @@ class CoreMCODriver(Plugin): mco_bundle = self._mco_bundle_by_name(mco_data.name) mco_model = mco_bundle.create_model(mco_data.model_data) mco = mco_bundle.create_optimizer(self.application, mco_model) - mco_communicator = mco_bundle.create_communicator( - self.application, - mco_model) - - if not self.application.evaluate: - mco.run() - return - - parameters = mco_communicator.receive_from_mco() - - ds_results = [] - for requested_ds in workflow.data_sources: - ds_bundle = self._data_source_bundle_by_name( - requested_ds.name) - ds_model = ds_bundle.create_model(requested_ds.model_data) - data_source = ds_bundle.create_data_source( - self.application, ds_model) - ds_results.append(data_source.run(parameters)) - - kpi_results = [] - for requested_kpic in workflow.kpi_calculators: - kpic_bundle = self._kpi_calculator_bundle_by_name( - requested_kpic.name) - ds_model = kpic_bundle.create_model( - requested_kpic.model_data) - kpi_calculator = kpic_bundle.create_data_source( - self.application, ds_model) - kpi_results.append(kpi_calculator.run(ds_results)) - - mco_communicator.send_to_mco(kpi_results) - - def _data_source_bundle_by_name(self, name): - for ds in self.data_source_bundles: - if ds.name == name: - return ds - - raise Exception("Requested data source {} but don't know " - "to find it.".format(name)) - - def _kpi_calculator_bundle_by_name(self, name): - for kpic in self.kpi_calculator_bundles: - if kpic.name == name: - return kpic - - raise Exception( - "Requested kpi calculator {} but don't know " - "to find it.".format(name)) - - def _mco_bundle_by_name(self, name): - for mco in self.mco_bundles: - if mco.name == name: - return mco - raise Exception("Requested MCO {} but it's not available" - "to compute it.".format(name)) + mco.run()