From 5e54c90b47a76fd649723c0874d9bc30709b0208 Mon Sep 17 00:00:00 2001 From: Dan Date: Fri, 15 Apr 2022 15:36:23 -0400 Subject: [PATCH] "Reset" should not mean "re-start". Calling the reset_workflow script will now set the workflow to an "unstarted" state with no outstanding tasks, no json-state structure stored. The workflow is not yet running. Also: * Assured that arguments are consistent (we always seem to use workflow_spec_id, so I made sure we use that consistently. * Don't require named parameters - so it's cool to call it like: reset_workflow('my_workflow_id') * Task Actions (ie create, assign, etc...) are now an enumeration in the models, and not static variables on Workflow Service, so we can reference them consistently from anywhere. * Removed some repetitive code * Always try to validate as much as possible in the scripts to save folks time debugging. * --- crc/api/workflow.py | 10 +-- crc/models/task_event.py | 9 +++ crc/scripts/delete_task_data.py | 4 +- crc/scripts/reset_workflow.py | 56 +++++++++------- crc/scripts/start_workflow.py | 67 ++++++------------- crc/services/workflow_processor.py | 24 +++++-- crc/services/workflow_service.py | 22 ++---- package-lock.json | 5 +- tests/base_test.py | 6 +- tests/data/reset_workflow/reset_workflow.bpmn | 34 +++++----- tests/data/start_workflow/start_workflow.bpmn | 4 +- tests/files/test_delete_task_data.py | 6 +- tests/scripts/test_start_workflow.py | 1 + tests/test_lookup_service.py | 6 +- tests/test_user_roles.py | 6 +- tests/workflow/test_workflow_processor.py | 3 +- tests/workflow/test_workflow_reset.py | 12 +++- tests/workflow/test_workflow_restart.py | 2 +- 18 files changed, 142 insertions(+), 135 deletions(-) diff --git a/crc/api/workflow.py b/crc/api/workflow.py index e7a4d1ca..0c94857f 100644 --- a/crc/api/workflow.py +++ b/crc/api/workflow.py @@ -6,7 +6,7 @@ from crc import session from crc.api.common import ApiError, ApiErrorSchema from crc.models.api_models import WorkflowApiSchema from crc.models.study import StudyModel, WorkflowMetadata, StudyStatus -from crc.models.task_event import TaskEventModel, TaskEvent, TaskEventSchema +from crc.models.task_event import TaskEventModel, TaskEvent, TaskEventSchema, TaskAction from crc.models.workflow import WorkflowModel, WorkflowSpecInfoSchema, WorkflowSpecCategorySchema from crc.services.error_service import ValidationErrorService from crc.services.lookup_service import LookupService @@ -205,8 +205,10 @@ def get_workflow(workflow_id, do_engine_steps=True): def restart_workflow(workflow_id, clear_data=False, delete_files=False): """Restart a workflow with the latest spec. Clear data allows user to restart the workflow without previous data.""" + # fixme: remove delete_files arg, clear_data is the only one respected. workflow_model: WorkflowModel = session.query(WorkflowModel).filter_by(id=workflow_id).first() - processor = WorkflowProcessor.reset(workflow_model, clear_data=clear_data, delete_files=delete_files) + WorkflowProcessor.reset(workflow_model, clear_data=clear_data) + processor = WorkflowProcessor(workflow_model) processor.do_engine_steps() processor.save() WorkflowService.update_task_assignments(processor) @@ -269,7 +271,7 @@ def set_current_task(workflow_id, task_id): spiff_task.reset_token({}, reset_data=True) # Don't try to copy the existing data back into this task. processor.save() - WorkflowService.log_task_action(user_uid, processor, spiff_task, WorkflowService.TASK_ACTION_TOKEN_RESET) + WorkflowService.log_task_action(user_uid, processor, spiff_task, TaskAction.TOKEN_RESET.value) WorkflowService.update_task_assignments(processor) workflow_api_model = WorkflowService.processor_to_workflow_api(processor, spiff_task) @@ -327,7 +329,7 @@ def __update_task(processor, task, data, user): # Log the action before doing the engine steps, as doing so could effect the state of the task # the workflow could wrap around in the ngine steps, and the task could jump from being completed to # another state. What we are logging here is the completion. - WorkflowService.log_task_action(user.uid, processor, task, WorkflowService.TASK_ACTION_COMPLETE) + WorkflowService.log_task_action(user.uid, processor, task, TaskAction.COMPLETE.value) processor.do_engine_steps() processor.save() diff --git a/crc/models/task_event.py b/crc/models/task_event.py index 103c6cb0..f8ce379a 100644 --- a/crc/models/task_event.py +++ b/crc/models/task_event.py @@ -1,3 +1,5 @@ +import enum + from marshmallow import INCLUDE, fields from marshmallow_sqlalchemy import SQLAlchemyAutoSchema @@ -7,6 +9,13 @@ from crc.models.workflow import WorkflowModel from crc.services.ldap_service import LdapService from sqlalchemy import func +class TaskAction(enum.Enum): + COMPLETE = "COMPLETE" + TOKEN_RESET = "TOKEN_RESET" + HARD_RESET = "HARD_RESET" + SOFT_RESET = "SOFT_RESET" + ASSIGNMENT = "ASSIGNMENT" # Whenever the lane changes between tasks we assign the task to specific user. + class TaskEventModel(db.Model): __tablename__ = 'task_event' diff --git a/crc/scripts/delete_task_data.py b/crc/scripts/delete_task_data.py index 7c7cbe60..83604662 100644 --- a/crc/scripts/delete_task_data.py +++ b/crc/scripts/delete_task_data.py @@ -2,7 +2,7 @@ from crc import session from crc.api.common import ApiError from crc.models.data_store import DataStoreModel from crc.models.file import FileModel -from crc.models.task_event import TaskEventModel +from crc.models.task_event import TaskEventModel, TaskAction from crc.scripts.script import Script from crc.services.document_service import DocumentService from crc.services.user_file_service import UserFileService @@ -36,7 +36,7 @@ class DeleteTaskData(Script): # delete task events session.query(TaskEventModel).filter(TaskEventModel.workflow_id == workflow_id).filter( TaskEventModel.study_id == study_id).filter(TaskEventModel.task_name == task_spec_name).filter_by( - action=WorkflowService.TASK_ACTION_COMPLETE).delete() + action=TaskAction.COMPLETE.value).delete() files_to_delete = session.query(FileModel). \ filter(FileModel.workflow_id == workflow_id). \ diff --git a/crc/scripts/reset_workflow.py b/crc/scripts/reset_workflow.py index cb3cc368..da15e125 100644 --- a/crc/scripts/reset_workflow.py +++ b/crc/scripts/reset_workflow.py @@ -9,34 +9,42 @@ from crc.services.workflow_spec_service import WorkflowSpecService class ResetWorkflow(Script): def get_description(self): - return """Reset a workflow. Run by master workflow. + return """Reset a workflow. Run by mas vftgv ter workflow. Designed for completed workflows where we need to force rerunning the workflow. I.e., a new PI""" + def get_spec(self, *args, **kwargs): + workflow_spec_id = None + if 'workflow_spec_id' in kwargs.keys(): + workflow_spec_id = kwargs['workflow_spec_id'] + elif len(args) > 0: + workflow_spec_id = args[0] + + if not workflow_spec_id: + raise ApiError(code='missing_workflow_id', + message='Reset workflow requires a workflow_spec_id') + + workflow_spec = WorkflowSpecService().get_spec(workflow_spec_id) + if not workflow_spec: + raise ApiError(code='missing_workflow_spec', + message=f'No workflow spec found with the \ + id: {workflow_spec_id}') + + return workflow_spec + def do_task_validate_only(self, task, study_id, workflow_id, *args, **kwargs): - return hasattr(kwargs, 'reset_id') + self.get_spec(*args, **kwargs) # Just assure we can find the workflow spec. def do_task(self, task, study_id, workflow_id, *args, **kwargs): - - if 'reset_id' in kwargs.keys(): - reset_id = kwargs['reset_id'] - workflow_spec = WorkflowSpecService().get_spec(reset_id) - 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: - 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. \ - workflow_spec_id: {workflow_spec.id} \ - study_id: {study_id}') - else: - raise ApiError(code='missing_workflow_spec', - message=f'No WorkflowSpecModel returned. \ - id: {workflow_id}') + if 'clear_data' in kwargs.keys(): + clear_data = bool(kwargs['clear_data']) else: - raise ApiError(code='missing_workflow_id', - message='Reset workflow requires a workflow id') + clear_data = False + + workflow_spec = self.get_spec(*args, **kwargs) + 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: + WorkflowProcessor.reset(workflow_model, clear_data=clear_data) diff --git a/crc/scripts/start_workflow.py b/crc/scripts/start_workflow.py index 69737fad..6dabeffa 100644 --- a/crc/scripts/start_workflow.py +++ b/crc/scripts/start_workflow.py @@ -9,67 +9,40 @@ from crc.services.workflow_service import WorkflowService class StartWorkflow(Script): - @staticmethod - def get_workflow(workflow_id): - workflow_model: WorkflowModel = session.query(WorkflowModel).filter_by(id=workflow_id).first() - processor = WorkflowProcessor(workflow_model) - - processor.do_engine_steps() - processor.save() - WorkflowService.update_task_assignments(processor) - - workflow_api_model = WorkflowService.processor_to_workflow_api(processor) - return WorkflowApiSchema().dump(workflow_api_model) - def get_description(self): return """Script to start a workflow programmatically. It requires a workflow_spec_id. It accepts the workflow_spec_id as a positional argument or with the keyword 'workflow_spec_id'""" - def do_task_validate_only(self, task, study_id, workflow_id, *args, **kwargs): + def get_workflow(self, study_id, *args, **kwargs): if len(args) == 1 or 'workflow_spec_id' in kwargs: if 'workflow_spec_id' in kwargs: workflow_spec_id = kwargs['workflow_spec_id'] else: workflow_spec_id = args[0] - - workflow_api = WorkflowApi(1234, - WorkflowStatus('user_input_required'), - 'next_task', - 'navigation', - workflow_spec_id, - 'total_tasks', - 'completed_tasks', - 'last_updated', - 'is_review', - 'title', - study_id) - return WorkflowApiSchema().dump(workflow_api) - else: raise ApiError(code='missing_parameter', message=f'The start_workflow script requires a workflow id') + workflow = session.query(WorkflowModel).\ + filter(WorkflowModel.study_id==study_id).\ + filter(WorkflowModel.workflow_spec_id==workflow_spec_id).\ + first() + + if not(workflow): + raise ApiError(code='unknown_workflow', + message=f"We could not find a workflow with workflow_spec_id '{workflow_spec_id}'.") + + return workflow + + def do_task_validate_only(self, task, study_id, workflow_id, *args, **kwargs): + self.get_workflow(study_id, *args, **kwargs) + def do_task(self, task, study_id, workflow_id, *args, **kwargs): - if len(args) == 1 or 'workflow_spec_id' in kwargs: - if 'workflow_spec_id' in kwargs: - workflow_spec_id = kwargs['workflow_spec_id'] - else: - workflow_spec_id = args[0] + workflow_model = self.get_workflow(study_id, *args, **kwargs) + processor = WorkflowProcessor(workflow_model) + processor.do_engine_steps() + processor.save() + WorkflowService.update_task_assignments(processor) - workflow = session.query(WorkflowModel).\ - filter(WorkflowModel.study_id==study_id).\ - filter(WorkflowModel.workflow_spec_id==workflow_spec_id).\ - first() - - if workflow: - workflow_api = self.get_workflow(workflow.id) - return workflow_api - else: - raise ApiError(code='unknown_workflow', - message=f"We could not find a workflow with workflow_spec_id '{workflow_spec_id}'.") - - else: - raise ApiError(code='missing_parameter', - message=f'The start_workflow script requires a workflow id') diff --git a/crc/services/workflow_processor.py b/crc/services/workflow_processor.py index 1a1e7e01..e4912c2e 100644 --- a/crc/services/workflow_processor.py +++ b/crc/services/workflow_processor.py @@ -20,7 +20,7 @@ from SpiffWorkflow.specs import WorkflowSpec from crc import session from crc.api.common import ApiError from crc.models.file import FileModel, FileType, File -from crc.models.task_event import TaskEventModel +from crc.models.task_event import TaskEventModel, TaskAction from crc.models.user import UserModelSchema from crc.models.workflow import WorkflowStatus, WorkflowModel, WorkflowSpecInfo from crc.scripts.script import Script @@ -171,7 +171,13 @@ class WorkflowProcessor(object): task.data['current_user'] = current_user_data @staticmethod - def reset(workflow_model, clear_data=False, delete_files=False): + def reset(workflow_model, clear_data=False): + """Resets the workflow back to an unstarted state - where nothing has + happened yet. If clear_data is set to false, then the information + previously used in forms will be re-populated when the form is re- + displayed, and any files that were updated will remain in place, otherwise + files will also be cleared out.""" + # Try to execute a cancel notify try: bpmn_workflow = WorkflowProcessor.__get_bpmn_workflow(workflow_model) @@ -182,19 +188,27 @@ class WorkflowProcessor(object): f" state. An %s error occured with the following information: %s" % (workflow_model.id, e.__class__.__name__, str(e))) workflow_model.bpmn_workflow_json = None + workflow_model.status = WorkflowStatus.not_started + + # clear out any task assignments + session.query(TaskEventModel). \ + filter(TaskEventModel.workflow_id == workflow_model.id). \ + filter(TaskEventModel.action == TaskAction.ASSIGNMENT.value).delete() + if clear_data: - # Clear form_data from task_events + # Clear out data in previous task events task_events = session.query(TaskEventModel). \ filter(TaskEventModel.workflow_id == workflow_model.id).all() for task_event in task_events: task_event.form_data = {} session.add(task_event) - if delete_files: + # Remove any uploaded files. files = FileModel.query.filter(FileModel.workflow_id == workflow_model.id).all() for file in files: UserFileService.delete_file(file.id) session.commit() - return WorkflowProcessor(workflow_model) + + @staticmethod def __get_bpmn_workflow(workflow_model: WorkflowModel, spec: WorkflowSpec = None, validate_only=False): diff --git a/crc/services/workflow_service.py b/crc/services/workflow_service.py index cebd163e..c703ed3b 100755 --- a/crc/services/workflow_service.py +++ b/crc/services/workflow_service.py @@ -27,7 +27,7 @@ from crc.models.api_models import Task, MultiInstanceType, WorkflowApi from crc.models.file import LookupDataModel, FileModel, File, FileSchema from crc.models.ldap import LdapModel from crc.models.study import StudyModel -from crc.models.task_event import TaskEventModel +from crc.models.task_event import TaskEventModel, TaskAction from crc.models.user import UserModel from crc.models.workflow import WorkflowModel, WorkflowStatus from crc.services.data_store_service import DataStoreBase @@ -45,11 +45,6 @@ from flask import g class WorkflowService(object): - TASK_ACTION_COMPLETE = "COMPLETE" - TASK_ACTION_TOKEN_RESET = "TOKEN_RESET" - TASK_ACTION_HARD_RESET = "HARD_RESET" - TASK_ACTION_SOFT_RESET = "SOFT_RESET" - TASK_ACTION_ASSIGNMENT = "ASSIGNMENT" # Whenever the lane changes between tasks we assign the task to specifc user. TASK_STATE_LOCKED = "LOCKED" # When the task belongs to a different user. @@ -723,7 +718,7 @@ class WorkflowService(object): query = db.session.query(TaskEventModel) \ .filter_by(workflow_id=workflow_id) \ .filter_by(task_name=spiff_task.task_spec.name) \ - .filter_by(action=WorkflowService.TASK_ACTION_COMPLETE) + .filter_by(action=TaskAction.COMPLETE.value) if hasattr(spiff_task, 'internal_data') and 'runtimes' in spiff_task.internal_data: query = query.filter_by(mi_index=spiff_task.internal_data['runtimes']) @@ -976,14 +971,14 @@ class WorkflowService(object): should be called whenever progress is made on a workflow.""" db.session.query(TaskEventModel). \ filter(TaskEventModel.workflow_id == processor.workflow_model.id). \ - filter(TaskEventModel.action == WorkflowService.TASK_ACTION_ASSIGNMENT).delete() + filter(TaskEventModel.action == TaskAction.ASSIGNMENT.value).delete() db.session.commit() tasks = processor.get_current_user_tasks() for task in tasks: user_ids = WorkflowService.get_users_assigned_to_task(processor, task) for user_id in user_ids: - WorkflowService.log_task_action(user_id, processor, task, WorkflowService.TASK_ACTION_ASSIGNMENT) + WorkflowService.log_task_action(user_id, processor, task, TaskAction.ASSIGNMENT.value) @staticmethod def get_users_assigned_to_task(processor, spiff_task) -> List[str]: @@ -1121,13 +1116,4 @@ class WorkflowService(object): db.session.commit() return workflow_model - @staticmethod - def delete_workflow_spec_task_events(spec_id): - session.query(TaskEventModel).filter(TaskEventModel.workflow_spec_id == spec_id).delete() - session.commit() - - @staticmethod - def delete_workflow_spec_workflow_models(spec_id): - for workflow in session.query(WorkflowModel).filter_by(workflow_spec_id=spec_id): - StudyService.delete_workflow(workflow.id) diff --git a/package-lock.json b/package-lock.json index 48e341a0..1638e11d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,3 +1,6 @@ { - "lockfileVersion": 1 + "name": "cr-connect-workflow", + "lockfileVersion": 2, + "requires": true, + "packages": {} } diff --git a/tests/base_test.py b/tests/base_test.py index 69cd276f..79e4069e 100644 --- a/tests/base_test.py +++ b/tests/base_test.py @@ -15,7 +15,7 @@ from flask import g from crc import app, db, session from crc.models.api_models import WorkflowApiSchema, MultiInstanceType from crc.models.file import FileModel, CONTENT_TYPES -from crc.models.task_event import TaskEventModel +from crc.models.task_event import TaskEventModel, TaskAction from crc.models.study import StudyModel, StudyStatus, ProgressStatus from crc.models.user import UserModel from crc.models.workflow import WorkflowSpecCategory @@ -368,7 +368,7 @@ class BaseTest(unittest.TestCase): task_events = session.query(TaskEventModel) \ .filter_by(workflow_id=workflow.id) \ .filter_by(task_id=task_id) \ - .filter_by(action=WorkflowService.TASK_ACTION_COMPLETE) \ + .filter_by(action=TaskAction.COMPLETE.value) \ .order_by(TaskEventModel.date.desc()).all() self.assertGreater(len(task_events), 0) event = task_events[0] @@ -377,7 +377,7 @@ class BaseTest(unittest.TestCase): self.assertEqual(user_uid, event.user_uid) self.assertEqual(workflow.id, event.workflow_id) self.assertEqual(workflow.workflow_spec_id, event.workflow_spec_id) - self.assertEqual(WorkflowService.TASK_ACTION_COMPLETE, event.action) + self.assertEqual(TaskAction.COMPLETE.value, event.action) self.assertEqual(task_in.id, task_id) self.assertEqual(task_in.name, event.task_name) self.assertEqual(task_in.title, event.task_title) diff --git a/tests/data/reset_workflow/reset_workflow.bpmn b/tests/data/reset_workflow/reset_workflow.bpmn index b199cf6c..63efaa72 100644 --- a/tests/data/reset_workflow/reset_workflow.bpmn +++ b/tests/data/reset_workflow/reset_workflow.bpmn @@ -1,5 +1,5 @@ - + 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. @@ -15,7 +15,7 @@ - + @@ -28,7 +28,7 @@ SequenceFlow_1q2ton3 SequenceFlow_0x127gc - value = reset_workflow(workflow_name=workflow_name) + value = reset_workflow(workflow_name) # Reset Workflow @@ -46,28 +46,28 @@ - - - - - - - - - - + + + + + + + + + + + + + + - - - - diff --git a/tests/data/start_workflow/start_workflow.bpmn b/tests/data/start_workflow/start_workflow.bpmn index c0b6ea17..af380b03 100644 --- a/tests/data/start_workflow/start_workflow.bpmn +++ b/tests/data/start_workflow/start_workflow.bpmn @@ -1,5 +1,5 @@ - + Flow_0ac3s7d @@ -14,7 +14,7 @@ - + diff --git a/tests/files/test_delete_task_data.py b/tests/files/test_delete_task_data.py index 282ea368..a6dcec41 100644 --- a/tests/files/test_delete_task_data.py +++ b/tests/files/test_delete_task_data.py @@ -4,7 +4,7 @@ from crc import session from crc.models.data_store import DataStoreModel from crc.models.file import FileModel -from crc.models.task_event import TaskEventModel +from crc.models.task_event import TaskEventModel, TaskAction from crc.services.workflow_service import WorkflowService from io import BytesIO @@ -100,7 +100,7 @@ class TestDeleteTaskData(BaseTest): # Make sure we have something in task_events task_events = session.query(TaskEventModel).\ filter(TaskEventModel.workflow_id == workflow.id).\ - filter(TaskEventModel.action == WorkflowService.TASK_ACTION_COMPLETE).all() + filter(TaskEventModel.action == TaskAction.COMPLETE.value).all() for task_event in task_events: self.assertNotEqual({}, task_event.form_data) @@ -117,7 +117,7 @@ class TestDeleteTaskData(BaseTest): files = session.query(FileModel).filter(FileModel.workflow_id == workflow.id).all() task_events = session.query(TaskEventModel).\ filter(TaskEventModel.workflow_id == workflow.id).\ - filter(TaskEventModel.action == WorkflowService.TASK_ACTION_COMPLETE).all() + filter(TaskEventModel.action == TaskAction.COMPLETE.value).all() self.assertEqual(0, len(data_stores)) self.assertEqual(0, len(data_stores_1)) diff --git a/tests/scripts/test_start_workflow.py b/tests/scripts/test_start_workflow.py index 4ca1ea09..18c0b9fb 100644 --- a/tests/scripts/test_start_workflow.py +++ b/tests/scripts/test_start_workflow.py @@ -22,6 +22,7 @@ class TestStartWorkflow(BaseTest): return workflow def test_start_workflow_validation(self): + random_wf = self.create_workflow('random_fact') # Assure we have a workflow to start. spec_model = self.load_test_spec('start_workflow') rv = self.app.get('/v1.0/workflow-specification/%s/validate' % spec_model.id, headers=self.logged_in_headers()) self.assertEqual([], rv.json) diff --git a/tests/test_lookup_service.py b/tests/test_lookup_service.py index 78440892..025b812f 100644 --- a/tests/test_lookup_service.py +++ b/tests/test_lookup_service.py @@ -63,7 +63,8 @@ class TestLookupService(BaseTest): file.close() # restart the workflow, so it can pick up the changes. - processor = WorkflowProcessor.reset(workflow) + WorkflowProcessor.reset(workflow) + processor = WorkflowProcessor(workflow) workflow = processor.workflow_model LookupService.lookup(workflow, "Task_Enum_Lookup", "sponsor", "sam", limit=10) @@ -100,7 +101,8 @@ class TestLookupService(BaseTest): results = LookupService.lookup(workflow, task.task_spec.name, "selectedItem", "", value="apples", limit=10) self.assertEqual(0, len(results), "We shouldn't find our fruits mixed in with our animals.") - processor = WorkflowProcessor.reset(workflow, clear_data=True) + WorkflowProcessor.reset(workflow, clear_data=True) + processor = WorkflowProcessor(workflow) processor.do_engine_steps() task = processor.get_ready_user_tasks()[0] task.data = {"type": "fruits"} diff --git a/tests/test_user_roles.py b/tests/test_user_roles.py index da24b787..c1e6ed42 100644 --- a/tests/test_user_roles.py +++ b/tests/test_user_roles.py @@ -6,7 +6,7 @@ from crc.models.api_models import NavigationItemSchema from crc.models.workflow import WorkflowStatus from crc import db from crc.api.common import ApiError -from crc.models.task_event import TaskEventModel, TaskEventSchema +from crc.models.task_event import TaskEventModel, TaskEventSchema, TaskAction from crc.services.workflow_service import WorkflowService @@ -82,7 +82,7 @@ class TestUserRoles(BaseTest): # the supervisor. task_logs = db.session.query(TaskEventModel). \ filter(TaskEventModel.user_uid == supervisor.uid). \ - filter(TaskEventModel.action == WorkflowService.TASK_ACTION_ASSIGNMENT).all() + filter(TaskEventModel.action == TaskAction.ASSIGNMENT.value).all() self.assertEqual(1, len(task_logs)) # A call to the /task endpoint as the supervisor user should return a list of @@ -213,7 +213,7 @@ class TestUserRoles(BaseTest): def get_assignment_task_events(self, uid): return db.session.query(TaskEventModel). \ filter(TaskEventModel.user_uid == uid). \ - filter(TaskEventModel.action == WorkflowService.TASK_ACTION_ASSIGNMENT).all() + filter(TaskEventModel.action == TaskAction.ASSIGNMENT.value).all() def test_workflow_reset_correctly_resets_the_task_events(self): diff --git a/tests/workflow/test_workflow_processor.py b/tests/workflow/test_workflow_processor.py index 28133eb7..af1ce425 100644 --- a/tests/workflow/test_workflow_processor.py +++ b/tests/workflow/test_workflow_processor.py @@ -270,7 +270,8 @@ class TestWorkflowProcessor(BaseTest): # self.assertFalse(processor2.is_latest_spec) # Still at version 1. # Do a hard reset, which should bring us back to the beginning, but retain the data. - processor2 = WorkflowProcessor.reset(processor2.workflow_model) + WorkflowProcessor.reset(processor2.workflow_model) + processor2 = WorkflowProcessor(processor2.workflow_model) processor3 = WorkflowProcessor(processor.workflow_model) processor3.do_engine_steps() self.assertEqual("Step 1", processor3.next_task().task_spec.description) diff --git a/tests/workflow/test_workflow_reset.py b/tests/workflow/test_workflow_reset.py index 01c923be..f81b9108 100644 --- a/tests/workflow/test_workflow_reset.py +++ b/tests/workflow/test_workflow_reset.py @@ -23,7 +23,7 @@ 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, reset_id='two_user_tasks') + ResetWorkflow().do_task(second_task, workflow.study_id, workflow.id, workflow_spec_id='two_user_tasks') workflow_api = self.get_workflow_api(workflow) task = workflow_api.next_task @@ -43,4 +43,12 @@ class TestWorkflowReset(BaseTest): first_task = workflow_api.next_task with self.assertRaises(ApiError): - ResetWorkflow().do_task(first_task, workflow.study_id, workflow.id, reset_id='bad_workflow_name') + ResetWorkflow().do_task(first_task, workflow.study_id, workflow.id, workflow_spec_id='bad_workflow_name') + + def test_workflow_reset_no_start(self): + """Sometimes we want to reset the workflow, but not start it up (don't do the engine steps etc...)""" + workflow = self.create_workflow('two_user_tasks') + workflow_api = self.get_workflow_api(workflow) + task = workflow_api.next_task + + ResetWorkflow().do_task(task, workflow.study_id, workflow.id, workflow_spec_id='two_user_tasks') diff --git a/tests/workflow/test_workflow_restart.py b/tests/workflow/test_workflow_restart.py index 307acd9b..79477f37 100644 --- a/tests/workflow/test_workflow_restart.py +++ b/tests/workflow/test_workflow_restart.py @@ -68,7 +68,7 @@ class TestWorkflowRestart(BaseTest): self.assertEqual(True, IsFileUploaded.do_task( IsFileUploaded, first_task, study_id, workflow.id, irb_code)) - workflow_api = self.restart_workflow_api(workflow_api, delete_files=True) + workflow_api = self.restart_workflow_api(workflow_api, clear_data=True) first_task = workflow_api.next_task # Assert we do not have the file