Skip to content

Commit a3e8285

Browse files
Merge pull request #29 from InformaticsMatters/sc-3475
Initial instance directory changes
2 parents 769b026 + 8b23139 commit a3e8285

File tree

7 files changed

+318
-36
lines changed

7 files changed

+318
-36
lines changed

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ warn_unused_ignores = true
5151

5252
[tool.pylint]
5353
disable = [
54+
"fixme",
5455
"R",
5556
"too-few-public-methods",
5657
"too-many-arguments",

tests/test_decoder.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,3 +255,77 @@ def test_set_variables_from_options_for_step_for_simnple_python_molprops_with_op
255255
assert "value" in new_variables
256256
assert new_variables["name"] == "propertyName"
257257
assert new_variables["value"] == "propertyValue"
258+
259+
260+
def test_get_workflow_inputs_for_step_with_name_step1():
261+
# Arrange
262+
263+
# Act
264+
inputs = decoder.get_workflow_input_names_for_step(
265+
_SIMPLE_PYTHON_MOLPROPS_WITH_OPTIONS_WORKFLOW, "step1"
266+
)
267+
268+
# Assert
269+
assert len(inputs) == 1
270+
assert "candidateMolecules" in inputs
271+
272+
273+
def test_get_workflow_inputs_for_step_with_name_step2():
274+
# Arrange
275+
276+
# Act
277+
inputs = decoder.get_workflow_input_names_for_step(
278+
_SIMPLE_PYTHON_MOLPROPS_WITH_OPTIONS_WORKFLOW, "step2"
279+
)
280+
281+
# Assert
282+
assert not inputs
283+
284+
285+
def test_get_workflow_inputs_for_step_with_unkown_step_name():
286+
# Arrange
287+
288+
# Act
289+
inputs = decoder.get_workflow_input_names_for_step(
290+
_SIMPLE_PYTHON_MOLPROPS_WITH_OPTIONS_WORKFLOW, "unknown"
291+
)
292+
293+
# Assert
294+
assert not inputs
295+
296+
297+
def test_get_workflow_outputs_for_step_with_name_step1():
298+
# Arrange
299+
300+
# Act
301+
outputs = decoder.get_workflow_output_values_for_step(
302+
_SIMPLE_PYTHON_MOLPROPS_WITH_OPTIONS_WORKFLOW, "step1"
303+
)
304+
305+
# Assert
306+
assert not outputs
307+
308+
309+
def test_get_workflow_outputs_for_step_with_name_step2():
310+
# Arrange
311+
312+
# Act
313+
outputs = decoder.get_workflow_output_values_for_step(
314+
_SIMPLE_PYTHON_MOLPROPS_WITH_OPTIONS_WORKFLOW, "step2"
315+
)
316+
317+
# Assert
318+
assert len(outputs) == 1
319+
assert "clustered-molecules.smi" in outputs
320+
321+
322+
def test_get_workflow_outputs_for_step_with_unkown_step_name():
323+
# Arrange
324+
325+
# Act
326+
outputs = decoder.get_workflow_output_values_for_step(
327+
_SIMPLE_PYTHON_MOLPROPS_WITH_OPTIONS_WORKFLOW, "unknown"
328+
)
329+
330+
# Assert
331+
assert not outputs

tests/test_test_api_adapter.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,3 +409,49 @@ def test_get_workflow_steps_driving_this_step_when_2nd_step():
409409
assert response["steps"][0]["name"] == "step-1"
410410
assert response["steps"][1]["name"] == "step-2"
411411
assert response["steps"][2]["name"] == "step-3"
412+
413+
414+
def test_get_running_workflow_step_by_name():
415+
# Arrange
416+
utaa = UnitTestWorkflowAPIAdapter()
417+
response = utaa.create_workflow(
418+
workflow_definition={
419+
"name": "blah",
420+
"steps": [{"name": "step-1"}, {"name": "step-2"}, {"name": "step-3"}],
421+
}
422+
)
423+
response = utaa.create_running_workflow(
424+
user_id="dlister",
425+
workflow_id=response["id"],
426+
project_id=TEST_PROJECT_ID,
427+
variables={},
428+
)
429+
rwf_id = response["id"]
430+
response, _ = utaa.create_running_workflow_step(
431+
running_workflow_id=rwf_id, step="step-2"
432+
)
433+
rwfs_id = response["id"]
434+
435+
# Act
436+
response, _ = utaa.get_running_workflow_step_by_name(
437+
name="step-2", running_workflow_id=rwf_id
438+
)
439+
440+
# Assert
441+
assert response["running_workflow"]["id"] == rwf_id
442+
assert response["name"] == "step-2"
443+
assert response["id"] == rwfs_id
444+
445+
446+
def test_basic_realise():
447+
# Arrange
448+
utaa = UnitTestWorkflowAPIAdapter()
449+
450+
# Act
451+
response, _ = utaa.realise_outputs(
452+
running_workflow_step_id="r-workflow-step-00000000-0000-0000-0000-000000000001",
453+
outputs=["a.txt"],
454+
)
455+
456+
# Assert
457+
assert not response

tests/wapi_adapter.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,25 @@ def get_running_workflow_step(
178178
response["id"] = running_workflow_step_id
179179
return response, 0
180180

181+
def get_running_workflow_step_by_name(
182+
self, *, name: str, running_workflow_id: str
183+
) -> dict[str, Any]:
184+
UnitTestWorkflowAPIAdapter.lock.acquire()
185+
with open(_RUNNING_WORKFLOW_STEP_PICKLE_FILE, "rb") as pickle_file:
186+
running_workflow_step = Unpickler(pickle_file).load()
187+
UnitTestWorkflowAPIAdapter.lock.release()
188+
189+
print(f"name={name} running_workflow_id={running_workflow_id}")
190+
for rwfs_id, record in running_workflow_step.items():
191+
print(f"rwfs_id={rwfs_id} record={record}")
192+
if record["running_workflow"]["id"] != running_workflow_id:
193+
continue
194+
if record["name"] == name:
195+
response = record
196+
response["id"] = rwfs_id
197+
return response, 0
198+
return {}, 0
199+
181200
def set_running_workflow_step_variables(
182201
self,
183202
*,
@@ -362,8 +381,8 @@ def get_running_workflow_steps(self, *, running_workflow_id: str) -> dict[str, A
362381
return {"count": len(steps), "running_workflow_steps": steps}
363382

364383
def realise_outputs(
365-
self, *, running_workflow_step_id: str, variables: dict[str, str]
384+
self, *, running_workflow_step_id: str, outputs: list[str, str]
366385
) -> tuple[dict[str, Any], int]:
367386
del running_workflow_step_id
368-
del variables
387+
del outputs
369388
return {}, 0

workflow/decoder.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,47 @@ def get_variable_names(definition: dict[str, Any]) -> list[str]:
8282
return wf_variable_names
8383

8484

85+
def get_workflow_input_names_for_step(
86+
definition: dict[str, Any], name: str
87+
) -> list[str]:
88+
"""Given a Workflow definition and a step name we return a list of workflow
89+
input variable names the step expects. To do this we iterate through the step's
90+
inputs to find those that are declared 'from->workflow-input'.
91+
92+
To get the input (a filename) the caller simply looks these names up
93+
in the variable map."""
94+
inputs: list[str] = []
95+
for step in definition.get("steps", {}):
96+
if step["name"] == name and "inputs" in step:
97+
# Find all the workflow inputs.
98+
# This gives us the name of the workflow input variable
99+
# and the name of the step input (Job) variable.
100+
inputs.extend(
101+
step_input["from"]["workflow-input"]
102+
for step_input in step["inputs"]
103+
if "from" in step_input and "workflow-input" in step_input["from"]
104+
)
105+
return inputs
106+
107+
108+
def get_workflow_output_values_for_step(
109+
definition: dict[str, Any], name: str
110+
) -> list[str]:
111+
"""Given a Workflow definition and a step name we return a list of workflow
112+
out variable names the step creates. To do this we iterate through the workflows's
113+
outputs to find those that are declared 'from' our step."""
114+
wf_outputs = definition.get("variable-mapping", {}).get("outputs", {})
115+
outputs: list[str] = []
116+
outputs.extend(
117+
output["as"]
118+
for output in wf_outputs
119+
if "from" in output
120+
and "step" in output["from"]
121+
and output["from"]["step"] == name
122+
)
123+
return outputs
124+
125+
85126
def set_variables_from_options_for_step(
86127
definition: dict[str, Any], variables: dict[str, Any], step_name: str
87128
) -> dict[str, Any]:

workflow/workflow_abc.py

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,37 @@ def get_running_workflow_step(
242242
# "id": "r-workflow-step-00000000-0000-0000-0000-000000000001",
243243
# },
244244

245+
@abstractmethod
246+
def get_running_workflow_step_by_name(
247+
self, *, name: str, running_workflow_id: str
248+
) -> tuple[dict[str, Any], int]:
249+
"""Get a RunningWorkflowStep Record given a step name
250+
(and its RUnningWorkflow ID)"""
251+
# Should return:
252+
# {
253+
# "id": "r-workflow-step-00000000-0000-0000-0000-000000000001",
254+
# "name:": "step-1234",
255+
# "done": False,
256+
# "success": false,
257+
# "error_num": 0,
258+
# "error_msg": "",
259+
# "variables": {
260+
# "x": 1,
261+
# "y": 2,
262+
# },
263+
# "running_workflow": {
264+
# "id": "r-workflow-00000000-0000-0000-0000-000000000001",
265+
# },
266+
# }
267+
# If not present an empty dictionary should be returned.
268+
#
269+
# For steps that are not the first in a workflow the following field
270+
# can be expected in the response: -
271+
#
272+
# "prior_running_workflow_step": {
273+
# "id": "r-workflow-step-00000000-0000-0000-0000-000000000001",
274+
# },
275+
245276
@abstractmethod
246277
def set_running_workflow_step_variables(
247278
self,
@@ -312,12 +343,11 @@ def get_job(
312343

313344
@abstractmethod
314345
def realise_outputs(
315-
self, *, running_workflow_step_id: str, outputs: list[tuple[str, str]]
346+
self, *, running_workflow_step_id: str, outputs: list[str]
316347
) -> tuple[dict[str, Any], int]:
317-
"""Copy (link) the step's files as outputs into the Project directory,
318-
while also renaming the file. A step ID is provided, along with a list of
319-
outputs (files in the instance directory) and the required counterpart file
320-
in the Project directory."""
348+
"""Copy (link) the step's files as outputs into the Project directory.
349+
A step ID is provided, along with a list of outputs
350+
(files that will be in the step's instance directory)."""
321351
# Should return an empty map or:
322352
# {
323353
# "error": "<error message>",

0 commit comments

Comments
 (0)