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

bugfix: Add missing inputs and outputs in REST API JSON payload #312

Merged
merged 4 commits into from
Jan 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 0 additions & 1 deletion src/sophios/api/http/restapi.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from pathlib import Path
import argparse
import copy
import uuid
import yaml


Expand Down
72 changes: 35 additions & 37 deletions src/sophios/api/utils/converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
4 changes: 2 additions & 2 deletions tests/data/wfb_data/multi_node/multi_node_wfb_truth.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Loading