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

simplify incoming wfb and correct outgoing object syntax #261

Merged
merged 2 commits into from
Aug 19, 2024
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
33 changes: 31 additions & 2 deletions src/sophios/api/http/restapi.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from pathlib import Path
import argparse
import copy
import yaml


import uvicorn
from fastapi import FastAPI, Request, status
from fastapi.middleware.cors import CORSMiddleware
Expand All @@ -12,8 +14,8 @@
from sophios.utils_yaml import wic_loader
from sophios import utils_cwl
from sophios.cli import get_args
from sophios.wic_types import CompilerInfo, Json, Tool, Tools, StepId, YamlTree, Cwl
# from sophios.api.utils import converter
from sophios.wic_types import CompilerInfo, Json, Tool, Tools, StepId, YamlTree, Cwl, NodeData
from sophios.api.utils import converter
# from .auth.auth import authenticate


Expand Down Expand Up @@ -105,6 +107,8 @@ async def compile_wf(request: Request) -> Json:
print('---------- Compile Workflow! ---------')
# ========= PROCESS REQUEST OBJECT ==========
req: Json = await request.json()
# clean up and convert the incoming object
req = converter.raw_wfb_to_lean_wfb(req)
wkflw_name = "generic_workflow"
args = get_args(wkflw_name)

Expand Down Expand Up @@ -137,7 +141,32 @@ async def compile_wf(request: Request) -> Json:
print('---------- Run Workflow locally! ---------')
retval = run_workflow(compiler_info, args)

# ======== OUTPUT PROCESSING ================
# ========= PROCESS COMPILED OBJECT =========
sub_node_data: NodeData = compiler_info.rose.data
yaml_stem = sub_node_data.name
cwl_tree = sub_node_data.compiled_cwl
yaml_inputs = sub_node_data.workflow_inputs_file
cwl_tree_no_dd = remove_dot_dollar(cwl_tree)
yaml_inputs_no_dd = remove_dot_dollar(yaml_inputs)

# Convert the compiled yaml file to json for labshare Compute.
cwl_tree_run = copy.deepcopy(cwl_tree_no_dd)
# for step_key in cwl_tree['steps']:
# step_name_i = step_key
# step_name_i = step_name_i.replace('.yml', '_yml') # Due to calling remove_dot_dollar above
# # step_name = '__'.join(step_key.split('__')[3:]) # Remove prefix
# # Get step CWL from templates
# # run_val = next((tval['cwlScript']
# # for _, tval in ict_plugins.items() if step_name == tval['name']), None)
# # cwl_tree_run['steps'][step_name_i]['run'] = run_val

compute_workflow: Json = {}
compute_workflow = {
"name": yaml_stem,
"cwlJobInputs": yaml_inputs_no_dd,
**cwl_tree_run
}
compute_workflow["retval"] = str(retval)
return compute_workflow

Expand Down
Empty file.
232 changes: 232 additions & 0 deletions src/sophios/api/utils/converter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
import copy
from typing import Any, Dict, List

from jsonschema import Draft202012Validator

# from sophios import cli
from sophios.wic_types import Json

SCHEMA: Json = {
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"Link": {
"properties": {
"id": {
"type": "number"
},
"inletIndex": {
"type": "number"
},
"outletIndex": {
"type": "number"
},
"sourceId": {
"type": "number"
},
"targetId": {
"type": "number"
},
"x1": {
"type": "number"
},
"x2": {
"type": "number"
},
"y1": {
"type": "number"
},
"y2": {
"type": "number"
}
},
"type": "object",
"required": ["id", "sourceId", "targetId"]
},
"NodeSettings": {
"properties": {
"inputs": {
"additionalProperties": {
"$ref": "#/definitions/T"
},
"type": "object"
},
"outputs": {
"additionalProperties": {
"$ref": "#/definitions/T"
},
"type": "object"
}
},
"type": "object"
},
"NodeX": {
"properties": {
"expanded": {
"type": "boolean"
},
"height": {
"type": "number"
},
"id": {
"type": "number"
},
"internal": {
"type": "boolean"
},
"name": {
"type": "string"
},
"pluginId": {
"type": "string"
},
"settings": {
"$ref": "#/definitions/NodeSettings"
},
"width": {
"type": "number"
},
"x": {
"type": "number"
},
"y": {
"type": "number"
},
"z": {
"type": "number"
},
},
"type": "object",
"required": ["id", "name", "pluginId", "settings", "internal"]
},
"T": {
"type": "object"
}
},
"properties": {
"links": {
"items": {
"$ref": "#/definitions/Link"
},
"type": "array"
},
"nodes": {
"items": {
"$ref": "#/definitions/NodeX"
},
"type": "array"
},
"selection": {
"items": {
"type": "number"
},
"type": "array"
}
},
"type": "object",
"required": ["links", "nodes"]
}


