@@ -55,14 +55,7 @@ def extract_state(inp: Json) -> Json:
5555 inp_inter ['state' ]['links' ] = step_edges
5656 # massage the plugins
5757 plugins = inp_inter ['plugins' ]
58- # drop incorrect/superfluous UI fields from plugins
59- # 'required' and 'format'
60- for ict_plugin in plugins :
61- for ui_elem in ict_plugin ['ui' ]:
62- _ , _ = ui_elem .pop ('required' , None ), ui_elem .pop ('format' , None )
63- for out in ict_plugin ['outputs' ]:
64- if out ['name' ] == 'outDir' :
65- ict_plugin ['inputs' ].append (out )
58+
6659 # Here goes the ICT to CLT extraction logic
6760 for node in inp_inter ['state' ]['nodes' ]:
6861 node_pid = node ["pluginId" ]
@@ -78,8 +71,6 @@ def extract_state(inp: Json) -> Json:
7871
7972def raw_wfb_to_lean_wfb (inp : Json ) -> Json :
8073 """Drop all the unnecessary info from incoming wfb object"""
81- if validate_schema_and_object (SCHEMA , inp ):
82- print ('incoming object is valid against input object schema' )
8374 inp_restrict = extract_state (inp )
8475 keys = list (inp_restrict .keys ())
8576 # To avoid deserialization
@@ -193,3 +184,66 @@ def ict_to_clt(ict: Union[ICT, Path, str, dict], network_access: bool = False) -
193184 ict_local = ict if isinstance (ict , ICT ) else cast_to_ict (ict )
194185
195186 return ict_local .to_clt (network_access = network_access )
187+
188+
189+ def update_payload_missing_inputs_outputs (wfb_data : Json ) -> Json :
190+ """Update payload with missing inputs and outputs using links"""
191+
192+ # ensure the incoming wfb data is valid
193+ if validate_schema_and_object (SCHEMA , wfb_data ):
194+ print ('incoming object is valid against input object schema' )
195+
196+ # return if no plugins are found in data
197+ if not wfb_data ['plugins' ]:
198+ return wfb_data
199+
200+ wfb_data_copy = copy .deepcopy (wfb_data )
201+
202+ links = wfb_data_copy ["state" ]["links" ]
203+ nodes = wfb_data_copy ["state" ]["nodes" ]
204+ plugins = wfb_data_copy ["plugins" ]
205+
206+ # hashmap of node id to nodes for fast node lookup
207+ nodes_dict = {node ['id' ]: node for node in nodes }
208+
209+ # hashmap of plugins id to nodes for fast plugin lookup
210+ plugins_dict = {plugin ['pid' ]: plugin for plugin in plugins }
211+
212+ # find links corresponding to the node
213+ for link in links :
214+
215+ # link ids
216+ target_id : int = link ["targetId" ]
217+ source_id : int = link ["sourceId" ]
218+
219+ target_node = nodes_dict [target_id ]
220+ source_node = nodes_dict [source_id ]
221+
222+ # plugins corresponding to the nodes
223+ target_plugin = plugins_dict [target_node ["pluginId" ]]
224+ source_plugin = plugins_dict [source_node ["pluginId" ]]
225+
226+ def is_inlet (binding : Json ) -> bool :
227+ """Check if a wfb input is an inlet (directory)"""
228+
229+ return (
230+ binding ['type' ] in ['directory' , 'file' , 'path' , 'collection' , 'csvCollection' ] or
231+ binding ['name' ].lower () == 'inpdir' or
232+ binding ['name' ].lower ().endswith ('path' ) or
233+ binding ['name' ].lower ().endswith ('dir' )
234+ )
235+
236+ # filter inputs by to only be inlets (directories)
237+ input_directories = [binding for binding in target_plugin ["inputs" ] if is_inlet (binding )]
238+ output_directories = [binding for binding in source_plugin ["outputs" ] if is_inlet (binding )]
239+
240+ missing_input_key = input_directories [link ["inletIndex" ]]["name" ]
241+ missing_output_key = output_directories [link ["outletIndex" ]]["name" ]
242+
243+ # add the missing input value to the node if needed
244+ target_node ["settings" ]["inputs" ][missing_input_key ] = source_node ["settings" ]["outputs" ][missing_output_key ]
245+
246+ if validate_schema_and_object (SCHEMA , wfb_data_copy ):
247+ print ('Updated object is valid against input object schema' )
248+
249+ return wfb_data_copy
0 commit comments