From c8f8888c172ecbc258c3dbbb681180a4553201ff Mon Sep 17 00:00:00 2001 From: mike cullerton Date: Mon, 14 Jun 2021 14:51:16 -0400 Subject: [PATCH 1/6] Script to reset workflow. Requires workflow spec name. --- crc/scripts/reset_workflow.py | 46 +++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 crc/scripts/reset_workflow.py diff --git a/crc/scripts/reset_workflow.py b/crc/scripts/reset_workflow.py new file mode 100644 index 00000000..ddd22651 --- /dev/null +++ b/crc/scripts/reset_workflow.py @@ -0,0 +1,46 @@ +from crc import session +from crc.api.common import ApiError +from crc.models.workflow import WorkflowModel, WorkflowSpecModel +from crc.scripts.script import Script +from crc.services.workflow_processor import WorkflowProcessor + + +class ResetWorkflow(Script): + + def get_description(self): + return """Reset a workflow. Run by master workflow. + Designed for completed workflows where we need to force rerunning the workflow. + I.e., a new PI""" + + def do_task_validate_only(self, task, study_id, workflow_id, *args, **kwargs): + return hasattr(kwargs, 'workflow_name') + + def do_task(self, task, study_id, workflow_id, *args, **kwargs): + + if 'workflow_name' in kwargs.keys(): + workflow_name = kwargs['workflow_name'] + workflow_spec: WorkflowSpecModel = session.query(WorkflowSpecModel).filter_by(name=workflow_name).first() + if workflow_spec: + workflow_model: WorkflowModel = session.query(WorkflowModel).filter_by( + workflow_spec_id=workflow_spec.id, + study_id=study_id).first() + if workflow_model: + try: + workflow_processor = WorkflowProcessor.reset(workflow_model, clear_data=False, delete_files=False) + except Exception as e: + raise ApiError(code='unknown_error', + message=f'An unknown error occurred: {e}') + else: + return True + else: + raise ApiError(code='missing_workflow_model', + message=f'No WorkflowModel returned. \ + workflow_spec_id: {workflow_spec.id} \ + study_id: {study_id}') + else: + raise ApiError(code='missing_workflow_spec', + message=f'No WorkflowSpecModel returned. \ + name: {workflow_name}') + else: + raise ApiError(code='missing_workflow_name', + message='Reset workflow requires a workflow name') From 9e20025f33705fcfde977655a3c8a826552ff790 Mon Sep 17 00:00:00 2001 From: mike cullerton Date: Tue, 15 Jun 2021 08:37:42 -0400 Subject: [PATCH 2/6] Test and workflow for reset_workflow --- tests/data/reset_workflow/reset_workflow.bpmn | 82 +++++++++++++++++++ tests/workflow/test_workflow_reset.py | 22 +++++ 2 files changed, 104 insertions(+) create mode 100644 tests/data/reset_workflow/reset_workflow.bpmn create mode 100644 tests/workflow/test_workflow_reset.py diff --git a/tests/data/reset_workflow/reset_workflow.bpmn b/tests/data/reset_workflow/reset_workflow.bpmn new file mode 100644 index 00000000..0a2fe9a9 --- /dev/null +++ b/tests/data/reset_workflow/reset_workflow.bpmn @@ -0,0 +1,82 @@ + + + + + SequenceFlow_1oykjju + + + + + + + + + + + + + + + SequenceFlow_1oykjju + SequenceFlow_0z8c3ob + + + + + + + + + + + + SequenceFlow_0z8c3ob + SequenceFlow_1jfrd7w + + + # Data +{{name}} is {{age}} years old. + SequenceFlow_1jfrd7w + SequenceFlow_0yjk26l + + + SequenceFlow_0yjk26l + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/workflow/test_workflow_reset.py b/tests/workflow/test_workflow_reset.py new file mode 100644 index 00000000..3bb5174c --- /dev/null +++ b/tests/workflow/test_workflow_reset.py @@ -0,0 +1,22 @@ +from tests.base_test import BaseTest +from crc.scripts.reset_workflow import ResetWorkflow + + +class TestWorkflowReset(BaseTest): + + def test_workflow_reset(self): + workflow = self.create_workflow('reset_workflow') + workflow_api = self.get_workflow_api(workflow) + first_task = workflow_api.next_task + self.assertEqual('Task_GetName', first_task.name) + + self.complete_form(workflow, first_task, {'name': 'Mona'}) + workflow_api = self.get_workflow_api(workflow) + second_task = workflow_api.next_task + self.assertEqual('Task_GetAge', second_task.name) + + ResetWorkflow().do_task(second_task, workflow.study_id, workflow.id, workflow_name='reset_workflow') + + workflow_api = self.get_workflow_api(workflow) + task = workflow_api.next_task + self.assertEqual('Task_GetName', task.name) From addf1cab5b32f640baffa1c4db1d34197d0d7deb Mon Sep 17 00:00:00 2001 From: mike cullerton Date: Tue, 15 Jun 2021 10:30:18 -0400 Subject: [PATCH 3/6] Added tests for failing conditions --- tests/workflow/test_workflow_reset.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/workflow/test_workflow_reset.py b/tests/workflow/test_workflow_reset.py index 3bb5174c..929301a2 100644 --- a/tests/workflow/test_workflow_reset.py +++ b/tests/workflow/test_workflow_reset.py @@ -1,5 +1,6 @@ from tests.base_test import BaseTest from crc.scripts.reset_workflow import ResetWorkflow +from crc.api.common import ApiError class TestWorkflowReset(BaseTest): @@ -20,3 +21,19 @@ class TestWorkflowReset(BaseTest): workflow_api = self.get_workflow_api(workflow) task = workflow_api.next_task self.assertEqual('Task_GetName', task.name) + + def test_workflow_reset_missing_name(self): + workflow = self.create_workflow('reset_workflow') + workflow_api = self.get_workflow_api(workflow) + first_task = workflow_api.next_task + + with self.assertRaises(ApiError): + ResetWorkflow().do_task(first_task, workflow.study_id, workflow.id) + + def test_workflow_reset_bad_name(self): + workflow = self.create_workflow('reset_workflow') + workflow_api = self.get_workflow_api(workflow) + first_task = workflow_api.next_task + + with self.assertRaises(ApiError): + ResetWorkflow().do_task(first_task, workflow.study_id, workflow.id, workflow_name='bad_workflow_name') From 02949dc6e27bb8463f56e112df9da9f9051684a8 Mon Sep 17 00:00:00 2001 From: mike cullerton Date: Tue, 15 Jun 2021 11:12:50 -0400 Subject: [PATCH 4/6] WorkflowProcessor handles this error --- crc/scripts/reset_workflow.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/crc/scripts/reset_workflow.py b/crc/scripts/reset_workflow.py index ddd22651..2b3275ec 100644 --- a/crc/scripts/reset_workflow.py +++ b/crc/scripts/reset_workflow.py @@ -25,13 +25,8 @@ class ResetWorkflow(Script): workflow_spec_id=workflow_spec.id, study_id=study_id).first() if workflow_model: - try: - workflow_processor = WorkflowProcessor.reset(workflow_model, clear_data=False, delete_files=False) - except Exception as e: - raise ApiError(code='unknown_error', - message=f'An unknown error occurred: {e}') - else: - return True + workflow_processor = WorkflowProcessor.reset(workflow_model, clear_data=False, delete_files=False) + return workflow_processor else: raise ApiError(code='missing_workflow_model', message=f'No WorkflowModel returned. \ From 8db4199d73c0a0525e87e8512fad4222eadec274 Mon Sep 17 00:00:00 2001 From: mike cullerton Date: Tue, 15 Jun 2021 11:15:04 -0400 Subject: [PATCH 5/6] Separate workflows for `using` the script and `validating` the script. --- tests/data/reset_workflow/reset_workflow.bpmn | 112 +++++++++--------- tests/data/two_user_tasks/two_user_tasks.bpmn | 82 +++++++++++++ 2 files changed, 138 insertions(+), 56 deletions(-) create mode 100644 tests/data/two_user_tasks/two_user_tasks.bpmn diff --git a/tests/data/reset_workflow/reset_workflow.bpmn b/tests/data/reset_workflow/reset_workflow.bpmn index 0a2fe9a9..1f2eaccf 100644 --- a/tests/data/reset_workflow/reset_workflow.bpmn +++ b/tests/data/reset_workflow/reset_workflow.bpmn @@ -1,82 +1,82 @@ - - + + + Use this process to reset a workflow for the current study. You must enter the name of the workflow. I.e., lower case with underscores. - SequenceFlow_1oykjju + SequenceFlow_0i872g2 - - - - - - - - - - - - - - SequenceFlow_1oykjju - SequenceFlow_0z8c3ob - - - - - - - - - - - - SequenceFlow_0z8c3ob - SequenceFlow_1jfrd7w - - - # Data -{{name}} is {{age}} years old. - SequenceFlow_1jfrd7w - SequenceFlow_0yjk26l - - - SequenceFlow_0yjk26l + + + + + SequenceFlow_0yy50p2 - + + + + + + + + + + + + SequenceFlow_0i872g2 + SequenceFlow_1q2ton3 + + + SequenceFlow_1q2ton3 + SequenceFlow_0x127gc + value = reset_workflow(workflow_name=workflow_name) + + + # Reset Workflow +<div> +{% if value %} +<span>Workflow {{workflow_name}} was reset.</span> +{% else %} +<span>There was a problem resetting workflow {{workflow_name}}.</span> +{% endif %} +</div> + + SequenceFlow_0x127gc + SequenceFlow_0yy50p2 + - + - + - + - + - - - - - - - - - - + - + + + + + + + + + + diff --git a/tests/data/two_user_tasks/two_user_tasks.bpmn b/tests/data/two_user_tasks/two_user_tasks.bpmn new file mode 100644 index 00000000..0a2fe9a9 --- /dev/null +++ b/tests/data/two_user_tasks/two_user_tasks.bpmn @@ -0,0 +1,82 @@ + + + + + SequenceFlow_1oykjju + + + + + + + + + + + + + + + SequenceFlow_1oykjju + SequenceFlow_0z8c3ob + + + + + + + + + + + + SequenceFlow_0z8c3ob + SequenceFlow_1jfrd7w + + + # Data +{{name}} is {{age}} years old. + SequenceFlow_1jfrd7w + SequenceFlow_0yjk26l + + + SequenceFlow_0yjk26l + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 94e730d04eaf616e2a361cd7fa1e26ae0503ef7d Mon Sep 17 00:00:00 2001 From: mike cullerton Date: Tue, 15 Jun 2021 11:15:31 -0400 Subject: [PATCH 6/6] Test script validation --- tests/workflow/test_workflow_reset.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/tests/workflow/test_workflow_reset.py b/tests/workflow/test_workflow_reset.py index 929301a2..d8b5bef3 100644 --- a/tests/workflow/test_workflow_reset.py +++ b/tests/workflow/test_workflow_reset.py @@ -5,8 +5,14 @@ from crc.api.common import ApiError class TestWorkflowReset(BaseTest): + def test_workflow_reset_validation(self): + self.load_example_data() + spec_model = self.load_test_spec('reset_workflow') + rv = self.app.get('/v1.0/workflow-specification/%s/validate' % spec_model.id, headers=self.logged_in_headers()) + self.assertEqual([], rv.json) + def test_workflow_reset(self): - workflow = self.create_workflow('reset_workflow') + workflow = self.create_workflow('two_user_tasks') workflow_api = self.get_workflow_api(workflow) first_task = workflow_api.next_task self.assertEqual('Task_GetName', first_task.name) @@ -16,14 +22,14 @@ class TestWorkflowReset(BaseTest): second_task = workflow_api.next_task self.assertEqual('Task_GetAge', second_task.name) - ResetWorkflow().do_task(second_task, workflow.study_id, workflow.id, workflow_name='reset_workflow') + ResetWorkflow().do_task(second_task, workflow.study_id, workflow.id, workflow_name='two_user_tasks') workflow_api = self.get_workflow_api(workflow) task = workflow_api.next_task self.assertEqual('Task_GetName', task.name) def test_workflow_reset_missing_name(self): - workflow = self.create_workflow('reset_workflow') + workflow = self.create_workflow('two_user_tasks') workflow_api = self.get_workflow_api(workflow) first_task = workflow_api.next_task @@ -31,7 +37,7 @@ class TestWorkflowReset(BaseTest): ResetWorkflow().do_task(first_task, workflow.study_id, workflow.id) def test_workflow_reset_bad_name(self): - workflow = self.create_workflow('reset_workflow') + workflow = self.create_workflow('two_user_tasks') workflow_api = self.get_workflow_api(workflow) first_task = workflow_api.next_task