def del_irrelevant_keys(ldict: List[Dict[Any, Any]], relevant_keys: List[Any]) -> None:
"""deletes irrelevant keys from every dict in the list of dicts"""
for elem in ldict:
ekeys = list(elem.keys())
for ek in ekeys:
if ek not in relevant_keys:
# delete the key if it exists
elem.pop(ek, None)


def validate_schema_and_object(schema: Json, jobj: Json) -> None:
"""Validate schema object"""
Draft202012Validator.check_schema(schema)
df2012 = Draft202012Validator(schema)
df2012.is_valid(jobj)


def raw_wfb_to_lean_wfb(inp: Json) -> Json:
"""drop all the unnecessary info from incoming wfb object"""
inp_restrict = copy.deepcopy(inp)
keys = list(inp.keys())
# To avoid deserialization
# required attributes from schema
prop_req = SCHEMA['required']
nodes_req = SCHEMA['definitions']['NodeX']['required']
links_req = SCHEMA['definitions']['Link']['required']
do_not_rem_nodes_prop = ['cwlScript']
do_not_rem_links_prop: list = []

for k in keys:
if k not in prop_req:
del inp_restrict[k]
elif k == 'links':
lems = inp_restrict[k]
rel_links_keys = links_req + do_not_rem_links_prop
del_irrelevant_keys(lems, rel_links_keys)
elif k == 'nodes':
nems = inp_restrict[k]
rel_nodes_keys = nodes_req + do_not_rem_nodes_prop
del_irrelevant_keys(nems, rel_nodes_keys)
else:
pass

validate_schema_and_object(SCHEMA, inp_restrict)
return inp_restrict


# def wfb_to_wic(request: Json) -> Json:
# """Convert the json object from http request object to a json object that can be used as input to wic compiler.

# Args:
# request (Json): json object from http request

# Returns:
# converted_json (Json): json object that can be used as input to wic compiler"""

# converted_steps: list[Any] = []

# for step in request['steps']:
# step_template = step['template']
# arguments = step['arguments']
# # Get the template name from the step template
# template_name = next((tval['name']
# for tname, tval in request['templates'].items() if step_template == tname), None)
# # template_name = None
# # for tname, tval in request['templates'].items():
# # if tname == step_template and tval['name']:
# # template_name = tval['name']
# # break
# # elif tname == step_template and not tval['name']:
# # break
# # else:
# # pass

# converted_step: Json = {}
# if template_name:
# converted_step[template_name] = {
# "in": {}
# }

# for key, value in arguments.items():
# # be aware of usage of periods in the values as delimiters, this may cause an issue when storing in MongoDB
# if value.startswith("steps."):
# parts = value.split('.')
# src_step_idx = int(parts[1][len("step"):])
# src_output_key = parts[3]

# # Get the src template name from the src step name
# src_template = next((step.get("template")
# for step in request['steps'] if step.get("name") == parts[1]), None)
# src_template_name = next((stval['name'] for stname, stval in request['templates'].items()
# if src_template == stname), None)
# src_converted_step = next((step.get(src_template_name)
# for step in converted_steps if step.get(src_template_name)), None)
# if src_converted_step:
# src_converted_step["in"][src_output_key] = f"&{src_template_name}.{src_output_key}.{src_step_idx}"
# converted_step[template_name]["in"][key] = f"*{src_template_name}.{src_output_key}.{src_step_idx}"
# else:
# converted_step[template_name]["in"][key] = value
# converted_steps.append(converted_step)

# converted_json: Json = {"steps": converted_steps}
# return converted_json
Loading