From bbff14da183e8434422bb0258327ad3c1b5ba084 Mon Sep 17 00:00:00 2001 From: Sameeul Samee Date: Wed, 8 Jan 2025 00:05:54 -0500 Subject: [PATCH 1/3] Fix logic for fixing missing inputs and outputs --- src/sophios/api/utils/converter.py | 69 ++++++++++++++++-------------- 1 file changed, 36 insertions(+), 33 deletions(-) diff --git a/src/sophios/api/utils/converter.py b/src/sophios/api/utils/converter.py index 9c64b5ca..00cd90e6 100644 --- a/src/sophios/api/utils/converter.py +++ b/src/sophios/api/utils/converter.py @@ -327,45 +327,48 @@ def update_payload_missing_inputs_outputs(wfb_data: Json) -> Json: nodes = wfb_data_copy["state"]["nodes"] plugins = wfb_data_copy["plugins"] - # hashmap of node id to nodes for fast node lookup - nodes_dict = {node['id']: node for node in nodes} - # hashmap of plugins id to nodes for fast plugin lookup - plugins_dict = {plugin['pid']: plugin for plugin in plugins} - - # find links corresponding to the node - for link in links: - - # link ids - target_id: int = link["targetId"] - source_id: int = link["sourceId"] - - target_node = nodes_dict[target_id] - source_node = nodes_dict[source_id] - - # plugins corresponding to the nodes - target_plugin = plugins_dict[target_node["pluginId"]] - source_plugin = plugins_dict[source_node["pluginId"]] + if plugins != []: # use the look up logic similar to WFB + plugin_config_map: dict[str, dict] = {} + for plugin in plugins: + pid: str = plugin.get("pid", "") + if pid == "": + continue + plugin_config_map[pid] = get_node_config(plugin) + # hashmap of node id to nodes for fast node lookup + nodes_dict = {node['id']: node for node in nodes} + for link in links: + src_node = nodes_dict[link['sourceId']] + tgt_node = nodes_dict[link['targetId']] - def is_inlet(binding: Json) -> bool: - """Check if a wfb input is an inlet (directory)""" + src_node_ui_config = plugin_config_map.get(src_node['pluginId'], None) + tgt_node_ui_config = plugin_config_map.get(tgt_node['pluginId'], None) - return ( - binding['type'] in ['directory', 'file', 'path', 'collection', 'csvCollection'] or - binding['name'].lower() == 'inpdir' or - binding['name'].lower().endswith('path') or - binding['name'].lower().endswith('dir') - ) + if src_node_ui_config and tgt_node_ui_config: + inlet_index = link['inletIndex'] + outlet_index = link['outletIndex'] + src_node_out_arg = src_node_ui_config['outputs'][outlet_index]["name"] + tgt_node_in_arg = tgt_node_ui_config['inputs'][inlet_index]["name"] - # filter inputs by to only be inlets (directories) - input_directories = [binding for binding in target_plugin["inputs"] if is_inlet(binding)] - output_directories = [binding for binding in source_plugin["outputs"] if is_inlet(binding)] + tgt_node["settings"]["inputs"][tgt_node_in_arg] = src_node["settings"]["outputs"][src_node_out_arg] - missing_input_key = input_directories[link["inletIndex"]]["name"] - missing_output_key = output_directories[link["outletIndex"]]["name"] + # add missing outputs + plugin_output_map: dict[str, dict] = {} + for plugin in plugins: + pid: str = plugin.get("pid", "") + if pid == "": + continue + plugin_output_map[pid] = plugin.get("outputs", []) + for node in nodes: + base_name = node["name"].replace(" ", "").lower() + "_" + str(node["id"]) + if "outputs" not in node["settings"]: + node["settings"]["outputs"] = {} + node_outputs = node["settings"]["outputs"] + plugin_outputs = plugin_output_map.get(node["pluginId"], []) + for output in plugin_outputs: + if output["name"] not in node_outputs: + node_outputs[output["name"]] = base_name + "_" + output["name"] - # add the missing input value to the node if needed - target_node["settings"]["inputs"][missing_input_key] = source_node["settings"]["outputs"][missing_output_key] if validate_schema_and_object(SCHEMA, wfb_data_copy): print('Updated object is valid against input object schema') From bc6d9c6d8cdc9a2a68108492c3ad09d6f4016e55 Mon Sep 17 00:00:00 2001 From: Sameeul Samee Date: Wed, 8 Jan 2025 02:56:28 -0500 Subject: [PATCH 2/3] Fix logic for fixing missing inputs and outputs --- src/sophios/api/http/restapi.py | 1 - src/sophios/api/utils/converter.py | 37 +++++++++++++----------------- 2 files changed, 16 insertions(+), 22 deletions(-) diff --git a/src/sophios/api/http/restapi.py b/src/sophios/api/http/restapi.py index f68a5de9..2bae0d39 100644 --- a/src/sophios/api/http/restapi.py +++ b/src/sophios/api/http/restapi.py @@ -1,7 +1,6 @@ from pathlib import Path import argparse import copy -import uuid import yaml diff --git a/src/sophios/api/utils/converter.py b/src/sophios/api/utils/converter.py index 00cd90e6..971aa37e 100644 --- a/src/sophios/api/utils/converter.py +++ b/src/sophios/api/utils/converter.py @@ -327,16 +327,30 @@ def update_payload_missing_inputs_outputs(wfb_data: Json) -> Json: nodes = wfb_data_copy["state"]["nodes"] plugins = wfb_data_copy["plugins"] - - if plugins != []: # use the look up logic similar to WFB + if plugins != []: plugin_config_map: dict[str, dict] = {} + plugin_output_map: dict[str, list] = {} for plugin in plugins: pid: str = plugin.get("pid", "") if pid == "": continue plugin_config_map[pid] = get_node_config(plugin) + plugin_output_map[pid] = plugin.get("outputs", []) + + # add missing outputs + for node in nodes: + base_name = node["name"].replace(" ", "").lower() + "_" + str(node["id"]) + if "outputs" not in node["settings"]: + node["settings"]["outputs"] = {} + node_outputs = node["settings"]["outputs"] + plugin_outputs: list = plugin_output_map.get(node["pluginId"], []) + for output in plugin_outputs: + if output["type"] == "path" and output["name"] not in node_outputs: + node_outputs[output["name"]] = base_name + "-" + output["name"] + # hashmap of node id to nodes for fast node lookup nodes_dict = {node['id']: node for node in nodes} + # use the look up logic similar to WFB to connect inputs and outputs for link in links: src_node = nodes_dict[link['sourceId']] tgt_node = nodes_dict[link['targetId']] @@ -349,27 +363,8 @@ def update_payload_missing_inputs_outputs(wfb_data: Json) -> Json: outlet_index = link['outletIndex'] src_node_out_arg = src_node_ui_config['outputs'][outlet_index]["name"] tgt_node_in_arg = tgt_node_ui_config['inputs'][inlet_index]["name"] - tgt_node["settings"]["inputs"][tgt_node_in_arg] = src_node["settings"]["outputs"][src_node_out_arg] - # add missing outputs - plugin_output_map: dict[str, dict] = {} - for plugin in plugins: - pid: str = plugin.get("pid", "") - if pid == "": - continue - plugin_output_map[pid] = plugin.get("outputs", []) - for node in nodes: - base_name = node["name"].replace(" ", "").lower() + "_" + str(node["id"]) - if "outputs" not in node["settings"]: - node["settings"]["outputs"] = {} - node_outputs = node["settings"]["outputs"] - plugin_outputs = plugin_output_map.get(node["pluginId"], []) - for output in plugin_outputs: - if output["name"] not in node_outputs: - node_outputs[output["name"]] = base_name + "_" + output["name"] - - if validate_schema_and_object(SCHEMA, wfb_data_copy): print('Updated object is valid against input object schema') From 27ff1f6d86403499b318769a6f34619f910cb621 Mon Sep 17 00:00:00 2001 From: Sameeul Samee Date: Wed, 8 Jan 2025 03:52:58 -0500 Subject: [PATCH 3/3] update test ground truth --- tests/data/wfb_data/multi_node/multi_node_wfb_truth.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/data/wfb_data/multi_node/multi_node_wfb_truth.json b/tests/data/wfb_data/multi_node/multi_node_wfb_truth.json index 3c0d5ace..93c60565 100644 --- a/tests/data/wfb_data/multi_node/multi_node_wfb_truth.json +++ b/tests/data/wfb_data/multi_node/multi_node_wfb_truth.json @@ -100,8 +100,8 @@ "width": 250, "settings": { "inputs": { - "stitchPath": "omeconverter_2-outDir", - "imgPath": "montage_4-outDir" + "stitchPath": "montage_4-outDir", + "imgPath": "omeconverter_2-outDir" }, "outputs": { "outDir": "imageassembler_5-outDir"