diff --git a/environment.yml b/environment.yml index 3b78aaa5..8c367826 100644 --- a/environment.yml +++ b/environment.yml @@ -2,8 +2,15 @@ name: vai-lab-env channels: - conda-forge dependencies: - - python=3.11 + - python=3.7 - c-compiler + - pandas + - pillow + - scikit-learn + - ttkwidgets + - opencv + - pybullet + - attrs - pip - pip: - . diff --git a/pyproject.toml b/pyproject.toml index 21e92a40..a399a614 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,20 +14,11 @@ classifiers = [ "License :: OSI Approved :: MIT License", 'Programming Language :: Python :: 3.11' ] -requires-python = ">=3.11" +requires-python = ">=3.7" version = "0.0.dev4" description = "AI assisted Virtual Laboratory framework." dependencies = [ - "numpy >= 1.20.0", - "pillow >= 9.0.0", # package name of PIL - "pandas >= 1.4.0", - "scikit-learn >= 0.0", - "ttkwidgets >= 0.12.0", - "matplotlib >= 3.5.0", - "opencv-python >= 4.6.0.65", - "pybullet >= 3.2.5", - "attrs >= 23.1.0" ] [project.optional-dependencies] diff --git a/src/vai_lab/Core/vai_lab_core.py b/src/vai_lab/Core/vai_lab_core.py index 1395798f..56a9d8a4 100644 --- a/src/vai_lab/Core/vai_lab_core.py +++ b/src/vai_lab/Core/vai_lab_core.py @@ -6,7 +6,6 @@ from vai_lab._import_helper import import_module, rel_to_abs from vai_lab._plugin_helpers import PluginSpecs -from vai_lab._types import ModuleInterface, PluginSpecsInterface from vai_lab.GUI.GUI_core import GUI from vai_lab.Data.Data_core import Data from vai_lab.Data.xml_handler import XML_handler @@ -17,7 +16,7 @@ def __init__(self) -> None: self.data = {} self.data['Initialiser'] = Data() self._xml_handler = XML_handler() - self._avail_plugins: PluginSpecsInterface = PluginSpecs() + self._avail_plugins = PluginSpecs() self.loop_level: int = 0 self._initialised: bool = False @@ -28,7 +27,7 @@ def _launch(self): gui_app = GUI() gui_app._debug = self._debug gui_app.set_avail_plugins(self._avail_plugins) - gui_app.set_gui_as_startpage() + gui_app.set_gui_as_startpage() # FIXME gui_output = gui_app.launch() if not self._debug: try: @@ -63,7 +62,7 @@ def _execute_module(self, specs): :param specs: dict of module to be executed """ - mod: ModuleInterface = import_module(globals(), specs["module_type"]).__call__() + mod = import_module(globals(), specs["module_type"]).__call__() mod._debug = self._debug mod.set_avail_plugins(self._avail_plugins) self._load_data(specs, specs["name"]) diff --git a/src/vai_lab/DataProcessing/DataProcessing_core.py b/src/vai_lab/DataProcessing/DataProcessing_core.py index 19d2c576..25881123 100644 --- a/src/vai_lab/DataProcessing/DataProcessing_core.py +++ b/src/vai_lab/DataProcessing/DataProcessing_core.py @@ -1,22 +1,21 @@ from vai_lab._import_helper import import_plugin_absolute -from vai_lab._types import PluginSpecsInterface, DataInterface, DataProcessingPluginInterface from pandas import DataFrame from numpy import array class DataProcessing(object): def __init__(self) -> None: - self.output_data: DataInterface + self.output_data - def set_avail_plugins(self, avail_plugins: PluginSpecsInterface) -> None: + def set_avail_plugins(self, avail_plugins) -> None: self._avail_plugins = avail_plugins - def set_data_in(self, data_in: DataInterface) -> None: + def set_data_in(self, data_in) -> None: self._data_in = data_in - def _load_plugin(self, data_in: DataInterface) -> None: + def _load_plugin(self, data_in) -> None: avail_plugins = self._avail_plugins.find_from_readable_name( self._module_config["plugin"]["plugin_name"]) self.set_data_in(data_in) - self._plugin: DataProcessingPluginInterface = import_plugin_absolute(globals(), + self._plugin = import_plugin_absolute(globals(), avail_plugins["_PLUGIN_PACKAGE"], avail_plugins["_PLUGIN_CLASS_NAME"])\ .__call__(self._module_config["plugin"], data_in) @@ -40,5 +39,5 @@ def launch(self) -> None: if len(out) > 0 and (isinstance(out[0], DataFrame) or isinstance(out[0], array)): self.output_data.data[list(out[1])[0]] = out[0] - def get_result(self) -> DataInterface: + def get_result(self): return self.output_data \ No newline at end of file diff --git a/src/vai_lab/DataProcessing/plugins/normalizer.py b/src/vai_lab/DataProcessing/plugins/normalizer.py index 9b517397..cef2d910 100644 --- a/src/vai_lab/DataProcessing/plugins/normalizer.py +++ b/src/vai_lab/DataProcessing/plugins/normalizer.py @@ -1,5 +1,4 @@ from vai_lab._plugin_templates import DataProcessingT -from vai_lab._types import DataInterface from sklearn.preprocessing import Normalizer as model import pandas as pd diff --git a/src/vai_lab/DecisionMaking/DecisionMaking_core.py b/src/vai_lab/DecisionMaking/DecisionMaking_core.py index 0792744b..67888369 100644 --- a/src/vai_lab/DecisionMaking/DecisionMaking_core.py +++ b/src/vai_lab/DecisionMaking/DecisionMaking_core.py @@ -1,17 +1,16 @@ # -*- coding: utf-8 -*- from vai_lab._import_helper import import_plugin_absolute -from vai_lab._types import PluginSpecsInterface, DataInterface class DecisionMaking(object): def __init__(self): - self.output_data: DataInterface + self.output_data - def set_avail_plugins(self,avail_plugins: PluginSpecsInterface): + def set_avail_plugins(self,avail_plugins): self._avail_plugins = avail_plugins - def set_data_in(self,data_in: DataInterface): + def set_data_in(self,data_in): self._data_in = data_in - def _load_plugin(self, data_in: DataInterface): + def _load_plugin(self, data_in): avail_plugins = self._avail_plugins.find_from_readable_name(self._module_config["plugin"]["plugin_name"]) self.set_data_in(data_in) self._plugin = import_plugin_absolute(globals(),\ diff --git a/src/vai_lab/Environment/Environment_core.py b/src/vai_lab/Environment/Environment_core.py index c64fe48e..b819492b 100644 --- a/src/vai_lab/Environment/Environment_core.py +++ b/src/vai_lab/Environment/Environment_core.py @@ -1,21 +1,20 @@ from vai_lab._import_helper import import_plugin_absolute -from vai_lab._types import PluginSpecsInterface, DataInterface, EnvironmentPluginInterface class Environment(object): def __init__(self) -> None: - self.output_data: DataInterface + self.output_data - def set_avail_plugins(self, avail_plugins: PluginSpecsInterface) -> None: + def set_avail_plugins(self, avail_plugins) -> None: self._avail_plugins = avail_plugins - def set_data_in(self, data_in: DataInterface) -> None: + def set_data_in(self, data_in) -> None: self._data_in = data_in - def _load_plugin(self, data_in: DataInterface) -> None: + def _load_plugin(self, data_in) -> None: avail_plugins = self._avail_plugins.find_from_readable_name( self._module_config["plugin"]["plugin_name"]) self.set_data_in(data_in) - self._plugin: EnvironmentPluginInterface = import_plugin_absolute(globals(), + self._plugin = import_plugin_absolute(globals(), avail_plugins["_PLUGIN_PACKAGE"], avail_plugins["_PLUGIN_CLASS_NAME"])\ .__call__(self._module_config["plugin"], data_in) @@ -33,6 +32,6 @@ def launch(self) -> None: self._plugin.run_simulation() - def get_result(self) -> DataInterface: + def get_result(self): # return self.output_data return self._data_in \ No newline at end of file diff --git a/src/vai_lab/GUI/GUI_core.py b/src/vai_lab/GUI/GUI_core.py index be55f46e..296f0334 100644 --- a/src/vai_lab/GUI/GUI_core.py +++ b/src/vai_lab/GUI/GUI_core.py @@ -1,6 +1,5 @@ from vai_lab.Data.xml_handler import XML_handler from vai_lab._import_helper import import_plugin_absolute -from vai_lab._types import DataInterface, PluginSpecsInterface, DictT from vai_lab.Data.Data_core import Data from typing import Any @@ -32,13 +31,13 @@ def __init__(self, *args, **kwargs): self._is_startpage = False self.output = {} - def set_avail_plugins(self, avail_plugins: PluginSpecsInterface): + def set_avail_plugins(self, avail_plugins): self._avail_plugins = avail_plugins - def set_data_in(self, data_in: DataInterface): + def set_data_in(self, data_in): self._data_in = data_in - def set_options(self, module_config: DictT): + def set_options(self, module_config): """Send configuration arguments to GUI :param module_config: dict of settings to congfigure the plugin @@ -48,7 +47,7 @@ def set_options(self, module_config: DictT): def set_gui_as_startpage(self): self._is_startpage = True - self._load_plugin("main") + self._load_plugin("main") # FIXME self.xml_handler = XML_handler() self.xml_handler.new_config_file() @@ -78,10 +77,10 @@ def _add_UI_type_to_frames(self, ui_specs): :param ui_name: name of the UI method being loaded :type ui_name: str """ + plugin = import_plugin_absolute(globals(), ui_specs["_PLUGIN_PACKAGE"], - ui_specs["_PLUGIN_CLASS_NAME"]) - + ui_specs["_PLUGIN_CLASS_NAME"]) # FIXME self._desired_ui_types.append(plugin) self._compare_layer_priority(ui_specs) if ui_specs["_PLUGIN_MODULE_OPTIONS"]["required_children"] != None: @@ -98,9 +97,9 @@ def _load_plugin(self, ui_type: list): if isinstance(ui_type, list)\ else [ui_type] for ui in ui_type: - ui_specs = self._avail_plugins.find_from_readable_name(ui) + ui_specs = self._avail_plugins.find_from_readable_name(ui) try: - self._add_UI_type_to_frames(ui_specs) + self._add_UI_type_to_frames(ui_specs) # FIXME except ModuleNotFoundError as ex: from sys import exit print(ex.msg) diff --git a/src/vai_lab/InputData/InputData_core.py b/src/vai_lab/InputData/InputData_core.py index 883d7c99..5344713b 100644 --- a/src/vai_lab/InputData/InputData_core.py +++ b/src/vai_lab/InputData/InputData_core.py @@ -1,6 +1,5 @@ from vai_lab.Data.Data_core import Data from vai_lab._import_helper import import_plugin_absolute -from vai_lab._types import PluginSpecsInterface, DataInterface class InputData(Data): def __init__(self): @@ -9,7 +8,7 @@ def __init__(self): self.plugin_name = None self.output_data = None - def set_data_in(self, data_in: DataInterface) -> None: + def set_data_in(self, data_in) -> None: """Pass existing data from another module to be stored in this class""" self._data_in = data_in @@ -24,14 +23,14 @@ def set_options(self, module_config: dict) -> None: """ self._module_config = module_config - def set_avail_plugins(self, avail_plugins: PluginSpecsInterface) -> None: + def set_avail_plugins(self, avail_plugins) -> None: self._avail_plugins = avail_plugins - def _load_plugin(self, data_in: DataInterface) -> None: + def _load_plugin(self, data_in) -> None: avail_plugins = self._avail_plugins.find_from_readable_name( self._module_config["plugin"]["plugin_name"]) self.set_data_in(data_in) - self._plugin: PluginSpecsInterface = import_plugin_absolute(globals(), + self._plugin = import_plugin_absolute(globals(), avail_plugins["_PLUGIN_PACKAGE"], avail_plugins["_PLUGIN_CLASS_NAME"])\ .__call__(self._module_config["plugin"], data_in) diff --git a/src/vai_lab/Modelling/Modelling_core.py b/src/vai_lab/Modelling/Modelling_core.py index 9e27d3fd..46128097 100644 --- a/src/vai_lab/Modelling/Modelling_core.py +++ b/src/vai_lab/Modelling/Modelling_core.py @@ -1,18 +1,17 @@ # -*- coding: utf-8 -*- from vai_lab._import_helper import import_plugin_absolute -from vai_lab._types import PluginSpecsInterface, DataInterface class Modelling(object): def __init__(self): - self.output_data: DataInterface + self.output_data - def set_avail_plugins(self, avail_plugins: PluginSpecsInterface): + def set_avail_plugins(self, avail_plugins): self._avail_plugins = avail_plugins - def set_data_in(self,data_in: DataInterface): + def set_data_in(self,data_in): self._data_in = data_in - def _load_plugin(self, data_in: DataInterface): + def _load_plugin(self, data_in): avail_plugins = self._avail_plugins.find_from_readable_name(self._module_config["plugin"]["plugin_name"]) self.set_data_in(data_in) self._plugin = import_plugin_absolute(globals(),\ diff --git a/src/vai_lab/UserInteraction/plugins/ManualInput.py b/src/vai_lab/UserInteraction/plugins/ManualInput.py index b3d7ac1b..46a9a76e 100644 --- a/src/vai_lab/UserInteraction/plugins/ManualInput.py +++ b/src/vai_lab/UserInteraction/plugins/ManualInput.py @@ -1,6 +1,5 @@ from vai_lab._plugin_templates import UI from vai_lab._import_helper import get_lib_parent_dir -from vai_lab._types import DictT, DataInterface, GUICoreInterface import os import numpy as np @@ -25,10 +24,10 @@ class ManualInput(tk.Frame, UI): # type:ignore """Method of user interaction for binary or classification data""" - def __init__(self, parent, controller, config: DictT): + def __init__(self, parent, controller, config): self.parent = parent super().__init__(parent, bg=self.parent['bg']) - self.controller: GUICoreInterface = controller + self.controller = controller self.controller.title('Manual Input') self.dirpath = get_lib_parent_dir() @@ -36,7 +35,7 @@ def __init__(self, parent, controller, config: DictT): self.assets_path = os.path.join(self.dirpath, 'utils', 'resources', 'Assets') - self._data_in: DataInterface + self._data_in self._class_list = None self._config = config self.save_path = '' diff --git a/src/vai_lab/UserInteraction/plugins/OptimisationInput.py b/src/vai_lab/UserInteraction/plugins/OptimisationInput.py index d0027488..01d4084e 100644 --- a/src/vai_lab/UserInteraction/plugins/OptimisationInput.py +++ b/src/vai_lab/UserInteraction/plugins/OptimisationInput.py @@ -1,6 +1,5 @@ from vai_lab._plugin_templates import UI from vai_lab._import_helper import get_lib_parent_dir -from vai_lab._types import DictT, DataInterface, GUICoreInterface import os import numpy as np @@ -27,10 +26,10 @@ class OptimisationInput(tk.Frame, UI): # type:ignore """Method of user interaction for optimisation problems""" - def __init__(self, parent, controller, config: DictT): + def __init__(self, parent, controller, config): self.parent = parent super().__init__(parent, bg=self.parent['bg']) - self.controller: GUICoreInterface = controller + self.controller = controller self.controller.title('Optimisation Interaction') self.dirpath = get_lib_parent_dir() @@ -44,7 +43,7 @@ def __init__(self, parent, controller, config: DictT): self.assets_path = os.path.join(self.dirpath, 'utils', 'resources', 'Assets') - self._data_in: DataInterface + self._data_in self._config = config self.save_path = '' self.saved = True diff --git a/src/vai_lab/_import_helper.py b/src/vai_lab/_import_helper.py index 3095ddbc..70e4d514 100644 --- a/src/vai_lab/_import_helper.py +++ b/src/vai_lab/_import_helper.py @@ -9,10 +9,26 @@ def import_plugin(script_config, plugin_name): return plugin_class def import_plugin_absolute(script_config,plugin_package,plugin_name): - plugin_list = __import__(plugin_package, - script_config, - {}, - [plugin_name]) + print("script_config:", script_config) + print() + print("plugin_package:", plugin_package) + print() + print("plugin_name:", plugin_name) + + # FIXME + import traceback + try: + plugin_list = __import__(plugin_package, + script_config, + {}, + [plugin_name]) + except Exception as err: + print("\n\nERROR >>>\n\n") + print(traceback.format_exc()) + print("err:", err) + print("\n\n<<< ERROR\n\n") + + plugin_class = getattr(plugin_list, plugin_name) return plugin_class diff --git a/src/vai_lab/_plugin_helpers.py b/src/vai_lab/_plugin_helpers.py index 90b4003d..50f51712 100644 --- a/src/vai_lab/_plugin_helpers.py +++ b/src/vai_lab/_plugin_helpers.py @@ -2,13 +2,11 @@ import os from typing import Iterator, List, Union -from vai_lab._types import DictT - class PluginSpecs(ast.NodeVisitor): def __init__(self) -> None: - self.module_dirs: DictT = {} - self.available_plugins: DictT = {} + self.module_dirs = {} + self.available_plugins = {} self._get_plugin_files() self._get_plugin_specs() @@ -100,7 +98,7 @@ def visit_Assign(self, node): "", "eval")) self.available_plugins[self.curr_module][self.curr_plugin][key] = val - def _get_default_name_from_dict(self, plugin: DictT) -> str: + def _get_default_name_from_dict(self, plugin) -> str: default = list(plugin.keys())\ [list(plugin.values()).index("default")] return default @@ -115,7 +113,7 @@ def _get_option_specs(self, option: str) -> dict: :returns output: nested dict of options by [Module][Plugin Class Name] """ - output: DictT = {} + output = {} for module in self.available_plugins.keys(): output[module] = {} for plugin in self.available_plugins[module].keys(): @@ -124,7 +122,7 @@ def _get_option_specs(self, option: str) -> dict: output[module][rn] = self.available_plugins[module][plugin][option] return output - def _find_plugin_by_tag_and_value(self, tag: str, name: str) -> Union[DictT, None]: + def _find_plugin_by_tag_and_value(self, tag: str, name: str) -> Union[None]: for module in self.available_plugins.keys(): for plugin in self.available_plugins[module].keys(): if tag in self.available_plugins[module][plugin]: diff --git a/src/vai_lab/_plugin_templates.py b/src/vai_lab/_plugin_templates.py index 460c0343..82b84be8 100644 --- a/src/vai_lab/_plugin_templates.py +++ b/src/vai_lab/_plugin_templates.py @@ -1,5 +1,4 @@ from typing import Dict -from vai_lab._types import DataInterface from abc import ABC, abstractmethod import numpy as np @@ -37,7 +36,7 @@ def configure(self, config: dict) -> None: self._config = config self._parse_config() - def set_data_in(self, data_in: DataInterface) -> None: + def set_data_in(self, data_in) -> None: """Sets and parses incoming data :param data_in: saves data as class variable expected type: vai_lab.Data.Data_core.Data @@ -140,7 +139,7 @@ def _clean_options(self): """ return self._parse_options_dict(self._config["options"]) - def _test(self, data: DataInterface) -> DataInterface: + def _test(self, data): """Run debug tests on data operations TODO: Investigate if all plugins need a score and predict method """ @@ -222,7 +221,7 @@ def fit(self, options={}): +str(list(self._PLUGIN_READABLE_NAMES.keys())[list(self._PLUGIN_READABLE_NAMES.values()).index('default')])+': '+str(exc)+'.') raise - def transform(self, options={}) -> DataInterface: + def transform(self, options={}): try: if isinstance(options, list): return pd.DataFrame(self.transform_plugin(*options)) diff --git a/src/vai_lab/_types.py b/src/vai_lab/_types.py index 2d9d9470..7377d607 100644 --- a/src/vai_lab/_types.py +++ b/src/vai_lab/_types.py @@ -1,4 +1,4 @@ -from typing import Any, Protocol, KeysView, Dict, TypeVar +from typing import Any, KeysView, Dict, TypeVar from pandas.core.frame import DataFrame from tkinter.font import Font @@ -6,7 +6,7 @@ DictT = Dict[str, Dict] -class PluginOptions(Protocol): +class PluginOptions: _PLUGIN_READABLE_NAMES: dict _PLUGIN_MODULE_OPTIONS: dict _PLUGIN_REQUIRED_SETTINGS: dict @@ -15,7 +15,7 @@ class PluginOptions(Protocol): _PLUGIN_OPTIONAL_DATA: dict -class DataInterface(Protocol): +class DataInterface: def __init__(self) -> None: ... @@ -38,7 +38,7 @@ def __getitem__(self, key: str) -> DataFrame: ... -class PluginInterface(Protocol): +class PluginInterface: def configure(self, config: dict) -> None: ... @@ -46,14 +46,14 @@ def set_data_in(self, data_in: DataInterface) -> None: ... -class DataProcessingPluginInterface(PluginInterface, Protocol): +class DataProcessingPluginInterface(PluginInterface): def fit(self): ... def transform(self, data: DataInterfaceT) -> DataInterface: ... -class EnvironmentPluginInterface(PluginInterface, Protocol): +class EnvironmentPluginInterface(PluginInterface): def load_model(self) -> None: ... @@ -70,7 +70,7 @@ def run_simulation(self): ... -class PluginSpecsInterface(Protocol): +class PluginSpecsInterface: @property def names(self): ... @@ -109,7 +109,7 @@ def print(self, value): ... -class ModuleInterface(Protocol): +class ModuleInterface: _debug: bool def set_avail_plugins(self, avail_plugins: PluginSpecsInterface): @@ -127,7 +127,7 @@ def launch(self): def get_result(self) -> DataInterface: ... -class GUICoreInterface(ModuleInterface,Protocol): +class GUICoreInterface(ModuleInterface): title: Any #mypy bug prevents proper typing pages_font: Font @@ -137,6 +137,6 @@ def destroy(self) -> None: def set_gui(self) -> None: ... -class InputDataCoreInterface(ModuleInterface,Protocol): +class InputDataCoreInterface(ModuleInterface): def load_data_from_file(self, filename:str, data_id:str) -> None: ... \ No newline at end of file diff --git a/src/vai_lab/utils/plugins/aidCanvas.py b/src/vai_lab/utils/plugins/aidCanvas.py index 8e4d03a9..5cffc9bc 100644 --- a/src/vai_lab/utils/plugins/aidCanvas.py +++ b/src/vai_lab/utils/plugins/aidCanvas.py @@ -1,9 +1,7 @@ -from vai_lab._types import DictT - import os import numpy as np import pandas as pd -from typing import List, Literal,Tuple +from typing import List, Tuple from PIL import Image, ImageTk import tkinter as tk @@ -35,7 +33,7 @@ def __init__(self, parent, controller, config: dict): self.canvas_startxy: List[Tuple] = [] self.out_data = pd.DataFrame() - self.connections: DictT = {} + self.connections = {} self.modules = 0 self.module_list: List[str] = [] self.module_names: List[str] = []