From 5e4db55518fecfcf3bfbaffecb9e31cbeb9926b9 Mon Sep 17 00:00:00 2001 From: Brad Macdonald Date: Fri, 28 Apr 2023 10:46:56 -0600 Subject: [PATCH 1/9] adds `get_workflow` and `get_workflow_status` methods to ops --- src/orca/services/nextflowtower/ops.py | 28 ++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/orca/services/nextflowtower/ops.py b/src/orca/services/nextflowtower/ops.py index 1ab51db..4bdc36b 100644 --- a/src/orca/services/nextflowtower/ops.py +++ b/src/orca/services/nextflowtower/ops.py @@ -41,3 +41,31 @@ def workspace(self) -> str: message = f"Config ({self.config}) does not specify a workspace." raise ConfigError(message) return self.config.workspace + + def get_workflow(self, workflow_id: str) -> dict: + """Gets available information about a workflow run + + Attributes: + workflow_id (str): The ID number for a workflow run to get information about + + Returns: + response (dict): Dictionary containing information about the workflow run + """ + path = f"/workflow/{workflow_id}" + response = self.client.get(path=path, params={"workspaceId": self.workspace_id}) + return response + + def get_workflow_status(self, workflow_id: str) -> tuple: + """Gets status of workflow run + + Args: + workflow_id (str): The ID number for a workflow run to get information about + + Returns: + tuple: Tuple containing 1. status (str) and 2. Whether the workflow is done (boolean) + """ + response = self.get_workflow(workflow_id=workflow_id) + return ( + response["workflow"]["status"], + bool(response["workflow"]["complete"]), + ) From 564c3bf404d3236a6e6cbd204fe3e427a2802ee9 Mon Sep 17 00:00:00 2001 From: Brad Macdonald Date: Fri, 28 Apr 2023 14:53:43 -0600 Subject: [PATCH 2/9] adds tests for `ger_workflow` and `get_workflow_status` --- tests/services/nextflowtower/conftest.py | 11 +- tests/services/nextflowtower/responses.py | 170 ++++++++++++++++++++++ tests/services/nextflowtower/test_ops.py | 27 ++++ 3 files changed, 205 insertions(+), 3 deletions(-) diff --git a/tests/services/nextflowtower/conftest.py b/tests/services/nextflowtower/conftest.py index 7387051..6f36d38 100644 --- a/tests/services/nextflowtower/conftest.py +++ b/tests/services/nextflowtower/conftest.py @@ -23,9 +23,14 @@ def config(patch_os_environ): @pytest.fixture -def ops(config, client, mocker): - mock = mocker.patch.object(NextflowTowerOps, "client") - mock.return_value = client +def ops(config): + yield NextflowTowerOps(config) + + +@pytest.fixture +def mocked_ops(config, client, mocker): + mocker.patch.object(NextflowTowerOps, "client", return_value=client) + mocker.patch.object(NextflowTowerOps, "workspace_id", return_value=98765) yield NextflowTowerOps(config) diff --git a/tests/services/nextflowtower/responses.py b/tests/services/nextflowtower/responses.py index 475d4f6..bbfb285 100644 --- a/tests/services/nextflowtower/responses.py +++ b/tests/services/nextflowtower/responses.py @@ -63,3 +63,173 @@ }, ] } + +get_workflow_complete = { + "workflow": { + "id": "123456789", + "submit": "2023-04-28T16:22:31Z", + "start": "2023-04-28T16:30:44Z", + "complete": "2023-04-28T16:30:54Z", + "dateCreated": "2023-04-28T16:22:31Z", + "lastUpdated": "2023-04-28T16:30:54Z", + "runName": "example-run", + "sessionId": "abc-abc-abc-abc-abc", + "profile": "standard", + "workDir": "s3://example-bucket/work", + "commitId": "123", + "userName": "example-user", + "scriptId": "123", + "revision": None, + "commandLine": "nextflow run nextflow-io/example-workflow -name example-run -with-tower 'https://tower.sagebionetworks.org/api' -r 123 -resume abc-abc-abc-abc-abc", + "projectName": "nextflow-io/example-workflow", + "scriptName": "main.nf", + "launchId": "abc", + "status": "SUCCEEDED", + "configFiles": [ + "/.nextflow/assets/nextflow-io/example-workflow/nextflow.config", + "/nextflow.config", + ], + "params": {}, + "configText": "example-config", + "manifest": { + "nextflowVersion": None, + "defaultBranch": "master", + "version": None, + "homePage": None, + "gitmodules": None, + "description": None, + "name": None, + "mainScript": "main.nf", + "author": None, + }, + "nextflow": { + "version": "22.10.6", + "build": "5843", + "timestamp": "2023-01-23T23:20:00Z", + }, + "stats": { + "computeTimeFmt": "(a few seconds)", + "cachedCount": 4, + "failedCount": 0, + "ignoredCount": 0, + "succeedCount": 0, + "cachedCountFmt": "4", + "succeedCountFmt": "0", + "failedCountFmt": "0", + "ignoredCountFmt": "0", + "cachedPct": 100.0, + "failedPct": 0.0, + "succeedPct": 0.0, + "ignoredPct": 0.0, + "cachedDuration": 0, + "failedDuration": 0, + "succeedDuration": 0, + }, + "errorMessage": None, + "errorReport": None, + "deleted": None, + "peakLoadCpus": None, + "peakLoadTasks": None, + "peakLoadMemory": None, + "projectDir": "/.nextflow/assets/nextflow-io/example-workflow", + "homeDir": "/root", + "container": "quay.io/nextflow/bash", + "repository": "https://github.com/nextflow-io/example-workflow", + "containerEngine": None, + "scriptFile": "/.nextflow/assets/nextflow-io/example-workflow/main.nf", + "launchDir": "/", + "duration": 10508, + "exitStatus": 0, + "resume": True, + "success": True, + "logFile": None, + "outFile": None, + "operationId": None, + "ownerId": 28, + } +} + +get_workflow_incomplete = { + "workflow": { + "id": "123456789", + "submit": "2023-04-28T16:22:31Z", + "start": None, + "complete": None, + "dateCreated": "2023-04-28T16:22:31Z", + "lastUpdated": "2023-04-28T16:30:54Z", + "runName": "example-run", + "sessionId": "abc-abc-abc-abc-abc", + "profile": "standard", + "workDir": "s3://example-bucket/work", + "commitId": "123", + "userName": "example-user", + "scriptId": "123", + "revision": None, + "commandLine": "nextflow run nextflow-io/example-workflow -name example-run -with-tower 'https://tower.sagebionetworks.org/api' -r 123 -resume abc-abc-abc-abc-abc", + "projectName": "nextflow-io/example-workflow", + "scriptName": "main.nf", + "launchId": "abc", + "status": "SUBMITTED", + "configFiles": [ + "/.nextflow/assets/nextflow-io/example-workflow/nextflow.config", + "/nextflow.config", + ], + "params": {}, + "configText": "example-config", + "manifest": { + "nextflowVersion": None, + "defaultBranch": "master", + "version": None, + "homePage": None, + "gitmodules": None, + "description": None, + "name": None, + "mainScript": "main.nf", + "author": None, + }, + "nextflow": { + "version": "22.10.6", + "build": "5843", + "timestamp": "2023-01-23T23:20:00Z", + }, + "stats": { + "computeTimeFmt": "(a few seconds)", + "cachedCount": 4, + "failedCount": 0, + "ignoredCount": 0, + "succeedCount": 0, + "cachedCountFmt": "4", + "succeedCountFmt": "0", + "failedCountFmt": "0", + "ignoredCountFmt": "0", + "cachedPct": 100.0, + "failedPct": 0.0, + "succeedPct": 0.0, + "ignoredPct": 0.0, + "cachedDuration": 0, + "failedDuration": 0, + "succeedDuration": 0, + }, + "errorMessage": None, + "errorReport": None, + "deleted": None, + "peakLoadCpus": None, + "peakLoadTasks": None, + "peakLoadMemory": None, + "projectDir": "/.nextflow/assets/nextflow-io/example-workflow", + "homeDir": "/root", + "container": "quay.io/nextflow/bash", + "repository": "https://github.com/nextflow-io/example-workflow", + "containerEngine": None, + "scriptFile": "/.nextflow/assets/nextflow-io/example-workflow/main.nf", + "launchDir": "/", + "duration": 10508, + "exitStatus": 0, + "resume": True, + "success": True, + "logFile": None, + "outFile": None, + "operationId": None, + "ownerId": 28, + } +} diff --git a/tests/services/nextflowtower/test_ops.py b/tests/services/nextflowtower/test_ops.py index 8f7be74..2d7f2e4 100644 --- a/tests/services/nextflowtower/test_ops.py +++ b/tests/services/nextflowtower/test_ops.py @@ -1,4 +1,5 @@ import pytest +from unittest.mock import patch from orca.errors import ConfigError from orca.services.nextflowtower import NextflowTowerConfig, NextflowTowerOps @@ -33,3 +34,29 @@ def test_for_error_when_the_workspace_id_does_not_exist(ops, mocker, get_respons mock.list_user_workspaces.return_value = items_filtered with pytest.raises(ValueError): ops.workspace_id + + +def test_that_get_workflow_returns_expected_response(mocked_ops, ops, get_response): + response = get_response("get_workflow_complete") + mocked_ops.client.get.return_value = response + assert ops.get_workflow(workflow_id="123456789") == response + + +def test_that_get_workflow_status_returns_expected_tuple_workflow_is_complete( + ops, mocker, get_response +): + response = get_response("get_workflow_complete") + mock = mocker.patch.object(ops, "get_workflow") + mock.return_value = response + result = ops.get_workflow_status(workflow_id="123456789") + assert result == ("SUCCEEDED", True) + + +def test_that_get_workflow_status_returns_expected_tuple_workflow_is_not_complete( + ops, mocker, get_response +): + response = get_response("get_workflow_incomplete") + mock = mocker.patch.object(ops, "get_workflow") + mock.return_value = response + result = ops.get_workflow_status(workflow_id="123456789") + assert result == ("SUBMITTED", False) From a869fe1ad1ec0a2d7a6cd266ff2d6881cb85ed4c Mon Sep 17 00:00:00 2001 From: Brad Macdonald Date: Fri, 28 Apr 2023 15:00:21 -0600 Subject: [PATCH 3/9] fix pre-commit errors --- src/orca/services/nextflowtower/ops.py | 3 ++- tests/services/nextflowtower/responses.py | 14 ++++++++++---- tests/services/nextflowtower/test_ops.py | 1 - 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/orca/services/nextflowtower/ops.py b/src/orca/services/nextflowtower/ops.py index 4bdc36b..73fbc08 100644 --- a/src/orca/services/nextflowtower/ops.py +++ b/src/orca/services/nextflowtower/ops.py @@ -62,7 +62,8 @@ def get_workflow_status(self, workflow_id: str) -> tuple: workflow_id (str): The ID number for a workflow run to get information about Returns: - tuple: Tuple containing 1. status (str) and 2. Whether the workflow is done (boolean) + tuple: Tuple containing 1. status (str) and + 2. Whether the workflow is done (boolean) """ response = self.get_workflow(workflow_id=workflow_id) return ( diff --git a/tests/services/nextflowtower/responses.py b/tests/services/nextflowtower/responses.py index bbfb285..0884d30 100644 --- a/tests/services/nextflowtower/responses.py +++ b/tests/services/nextflowtower/responses.py @@ -64,7 +64,7 @@ ] } -get_workflow_complete = { +get_workflow_complete: dict = { "workflow": { "id": "123456789", "submit": "2023-04-28T16:22:31Z", @@ -80,7 +80,10 @@ "userName": "example-user", "scriptId": "123", "revision": None, - "commandLine": "nextflow run nextflow-io/example-workflow -name example-run -with-tower 'https://tower.sagebionetworks.org/api' -r 123 -resume abc-abc-abc-abc-abc", + "commandLine": "nextflow run nextflow-io/example-workflow\ + -name example-run -with-tower\ + 'https://tower.sagebionetworks.org/api'\ + -r 123 -resume abc-abc-abc-abc-abc", "projectName": "nextflow-io/example-workflow", "scriptName": "main.nf", "launchId": "abc", @@ -149,7 +152,7 @@ } } -get_workflow_incomplete = { +get_workflow_incomplete: dict = { "workflow": { "id": "123456789", "submit": "2023-04-28T16:22:31Z", @@ -165,7 +168,10 @@ "userName": "example-user", "scriptId": "123", "revision": None, - "commandLine": "nextflow run nextflow-io/example-workflow -name example-run -with-tower 'https://tower.sagebionetworks.org/api' -r 123 -resume abc-abc-abc-abc-abc", + "commandLine": "nextflow run nextflow-io/example-workflow\ + -name example-run -with-tower\ + 'https://tower.sagebionetworks.org/api'\ + -r 123 -resume abc-abc-abc-abc-abc", "projectName": "nextflow-io/example-workflow", "scriptName": "main.nf", "launchId": "abc", diff --git a/tests/services/nextflowtower/test_ops.py b/tests/services/nextflowtower/test_ops.py index 2d7f2e4..6670e66 100644 --- a/tests/services/nextflowtower/test_ops.py +++ b/tests/services/nextflowtower/test_ops.py @@ -1,5 +1,4 @@ import pytest -from unittest.mock import patch from orca.errors import ConfigError from orca.services.nextflowtower import NextflowTowerConfig, NextflowTowerOps From 567a35705ed73d084847aa43b7740879287e4cb9 Mon Sep 17 00:00:00 2001 From: Brad Macdonald Date: Mon, 1 May 2023 10:18:12 -0600 Subject: [PATCH 4/9] moves get_workflow to client, refactors ops --- src/orca/services/nextflowtower/client.py | 14 ++++++++++++++ src/orca/services/nextflowtower/ops.py | 20 +++++--------------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/orca/services/nextflowtower/client.py b/src/orca/services/nextflowtower/client.py index 7a7937f..5eece97 100644 --- a/src/orca/services/nextflowtower/client.py +++ b/src/orca/services/nextflowtower/client.py @@ -184,3 +184,17 @@ def list_user_workspaces(self) -> list[dict[str, Any]]: continue workspaces.append(workspace) return workspaces + + def get_workflow(self, workspace_id: int, workflow_id: str) -> dict: + """Gets available information about a workflow run + + Attributes: + workspace_id (int): The ID number of the workspace the workflow exists within. + workflow_id (str): The ID number for a workflow run to get information about. + + Returns: + response (dict): Dictionary containing information about the workflow run + """ + path = f"/workflow/{workflow_id}" + response = self.get(path=path, params={"workspaceId": workspace_id}) + return response diff --git a/src/orca/services/nextflowtower/ops.py b/src/orca/services/nextflowtower/ops.py index 73fbc08..1cac7ee 100644 --- a/src/orca/services/nextflowtower/ops.py +++ b/src/orca/services/nextflowtower/ops.py @@ -42,19 +42,6 @@ def workspace(self) -> str: raise ConfigError(message) return self.config.workspace - def get_workflow(self, workflow_id: str) -> dict: - """Gets available information about a workflow run - - Attributes: - workflow_id (str): The ID number for a workflow run to get information about - - Returns: - response (dict): Dictionary containing information about the workflow run - """ - path = f"/workflow/{workflow_id}" - response = self.client.get(path=path, params={"workspaceId": self.workspace_id}) - return response - def get_workflow_status(self, workflow_id: str) -> tuple: """Gets status of workflow run @@ -65,8 +52,11 @@ def get_workflow_status(self, workflow_id: str) -> tuple: tuple: Tuple containing 1. status (str) and 2. Whether the workflow is done (boolean) """ - response = self.get_workflow(workflow_id=workflow_id) + response = self.client.get_workflow( + workspace_id=self.workspace_id, workflow_id=workflow_id + ) + # TODO consider switching return value to a namedtuple return ( response["workflow"]["status"], - bool(response["workflow"]["complete"]), + response["workflow"]["complete"] is not None, ) From 3b809f82f863649f8fabe02b27e88fba1748189b Mon Sep 17 00:00:00 2001 From: Brad Macdonald Date: Mon, 1 May 2023 11:13:52 -0600 Subject: [PATCH 5/9] updates tests --- tests/services/nextflowtower/responses.py | 90 +-------------------- tests/services/nextflowtower/test_client.py | 9 +++ tests/services/nextflowtower/test_ops.py | 30 ++++--- 3 files changed, 24 insertions(+), 105 deletions(-) diff --git a/tests/services/nextflowtower/responses.py b/tests/services/nextflowtower/responses.py index 0884d30..5ca73ee 100644 --- a/tests/services/nextflowtower/responses.py +++ b/tests/services/nextflowtower/responses.py @@ -64,7 +64,7 @@ ] } -get_workflow_complete: dict = { +get_workflow: dict = { "workflow": { "id": "123456789", "submit": "2023-04-28T16:22:31Z", @@ -151,91 +151,3 @@ "ownerId": 28, } } - -get_workflow_incomplete: dict = { - "workflow": { - "id": "123456789", - "submit": "2023-04-28T16:22:31Z", - "start": None, - "complete": None, - "dateCreated": "2023-04-28T16:22:31Z", - "lastUpdated": "2023-04-28T16:30:54Z", - "runName": "example-run", - "sessionId": "abc-abc-abc-abc-abc", - "profile": "standard", - "workDir": "s3://example-bucket/work", - "commitId": "123", - "userName": "example-user", - "scriptId": "123", - "revision": None, - "commandLine": "nextflow run nextflow-io/example-workflow\ - -name example-run -with-tower\ - 'https://tower.sagebionetworks.org/api'\ - -r 123 -resume abc-abc-abc-abc-abc", - "projectName": "nextflow-io/example-workflow", - "scriptName": "main.nf", - "launchId": "abc", - "status": "SUBMITTED", - "configFiles": [ - "/.nextflow/assets/nextflow-io/example-workflow/nextflow.config", - "/nextflow.config", - ], - "params": {}, - "configText": "example-config", - "manifest": { - "nextflowVersion": None, - "defaultBranch": "master", - "version": None, - "homePage": None, - "gitmodules": None, - "description": None, - "name": None, - "mainScript": "main.nf", - "author": None, - }, - "nextflow": { - "version": "22.10.6", - "build": "5843", - "timestamp": "2023-01-23T23:20:00Z", - }, - "stats": { - "computeTimeFmt": "(a few seconds)", - "cachedCount": 4, - "failedCount": 0, - "ignoredCount": 0, - "succeedCount": 0, - "cachedCountFmt": "4", - "succeedCountFmt": "0", - "failedCountFmt": "0", - "ignoredCountFmt": "0", - "cachedPct": 100.0, - "failedPct": 0.0, - "succeedPct": 0.0, - "ignoredPct": 0.0, - "cachedDuration": 0, - "failedDuration": 0, - "succeedDuration": 0, - }, - "errorMessage": None, - "errorReport": None, - "deleted": None, - "peakLoadCpus": None, - "peakLoadTasks": None, - "peakLoadMemory": None, - "projectDir": "/.nextflow/assets/nextflow-io/example-workflow", - "homeDir": "/root", - "container": "quay.io/nextflow/bash", - "repository": "https://github.com/nextflow-io/example-workflow", - "containerEngine": None, - "scriptFile": "/.nextflow/assets/nextflow-io/example-workflow/main.nf", - "launchDir": "/", - "duration": 10508, - "exitStatus": 0, - "resume": True, - "success": True, - "logFile": None, - "outFile": None, - "operationId": None, - "ownerId": 28, - } -} diff --git a/tests/services/nextflowtower/test_client.py b/tests/services/nextflowtower/test_client.py index 200f54e..e5aa73a 100644 --- a/tests/services/nextflowtower/test_client.py +++ b/tests/services/nextflowtower/test_client.py @@ -61,3 +61,12 @@ def test_that_list_user_workspaces_fails_with_nonstandard_response(client, mocke mock.return_value = {"message": "foobar"} with pytest.raises(HTTPError): client.list_user_workspaces() + + +def test_that_get_workflow_returns_expected_response(client, mocker, get_response): + expected = get_response("get_workflow") + mock = mocker.patch.object(client, "get") + mock.return_value = expected + actual = client.get_workflow(workspace_id=98765, workflow_id="123456789") + mock.assert_called_once() + assert actual == expected diff --git a/tests/services/nextflowtower/test_ops.py b/tests/services/nextflowtower/test_ops.py index 6670e66..9eba021 100644 --- a/tests/services/nextflowtower/test_ops.py +++ b/tests/services/nextflowtower/test_ops.py @@ -35,27 +35,25 @@ def test_for_error_when_the_workspace_id_does_not_exist(ops, mocker, get_respons ops.workspace_id -def test_that_get_workflow_returns_expected_response(mocked_ops, ops, get_response): - response = get_response("get_workflow_complete") - mocked_ops.client.get.return_value = response - assert ops.get_workflow(workflow_id="123456789") == response - - def test_that_get_workflow_status_returns_expected_tuple_workflow_is_complete( - ops, mocker, get_response + mocker, get_response, mocked_ops ): - response = get_response("get_workflow_complete") - mock = mocker.patch.object(ops, "get_workflow") - mock.return_value = response - result = ops.get_workflow_status(workflow_id="123456789") + response = get_response("get_workflow") + mock = mocker.patch.object(mocked_ops, "client") + mock.get_workflow.return_value = response + result = mocked_ops.get_workflow_status(workflow_id="123456789") + mock.get_workflow.assert_called_once() assert result == ("SUCCEEDED", True) def test_that_get_workflow_status_returns_expected_tuple_workflow_is_not_complete( - ops, mocker, get_response + mocked_ops, mocker, get_response ): - response = get_response("get_workflow_incomplete") - mock = mocker.patch.object(ops, "get_workflow") - mock.return_value = response - result = ops.get_workflow_status(workflow_id="123456789") + response = get_response("get_workflow") + response["workflow"]["complete"] = None + response["workflow"]["status"] = "SUBMITTED" + mock = mocker.patch.object(mocked_ops, "client") + mock.get_workflow.return_value = response + result = mocked_ops.get_workflow_status(workflow_id="123456789") + mock.get_workflow.assert_called_once() assert result == ("SUBMITTED", False) From 4e4541b8e8f47edc634cf6d4d668fe338f03bfc8 Mon Sep 17 00:00:00 2001 From: Brad Macdonald Date: Mon, 1 May 2023 11:20:46 -0600 Subject: [PATCH 6/9] adjust line-lengths for pre-commit --- src/orca/services/nextflowtower/client.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/orca/services/nextflowtower/client.py b/src/orca/services/nextflowtower/client.py index 5eece97..1aa59f5 100644 --- a/src/orca/services/nextflowtower/client.py +++ b/src/orca/services/nextflowtower/client.py @@ -189,8 +189,10 @@ def get_workflow(self, workspace_id: int, workflow_id: str) -> dict: """Gets available information about a workflow run Attributes: - workspace_id (int): The ID number of the workspace the workflow exists within. - workflow_id (str): The ID number for a workflow run to get information about. + workspace_id (int): The ID number of the workspace the workflow + exists within. + workflow_id (str): The ID number for a workflow run to get + information about. Returns: response (dict): Dictionary containing information about the workflow run From 0f1146a74ce659cb25b9a8599bb47d9d3e0990a4 Mon Sep 17 00:00:00 2001 From: Brad Macdonald Date: Mon, 1 May 2023 11:31:32 -0600 Subject: [PATCH 7/9] incoperates TaskStatus enum for nextflowtower module --- src/orca/services/nextflowtower/models/enums.py | 9 +++++++++ src/orca/services/nextflowtower/ops.py | 9 +++++---- 2 files changed, 14 insertions(+), 4 deletions(-) create mode 100644 src/orca/services/nextflowtower/models/enums.py diff --git a/src/orca/services/nextflowtower/models/enums.py b/src/orca/services/nextflowtower/models/enums.py new file mode 100644 index 0000000..a02dc48 --- /dev/null +++ b/src/orca/services/nextflowtower/models/enums.py @@ -0,0 +1,9 @@ +class TaskStatus: + SUBMITTED = "SUBMITTED" + RUNNING = "RUNNING" + SUCCEEDED = "SUCCEEDED" + FAILED = "FAILED" + CANCELLED = "CANCELLED" + UNKNOWN = "UNKNOWN" + + terminal_states = [SUCCEEDED, FAILED, CANCELLED, UNKNOWN] diff --git a/src/orca/services/nextflowtower/ops.py b/src/orca/services/nextflowtower/ops.py index 1cac7ee..57b70e2 100644 --- a/src/orca/services/nextflowtower/ops.py +++ b/src/orca/services/nextflowtower/ops.py @@ -1,4 +1,5 @@ from functools import cached_property +from typing import cast from pydantic.dataclasses import dataclass @@ -6,6 +7,7 @@ from orca.services.base.ops import BaseOps from orca.services.nextflowtower.client_factory import NextflowTowerClientFactory from orca.services.nextflowtower.config import NextflowTowerConfig +from orca.services.nextflowtower.models.enums import TaskStatus @dataclass(kw_only=False) @@ -55,8 +57,7 @@ def get_workflow_status(self, workflow_id: str) -> tuple: response = self.client.get_workflow( workspace_id=self.workspace_id, workflow_id=workflow_id ) + task_status = cast(TaskStatus, response["workflow"]["status"]) + is_done = task_status in TaskStatus.terminal_states # TODO consider switching return value to a namedtuple - return ( - response["workflow"]["status"], - response["workflow"]["complete"] is not None, - ) + return task_status, is_done From f393ce7a23b0cf2555735e8c8181b08e054478ac Mon Sep 17 00:00:00 2001 From: Brad Macdonald Date: Mon, 1 May 2023 11:46:46 -0600 Subject: [PATCH 8/9] implements test for enum --- src/orca/services/nextflowtower/models/enums.py | 6 ++++++ tests/services/nextflowtower/test_enums.py | 17 +++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 tests/services/nextflowtower/test_enums.py diff --git a/src/orca/services/nextflowtower/models/enums.py b/src/orca/services/nextflowtower/models/enums.py index a02dc48..f2e598a 100644 --- a/src/orca/services/nextflowtower/models/enums.py +++ b/src/orca/services/nextflowtower/models/enums.py @@ -1,4 +1,10 @@ class TaskStatus: + """enum containing all possible status values for + Nextflow Tower runs. terminal_states set which + statuses result in a run being determined to be + "complete" + """ + SUBMITTED = "SUBMITTED" RUNNING = "RUNNING" SUCCEEDED = "SUCCEEDED" diff --git a/tests/services/nextflowtower/test_enums.py b/tests/services/nextflowtower/test_enums.py new file mode 100644 index 0000000..55699d2 --- /dev/null +++ b/tests/services/nextflowtower/test_enums.py @@ -0,0 +1,17 @@ +from orca.services.nextflowtower.models.enums import TaskStatus + + +def test_that_TaskStatus_contant_values_are_correct(): + assert TaskStatus.SUBMITTED == "SUBMITTED" + assert TaskStatus.RUNNING == "RUNNING" + assert TaskStatus.SUCCEEDED == "SUCCEEDED" + assert TaskStatus.FAILED == "FAILED" + assert TaskStatus.CANCELLED == "CANCELLED" + assert TaskStatus.UNKNOWN == "UNKNOWN" + + +def test_that_TaskStatus_terminal_states_are_in_terminal_states_list(): + assert TaskStatus.SUCCEEDED in TaskStatus.terminal_states + assert TaskStatus.FAILED in TaskStatus.terminal_states + assert TaskStatus.CANCELLED in TaskStatus.terminal_states + assert TaskStatus.UNKNOWN in TaskStatus.terminal_states From 0385b89140a40035672558d164911c7d3bcca533 Mon Sep 17 00:00:00 2001 From: Brad Macdonald Date: Mon, 1 May 2023 13:23:02 -0600 Subject: [PATCH 9/9] utilizes enum base class --- .../services/nextflowtower/models/enums.py | 5 ++++- src/orca/services/nextflowtower/ops.py | 4 ++-- tests/services/nextflowtower/test_enums.py | 20 +++++++++---------- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/orca/services/nextflowtower/models/enums.py b/src/orca/services/nextflowtower/models/enums.py index f2e598a..3165ed2 100644 --- a/src/orca/services/nextflowtower/models/enums.py +++ b/src/orca/services/nextflowtower/models/enums.py @@ -1,4 +1,7 @@ -class TaskStatus: +from enum import Enum + + +class TaskStatus(Enum): """enum containing all possible status values for Nextflow Tower runs. terminal_states set which statuses result in a run being determined to be diff --git a/src/orca/services/nextflowtower/ops.py b/src/orca/services/nextflowtower/ops.py index 57b70e2..3b1fb56 100644 --- a/src/orca/services/nextflowtower/ops.py +++ b/src/orca/services/nextflowtower/ops.py @@ -44,7 +44,7 @@ def workspace(self) -> str: raise ConfigError(message) return self.config.workspace - def get_workflow_status(self, workflow_id: str) -> tuple: + def get_workflow_status(self, workflow_id: str) -> tuple[TaskStatus, bool]: """Gets status of workflow run Args: @@ -58,6 +58,6 @@ def get_workflow_status(self, workflow_id: str) -> tuple: workspace_id=self.workspace_id, workflow_id=workflow_id ) task_status = cast(TaskStatus, response["workflow"]["status"]) - is_done = task_status in TaskStatus.terminal_states + is_done = task_status in TaskStatus.terminal_states.value # TODO consider switching return value to a namedtuple return task_status, is_done diff --git a/tests/services/nextflowtower/test_enums.py b/tests/services/nextflowtower/test_enums.py index 55699d2..b556fa8 100644 --- a/tests/services/nextflowtower/test_enums.py +++ b/tests/services/nextflowtower/test_enums.py @@ -2,16 +2,16 @@ def test_that_TaskStatus_contant_values_are_correct(): - assert TaskStatus.SUBMITTED == "SUBMITTED" - assert TaskStatus.RUNNING == "RUNNING" - assert TaskStatus.SUCCEEDED == "SUCCEEDED" - assert TaskStatus.FAILED == "FAILED" - assert TaskStatus.CANCELLED == "CANCELLED" - assert TaskStatus.UNKNOWN == "UNKNOWN" + assert TaskStatus.SUBMITTED.value == "SUBMITTED" + assert TaskStatus.RUNNING.value == "RUNNING" + assert TaskStatus.SUCCEEDED.value == "SUCCEEDED" + assert TaskStatus.FAILED.value == "FAILED" + assert TaskStatus.CANCELLED.value == "CANCELLED" + assert TaskStatus.UNKNOWN.value == "UNKNOWN" def test_that_TaskStatus_terminal_states_are_in_terminal_states_list(): - assert TaskStatus.SUCCEEDED in TaskStatus.terminal_states - assert TaskStatus.FAILED in TaskStatus.terminal_states - assert TaskStatus.CANCELLED in TaskStatus.terminal_states - assert TaskStatus.UNKNOWN in TaskStatus.terminal_states + assert TaskStatus.SUCCEEDED.value in TaskStatus.terminal_states.value + assert TaskStatus.FAILED.value in TaskStatus.terminal_states.value + assert TaskStatus.CANCELLED.value in TaskStatus.terminal_states.value + assert TaskStatus.UNKNOWN.value in TaskStatus.terminal_states.value