From 20f0865bc50995d4a64879c7e83129d950a037d2 Mon Sep 17 00:00:00 2001 From: Sameeul Bashir Samee Date: Wed, 8 Jan 2025 13:27:56 -0500 Subject: [PATCH] bugfix: Add missing inputs and outputs in REST API JSON payload (#312) * Fix logic for fixing missing inputs and outputs * Fix logic for fixing missing inputs and outputs * update test ground truth --- src/sophios/api/http/restapi.py | 1 - src/sophios/api/utils/converter.py | 72 +++++++++---------- .../multi_node/multi_node_wfb_truth.json | 4 +- 3 files changed, 37 insertions(+), 40 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 9c64b5ca..971aa37e 100644 --- a/src/sophios/api/utils/converter.py +++ b/src/sophios/api/utils/converter.py @@ -327,45 +327,43 @@ 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} + 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']] - # 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"]] - - def is_inlet(binding: Json) -> bool: - """Check if a wfb input is an inlet (directory)""" - - 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') - ) - - # 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)] - - missing_input_key = input_directories[link["inletIndex"]]["name"] - missing_output_key = output_directories[link["outletIndex"]]["name"] + src_node_ui_config = plugin_config_map.get(src_node['pluginId'], None) + tgt_node_ui_config = plugin_config_map.get(tgt_node['pluginId'], None) - # 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 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"] + tgt_node["settings"]["inputs"][tgt_node_in_arg] = src_node["settings"]["outputs"][src_node_out_arg] if validate_schema_and_object(SCHEMA, wfb_data_copy): print('Updated object is valid against input object schema') 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"