Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Css-methodSelection #113

Merged
merged 49 commits into from
Oct 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
82028a0
Update functionSelection (#108)
sevisal Jun 21, 2023
a146b94
Create a list with available functions
sevisal Jun 21, 2023
8cd9ded
Fine-tuning merge
sevisal Jul 7, 2023
50727af
Change model name inside class
sevisal Jul 7, 2023
8e054b4
Display all available class optional parameters
sevisal Jul 7, 2023
489719a
Read required settings from the class
sevisal Jul 7, 2023
cff2d6f
Remove some unrequired parameters
sevisal Jul 7, 2023
9dc4f0c
Set arguments with default values as optional
sevisal Jul 7, 2023
40fd902
Define function to get arguments from function
sevisal Jul 10, 2023
09fc1d1
Updated settings display
sevisal Jul 10, 2023
5f7ef77
Add treview for function selection
sevisal Jul 11, 2023
98631f1
GUI for function definition
sevisal Jul 11, 2023
7a4cded
Changed solve method to fit
sevisal Jul 11, 2023
11b46e5
Save methods in XML
sevisal Jul 11, 2023
c3dac48
Add tag "options" to plugins
sevisal Jul 17, 2023
4ecc32e
Load options from XML
sevisal Jul 18, 2023
8637317
Using methods and options defined in XML
sevisal Jul 19, 2023
b530965
Fix issue with canvas interaction
sevisal Jul 19, 2023
66c932a
Allow having methods without options
sevisal Jul 20, 2023
c2c454c
Indicate method's data
sevisal Jul 20, 2023
2d4706f
Remove 'Data' from DataProcessing modules
sevisal Jul 21, 2023
b3d0f98
Store options
sevisal Jul 21, 2023
d0d3e1a
Reset XML if reset canvas
sevisal Jul 21, 2023
f38c37b
Change default values for input data list
sevisal Jul 24, 2023
dcf19c1
Load data before XML
sevisal Jul 26, 2023
d376bcb
Correctly update settings dictionary
sevisal Jul 27, 2023
397e3ac
Method-wise data
sevisal Jul 27, 2023
1667f40
Fix issue with stored values and changed test data
sevisal Sep 15, 2023
aedc426
Combine Initialiser if previous XML existed
sevisal Sep 15, 2023
16774eb
Fixed issue with correctly updating loaded data
sevisal Sep 19, 2023
337491b
ProgressTracking working for functions
sevisal Sep 19, 2023
bf45c15
Progress tracker loads the plugin's options
sevisal Sep 22, 2023
a1228d6
Add methods and options to Progress tracker
sevisal Sep 22, 2023
9fc6099
Fixed error with plugin trereview
sevisal Sep 22, 2023
ac3fe9a
Remove reuirement of having methods and options
sevisal Sep 22, 2023
c9eca5e
Update example XML files
sevisal Sep 22, 2023
0840e2e
Remove tests
sevisal Sep 22, 2023
9d4393f
Merge branch 'main' into css-functionSelection
sevisal Sep 22, 2023
1e48acb
Fix error with user interaction demo
sevisal Sep 22, 2023
d1f23c2
Modify example to use test data
sevisal Sep 26, 2023
3d6d783
Fix path issue with xml
sevisal Sep 29, 2023
0124cfc
Fixed issue with relative paths
sevisal Sep 29, 2023
614cdf6
Solve issue with relative paths
sevisal Oct 3, 2023
8b5471f
Restructure conditions
sevisal Oct 5, 2023
9eea132
Fix options reading from XML and change data reading for looping
sevisal Oct 5, 2023
19f2052
Fixed issue with options in multiple lines
sevisal Oct 5, 2023
b1a60aa
MOD: update to Python 3.11
ruokolt Oct 5, 2023
03b6b2a
FIX: python3.11 for sphinx
ruokolt Oct 5, 2023
bfce8f3
DEL: delete obsolete workflow
ruokolt Oct 5, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/pytest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ["3.10", "3.11"]
python-version: ["3.11"]
fail-fast: false

steps:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/sphinx.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: '3.10'
python-version: '3.11'

# pip cache
- name: pip cache
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ __pycache__/
**/*.pyc
*.py[cod]
*$py.class
*-env/

# C extensions
*.so
Expand Down
2 changes: 1 addition & 1 deletion dev-environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: dev-vai-lab-env
channels:
- conda-forge
dependencies:
- python=3.10
- python=3.11
- c-compiler
- pip
- pip:
Expand Down
2 changes: 1 addition & 1 deletion environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: vai-lab-env
channels:
- conda-forge
dependencies:
- python=3.10
- python=3.11
- c-compiler
- pip
- pip:
Expand Down
3 changes: 1 addition & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,9 @@ authors = [
readme = "README.md"
classifiers = [
"License :: OSI Approved :: MIT License",
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11'
]
requires-python = ">=3.8"
requires-python = ">=3.11"
version = "0.0.dev4"
description = "AI assisted Virtual Laboratory framework."

Expand Down
2 changes: 1 addition & 1 deletion sphinx-environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: sphinx-env
channels:
- conda-forge
dependencies:
- python=3.10
- python=3.11
- sphinx
- sphinx-rtd-theme
- myst-parser
1 change: 0 additions & 1 deletion sphinx-requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# requirements.txt for sphinx
sphinx
sphinx-rtd-theme
myst-parser
10 changes: 5 additions & 5 deletions src/vai_lab/Core/vai_lab_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ def load_config_file(self, filename: Union[str,List,Tuple]):
self._xml_handler.load_XML(filedir)
self._initialised = True

def _load_data(self, module = 'Initialiser') -> None:
def _load_data(self, specs, module = 'Initialiser') -> None:
"""Loads data from XML file into Data object"""
init_data_fn = self._xml_handler.data_to_load(module)
init_data_fn = self._xml_handler.data_to_load(modules=specs, module=module)
if module not in self.data.keys():
self.data[module] = Data()
if isinstance(init_data_fn, str):
Expand All @@ -66,7 +66,7 @@ def _execute_module(self, specs):
mod: ModuleInterface = import_module(globals(), specs["module_type"]).__call__()
mod._debug = self._debug
mod.set_avail_plugins(self._avail_plugins)
self._load_data(specs["name"])
self._load_data(specs, specs["name"])
mod.set_data_in(self.data[specs["name"]])
mod.set_options(specs)
print("\t"*self.loop_level
Expand Down Expand Up @@ -108,6 +108,7 @@ def _execute_exit_point(self, specs):

with open(rel_to_abs(specs['plugin']['options']['outpath']), 'wb') as handle:
pickle.dump(data_out, handle, protocol=pickle.HIGHEST_PROTOCOL)

def _parse_loop_condition(self, condition):
try:
condition = int(condition)
Expand Down Expand Up @@ -169,7 +170,6 @@ def _execute(self, specs):

if not _tracker['terminate']:
self.load_config_file(self._xml_handler.filename)
# pass
else:
print('Pipeline terminated')
exit()
Expand All @@ -185,7 +185,7 @@ def run(self):
self._initialise_with_gui()
print("Running pipeline...")
if len(self._xml_handler.loaded_modules) > 0:
self._load_data()
self._load_data(self._xml_handler.loaded_modules)

self._init_status(self._xml_handler.loaded_modules)
self._execute(self._xml_handler.loaded_modules)
Expand Down
146 changes: 124 additions & 22 deletions src/vai_lab/Data/xml_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
root_mod = path.dirname(path.dirname(path.dirname(__file__)))
sys.path.append(root_mod)

from vai_lab._import_helper import get_lib_parent_dir
from vai_lab._import_helper import get_lib_parent_dir, rel_to_abs


class XML_handler:
Expand All @@ -30,6 +30,8 @@ def __init__(self, filename: str = None):
"pipeline": "declaration",
"relationships": "relationships",
"plugin": "plugin",
"method": "method",
"options": "options",
"coordinates": "list",
"Initialiser": "entry_point",
"inputdata": "data",
Expand Down Expand Up @@ -91,9 +93,42 @@ def load_XML(self, filename: str) -> None:
"""
if filename != None:
self.set_filename(filename)
self.tree = ET.parse(self.filename)
if hasattr(self, 'tree'):
prev_tree = self.tree
self.tree = self._combine_XML(prev_tree.getroot(), ET.parse(self.filename).getroot())
else:
self.tree = ET.parse(self.filename)
self._parse_XML()

def _combine_XML(self, tree1, tree2):
"""
This function recursively updates either the text or the children
of an element if another element is found in `tree1`, or adds it
from `tree2` if not found.
"""
# Create a mapping from tag name to element, as that's what we are fltering with
mapping = {el.tag: el for el in tree1}
for el in tree2:
if len(el) == 0:
# Not nested
try:
# Update the text
mapping[el.tag].text = el.text
except KeyError:
# An element with this name is not in the mapping
mapping[el.tag] = el
# Add it
tree1.append(el)
else :
try:
# Recursively process the element, and update it in the same way
self._combine_XML(mapping[el.tag], el)
except KeyError:
# Not in the mapping
mapping[el.tag] = el
tree1.append(el)
return ET.ElementTree(tree1)

def _parse_XML(self) -> None:
self.root = self.tree.getroot()
self._parse_tags(self.root, self.loaded_modules)
Expand Down Expand Up @@ -144,18 +179,38 @@ def _load_plugin(self, element: ET.Element, parent: dict) -> None:
"""
parent["plugin"] = {}
parent["plugin"]["plugin_name"] = element.attrib["type"]
parent["plugin"]["methods"] = {"_order" : []}
parent["plugin"]["options"] = {}
self._parse_tags(element, parent["plugin"])

def _load_method(self, element: ET.Element, parent: dict) -> None:
"""Parses tags associated with methods and appends to parent dict
:param elem: xml.etree.ElementTree.Element to be parsed
:param parent: dict or dict fragment parsed tags will be appened to
"""
for key in element.attrib:
parent["methods"]["_order"].append(element.attrib[key])
parent["methods"][element.attrib[key]] = {'options': {}}
self._parse_tags(element, parent["methods"][element.attrib[key]])

def _load_options(self, element: ET.Element, parent: dict) -> None:
"""Parses tags associated with options and appends to parent dict
:param elem: xml.etree.ElementTree.Element to be parsed
:param parent: dict or dict fragment parsed tags will be appened to
"""
for child in element:
if child.text is not None:
val = self._parse_text_to_list(child)
val = (val[0] if len(val) == 1 else val)
parent["plugin"]["options"][child.tag] = val
try:
parent["options"][child.tag] = literal_eval(child.text.strip())
except Exception as exc:
val = self._parse_text_to_list(child)
parent["options"][child.tag] = (val[0] if len(val) == 1 else val)

for key in child.attrib:
if key == "val":
parent["plugin"]["options"][child.tag] = child.attrib[key]
parent["options"][child.tag] = child.attrib[key]
else:
parent["plugin"]["options"][child.tag] = {
key: child.attrib[key]}
parent["options"][child.tag] = {key: child.attrib[key]}

def _load_entry_point(self, element: ET.Element, parent: dict) -> None:
"""Parses tags associated with initialiser and appends to parent dict
Expand Down Expand Up @@ -453,30 +508,33 @@ def update_plugin_options(self,
else:
xml_parent = xml_parent_name
plugin_elem: ET.Element = xml_parent.find("./plugin")
self._add_plugin_options(plugin_elem, options)
self._add_options(plugin_elem, options)
self._parse_XML()
if save_changes:
self.write_to_XML()

def _add_plugin_options(self,
def _add_options(self,
plugin_elem: ET.Element,
options
):
opt_elem = plugin_elem.find("./options")
if opt_elem is None:
opt_elem = ET.SubElement(plugin_elem, "options")
for key in options.keys():
if isinstance(options[key], list):
new_option = ET.SubElement(plugin_elem, key)
new_option = ET.SubElement(opt_elem, key)
option_text = ("\n{}".format(
"\n".join([*options[key]])))
new_option.text = option_text
elif isinstance(options[key], (int, float, str)):
new_option = plugin_elem.find(str("./" + key))
new_option = opt_elem.find(str("./" + key))
if new_option is None:
new_option = ET.SubElement(plugin_elem, key)
new_option = ET.SubElement(opt_elem, key)
text_lead = "\n" if "\n" not in str(options[key]) else ""
new_option.text = "{0} {1}".format(
text_lead, str(options[key]))
elif isinstance(options[key], (dict)):
self._add_plugin_options(plugin_elem, options[key])
self._add_options(opt_elem, options[key])

def append_input_data(self,
data_name: str,
Expand Down Expand Up @@ -511,14 +569,44 @@ def append_input_data(self,
if save_dir_as_relative:
data_dir = data_dir.replace(self.lib_base_path, "./")
data_dir = data_dir.replace("\\", "/")
if path.exists(path.dirname(data_dir)):
if path.exists(path.dirname(rel_to_abs(data_dir))):
plugin_elem.set('file', data_dir)
else:
plugin_elem.set('module', data_dir)

def append_method_to_plugin(self,
method_type: str,
method_options: dict,
xml_parent: Union[ET.Element, str],
overwrite_existing: Union[bool, int] = False
):
"""Appened method as subelement to existing plugin element

:param method_type: string type of method to be loaded into plugin
:param method_options: dict where keys & values are options & values
:param xml_parent: dict OR str.
If string given, parent elem is found via search,
Otherwise, method appeneded directly
"""
if isinstance(xml_parent, str):
xml_parent = self._get_element_from_name(xml_parent)

method_elem = xml_parent.find("./method")

if method_elem is not None and overwrite_existing:
if method_elem.attrib['type'] == method_type:
xml_parent.remove(method_elem)
method_elem = None

if method_elem is None:
method_elem = ET.SubElement(xml_parent, "method")
method_elem.set('type', method_type)
self._add_options(method_elem, method_options)

def append_plugin_to_module(self,
plugin_type: str,
plugin_options: dict,
method_list: list,
plugin_data: str,
xml_parent: Union[ET.Element, str],
overwrite_existing: Union[bool, int] = False
Expand Down Expand Up @@ -546,7 +634,13 @@ def append_plugin_to_module(self,
plugin_elem.set('type', plugin_type)
if plugin_data is not None and len(plugin_data) > 0:
self.append_input_data('X', plugin_data, xml_parent, False)
self._add_plugin_options(plugin_elem, plugin_options)
if '__init__' in plugin_options.keys():
self._add_options(plugin_elem, plugin_options['__init__'])
for f in method_list:
self.append_method_to_plugin(f,
plugin_options[f],
plugin_elem,
overwrite_existing)

def append_pipeline_module(self,
module_type: str,
Expand Down Expand Up @@ -577,6 +671,7 @@ def append_pipeline_module(self,
if plugin_type != None:
self.append_plugin_to_module(plugin_type,
plugin_options,
[],
parents[0],
new_mod,
0
Expand Down Expand Up @@ -625,10 +720,15 @@ def append_pipeline_loop(self,

xml_parent_element.append(new_loop)

def _get_data_structure(self, module) -> Dict[str, Any]:
data_struct = self._find_dict_with_key_val_pair(
self.loaded_modules[module],
"class", "data")
def _get_data_structure(self, modules, module) -> Dict[str, Any]:
try:
data_struct = self._find_dict_with_key_val_pair(
modules[module],
"class", "data")
except Exception as exc:
data_struct = self._find_dict_with_key_val_pair(
modules,
"class", "data")

assert len(data_struct) < 2, \
"Multiple data with same ID, please check XML"
Expand All @@ -641,8 +741,10 @@ def _get_data_structure(self, module) -> Dict[str, Any]:
return out

#@property
def data_to_load(self, module='Initialiser') -> Dict[str, str]:
return self._get_data_structure(module)["to_load"]
def data_to_load(self, modules=False, module='Initialiser') -> Dict[str, str]:
if not modules:
modules = self.loaded_modules
return self._get_data_structure(modules, module)["to_load"]


# Use case examples:
Expand Down
12 changes: 9 additions & 3 deletions src/vai_lab/DataProcessing/DataProcessing_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,14 @@ def set_options(self, module_config: dict) -> None:
def launch(self) -> None:
self._plugin.set_data_in(self._data_in)
self._plugin.configure(self._module_config["plugin"])
self._plugin.fit()
self.output_data = self._plugin.transform(self._data_in)
self._plugin.init()
for method in self._module_config["plugin"]["methods"]["_order"]:
if "options" in self._module_config["plugin"]["methods"][method].keys():
getattr(self._plugin, "{}".format(method))(self._plugin._parse_options_dict(self._module_config["plugin"]["methods"][method]["options"]))
else:
getattr(self._plugin, "{}".format(method))()

self.output_data = self._data_in.copy()

def get_result(self) -> DataInterface:
return self.output_data
return self.output_data
Loading
Loading