Merge pull request #296 from sartography/launch-workflow-outside-study-204
Launch workflow outside study 204
This commit is contained in:
commit
77d9bfca43
34
crc/api.yml
34
crc/api.yml
|
@ -425,7 +425,7 @@ paths:
|
||||||
- name: spec_id
|
- name: spec_id
|
||||||
in: path
|
in: path
|
||||||
required: true
|
required: true
|
||||||
description: The unique id of an existing workflow specification to modify.
|
description: The unique id of an existing workflow specification.
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
get:
|
get:
|
||||||
|
@ -440,6 +440,18 @@ paths:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/WorkflowSpec"
|
$ref: "#/components/schemas/WorkflowSpec"
|
||||||
|
post:
|
||||||
|
operationId: crc.api.workflow.get_workflow_from_spec
|
||||||
|
summary: Creates a workflow from a workflow spec and returns the workflow
|
||||||
|
tags:
|
||||||
|
- Workflow Specifications
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Workflow generated successfully
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/Workflow"
|
||||||
put:
|
put:
|
||||||
operationId: crc.api.workflow.update_workflow_specification
|
operationId: crc.api.workflow.update_workflow_specification
|
||||||
security:
|
security:
|
||||||
|
@ -469,6 +481,21 @@ paths:
|
||||||
responses:
|
responses:
|
||||||
'204':
|
'204':
|
||||||
description: The workflow specification has been removed.
|
description: The workflow specification has been removed.
|
||||||
|
/workflow-specification/standalone:
|
||||||
|
get:
|
||||||
|
operationId: crc.api.workflow.standalone_workflow_specs
|
||||||
|
summary: Provides a list of workflow specifications that can be run outside a study.
|
||||||
|
tags:
|
||||||
|
- Workflow Specifications
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: A list of workflow specifications
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: "#/components/schemas/WorkflowSpec"
|
||||||
/workflow-specification/{spec_id}/validate:
|
/workflow-specification/{spec_id}/validate:
|
||||||
parameters:
|
parameters:
|
||||||
- name: spec_id
|
- name: spec_id
|
||||||
|
@ -1536,6 +1563,9 @@ components:
|
||||||
category_id:
|
category_id:
|
||||||
type: integer
|
type: integer
|
||||||
nullable: true
|
nullable: true
|
||||||
|
standalone:
|
||||||
|
type: boolean
|
||||||
|
example: false
|
||||||
workflow_spec_category:
|
workflow_spec_category:
|
||||||
$ref: "#/components/schemas/WorkflowSpecCategory"
|
$ref: "#/components/schemas/WorkflowSpecCategory"
|
||||||
is_status:
|
is_status:
|
||||||
|
@ -1608,6 +1638,8 @@ components:
|
||||||
type: integer
|
type: integer
|
||||||
num_tasks_incomplete:
|
num_tasks_incomplete:
|
||||||
type: integer
|
type: integer
|
||||||
|
study_id:
|
||||||
|
type: integer
|
||||||
|
|
||||||
example:
|
example:
|
||||||
id: 291234
|
id: 291234
|
||||||
|
|
|
@ -101,6 +101,24 @@ def delete_workflow_specification(spec_id):
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
|
|
||||||
|
def get_workflow_from_spec(spec_id):
|
||||||
|
workflow_model = WorkflowService.get_workflow_from_spec(spec_id, g.user)
|
||||||
|
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 standalone_workflow_specs():
|
||||||
|
schema = WorkflowSpecModelSchema(many=True)
|
||||||
|
specs = WorkflowService.get_standalone_workflow_specs()
|
||||||
|
return schema.dump(specs)
|
||||||
|
|
||||||
|
|
||||||
def get_workflow(workflow_id, do_engine_steps=True):
|
def get_workflow(workflow_id, do_engine_steps=True):
|
||||||
"""Retrieve workflow based on workflow_id, and return it in the last saved State.
|
"""Retrieve workflow based on workflow_id, and return it in the last saved State.
|
||||||
If do_engine_steps is False, return the workflow without running any engine tasks or logging any events. """
|
If do_engine_steps is False, return the workflow without running any engine tasks or logging any events. """
|
||||||
|
@ -185,9 +203,6 @@ def update_task(workflow_id, task_id, body, terminate_loop=None, update_all=Fals
|
||||||
if workflow_model is None:
|
if workflow_model is None:
|
||||||
raise ApiError("invalid_workflow_id", "The given workflow id is not valid.", status_code=404)
|
raise ApiError("invalid_workflow_id", "The given workflow id is not valid.", status_code=404)
|
||||||
|
|
||||||
elif workflow_model.study is None:
|
|
||||||
raise ApiError("invalid_study", "There is no study associated with the given workflow.", status_code=404)
|
|
||||||
|
|
||||||
processor = WorkflowProcessor(workflow_model)
|
processor = WorkflowProcessor(workflow_model)
|
||||||
task_id = uuid.UUID(task_id)
|
task_id = uuid.UUID(task_id)
|
||||||
spiff_task = processor.bpmn_workflow.get_task(task_id)
|
spiff_task = processor.bpmn_workflow.get_task(task_id)
|
||||||
|
|
|
@ -191,7 +191,7 @@ class DocumentDirectory(object):
|
||||||
class WorkflowApi(object):
|
class WorkflowApi(object):
|
||||||
def __init__(self, id, status, next_task, navigation,
|
def __init__(self, id, status, next_task, navigation,
|
||||||
spec_version, is_latest_spec, workflow_spec_id, total_tasks, completed_tasks,
|
spec_version, is_latest_spec, workflow_spec_id, total_tasks, completed_tasks,
|
||||||
last_updated, is_review, title):
|
last_updated, is_review, title, study_id):
|
||||||
self.id = id
|
self.id = id
|
||||||
self.status = status
|
self.status = status
|
||||||
self.next_task = next_task # The next task that requires user input.
|
self.next_task = next_task # The next task that requires user input.
|
||||||
|
@ -204,13 +204,14 @@ class WorkflowApi(object):
|
||||||
self.last_updated = last_updated
|
self.last_updated = last_updated
|
||||||
self.title = title
|
self.title = title
|
||||||
self.is_review = is_review
|
self.is_review = is_review
|
||||||
|
self.study_id = study_id or ''
|
||||||
|
|
||||||
class WorkflowApiSchema(ma.Schema):
|
class WorkflowApiSchema(ma.Schema):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = WorkflowApi
|
model = WorkflowApi
|
||||||
fields = ["id", "status", "next_task", "navigation",
|
fields = ["id", "status", "next_task", "navigation",
|
||||||
"workflow_spec_id", "spec_version", "is_latest_spec", "total_tasks", "completed_tasks",
|
"workflow_spec_id", "spec_version", "is_latest_spec", "total_tasks", "completed_tasks",
|
||||||
"last_updated", "is_review", "title"]
|
"last_updated", "is_review", "title", "study_id"]
|
||||||
unknown = INCLUDE
|
unknown = INCLUDE
|
||||||
|
|
||||||
status = EnumField(WorkflowStatus)
|
status = EnumField(WorkflowStatus)
|
||||||
|
@ -221,7 +222,7 @@ class WorkflowApiSchema(ma.Schema):
|
||||||
def make_workflow(self, data, **kwargs):
|
def make_workflow(self, data, **kwargs):
|
||||||
keys = ['id', 'status', 'next_task', 'navigation',
|
keys = ['id', 'status', 'next_task', 'navigation',
|
||||||
'workflow_spec_id', 'spec_version', 'is_latest_spec', "total_tasks", "completed_tasks",
|
'workflow_spec_id', 'spec_version', 'is_latest_spec', "total_tasks", "completed_tasks",
|
||||||
"last_updated", "is_review", "title"]
|
"last_updated", "is_review", "title", "study_id"]
|
||||||
filtered_fields = {key: data[key] for key in keys}
|
filtered_fields = {key: data[key] for key in keys}
|
||||||
filtered_fields['next_task'] = TaskSchema().make_task(data['next_task'])
|
filtered_fields['next_task'] = TaskSchema().make_task(data['next_task'])
|
||||||
return WorkflowApi(**filtered_fields)
|
return WorkflowApi(**filtered_fields)
|
||||||
|
|
|
@ -10,7 +10,7 @@ from crc.services.ldap_service import LdapService
|
||||||
class TaskEventModel(db.Model):
|
class TaskEventModel(db.Model):
|
||||||
__tablename__ = 'task_event'
|
__tablename__ = 'task_event'
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
study_id = db.Column(db.Integer, db.ForeignKey('study.id'), nullable=False)
|
study_id = db.Column(db.Integer, db.ForeignKey('study.id'))
|
||||||
user_uid = db.Column(db.String, nullable=False) # In some cases the unique user id may not exist in the db yet.
|
user_uid = db.Column(db.String, nullable=False) # In some cases the unique user id may not exist in the db yet.
|
||||||
workflow_id = db.Column(db.Integer, db.ForeignKey('workflow.id'), nullable=False)
|
workflow_id = db.Column(db.Integer, db.ForeignKey('workflow.id'), nullable=False)
|
||||||
workflow_spec_id = db.Column(db.String, db.ForeignKey('workflow_spec.id'))
|
workflow_spec_id = db.Column(db.String, db.ForeignKey('workflow_spec.id'))
|
||||||
|
|
|
@ -33,6 +33,7 @@ class WorkflowSpecModel(db.Model):
|
||||||
category_id = db.Column(db.Integer, db.ForeignKey('workflow_spec_category.id'), nullable=True)
|
category_id = db.Column(db.Integer, db.ForeignKey('workflow_spec_category.id'), nullable=True)
|
||||||
category = db.relationship("WorkflowSpecCategoryModel")
|
category = db.relationship("WorkflowSpecCategoryModel")
|
||||||
is_master_spec = db.Column(db.Boolean, default=False)
|
is_master_spec = db.Column(db.Boolean, default=False)
|
||||||
|
standalone = db.Column(db.Boolean, default=False)
|
||||||
|
|
||||||
|
|
||||||
class WorkflowSpecModelSchema(SQLAlchemyAutoSchema):
|
class WorkflowSpecModelSchema(SQLAlchemyAutoSchema):
|
||||||
|
@ -88,6 +89,7 @@ class WorkflowModel(db.Model):
|
||||||
total_tasks = db.Column(db.Integer, default=0)
|
total_tasks = db.Column(db.Integer, default=0)
|
||||||
completed_tasks = db.Column(db.Integer, default=0)
|
completed_tasks = db.Column(db.Integer, default=0)
|
||||||
last_updated = db.Column(db.DateTime)
|
last_updated = db.Column(db.DateTime)
|
||||||
|
user_id = db.Column(db.String, default=None)
|
||||||
# Order By is important or generating hashes on reviews.
|
# Order By is important or generating hashes on reviews.
|
||||||
dependencies = db.relationship(WorkflowSpecDependencyFile, cascade="all, delete, delete-orphan",
|
dependencies = db.relationship(WorkflowSpecDependencyFile, cascade="all, delete, delete-orphan",
|
||||||
order_by="WorkflowSpecDependencyFile.file_data_id")
|
order_by="WorkflowSpecDependencyFile.file_data_id")
|
||||||
|
|
|
@ -495,6 +495,7 @@ class StudyService(object):
|
||||||
def _create_workflow_model(study: StudyModel, spec):
|
def _create_workflow_model(study: StudyModel, spec):
|
||||||
workflow_model = WorkflowModel(status=WorkflowStatus.not_started,
|
workflow_model = WorkflowModel(status=WorkflowStatus.not_started,
|
||||||
study=study,
|
study=study,
|
||||||
|
user_id=None,
|
||||||
workflow_spec_id=spec.id,
|
workflow_spec_id=spec.id,
|
||||||
last_updated=datetime.now())
|
last_updated=datetime.now())
|
||||||
session.add(workflow_model)
|
session.add(workflow_model)
|
||||||
|
|
|
@ -408,7 +408,8 @@ class WorkflowService(object):
|
||||||
completed_tasks=processor.workflow_model.completed_tasks,
|
completed_tasks=processor.workflow_model.completed_tasks,
|
||||||
last_updated=processor.workflow_model.last_updated,
|
last_updated=processor.workflow_model.last_updated,
|
||||||
is_review=is_review,
|
is_review=is_review,
|
||||||
title=spec.display_name
|
title=spec.display_name,
|
||||||
|
study_id=processor.workflow_model.study_id or None
|
||||||
)
|
)
|
||||||
if not next_task: # The Next Task can be requested to be a certain task, useful for parallel tasks.
|
if not next_task: # The Next Task can be requested to be a certain task, useful for parallel tasks.
|
||||||
# This may or may not work, sometimes there is no next task to complete.
|
# This may or may not work, sometimes there is no next task to complete.
|
||||||
|
@ -667,6 +668,15 @@ class WorkflowService(object):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_users_assigned_to_task(processor, spiff_task) -> List[str]:
|
def get_users_assigned_to_task(processor, spiff_task) -> List[str]:
|
||||||
|
if processor.workflow_model.study_id is None and processor.workflow_model.user_id is None:
|
||||||
|
raise ApiError.from_task(code='invalid_workflow',
|
||||||
|
message='A workflow must have either a study_id or a user_id.',
|
||||||
|
task=spiff_task)
|
||||||
|
# Standalone workflow - we only care about the current user
|
||||||
|
elif processor.workflow_model.study_id is None and processor.workflow_model.user_id is not None:
|
||||||
|
return [processor.workflow_model.user_id]
|
||||||
|
# Workflow associated with a study - get all the users
|
||||||
|
else:
|
||||||
if not hasattr(spiff_task.task_spec, 'lane') or spiff_task.task_spec.lane is None:
|
if not hasattr(spiff_task.task_spec, 'lane') or spiff_task.task_spec.lane is None:
|
||||||
associated = StudyService.get_study_associates(processor.workflow_model.study.id)
|
associated = StudyService.get_study_associates(processor.workflow_model.study.id)
|
||||||
return [user['uid'] for user in associated if user['access']]
|
return [user['uid'] for user in associated if user['access']]
|
||||||
|
@ -783,3 +793,19 @@ class WorkflowService(object):
|
||||||
for workflow in workflows:
|
for workflow in workflows:
|
||||||
if workflow.status == WorkflowStatus.user_input_required or workflow.status == WorkflowStatus.waiting:
|
if workflow.status == WorkflowStatus.user_input_required or workflow.status == WorkflowStatus.waiting:
|
||||||
WorkflowProcessor.reset(workflow, clear_data=False)
|
WorkflowProcessor.reset(workflow, clear_data=False)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_workflow_from_spec(workflow_spec_id, user):
|
||||||
|
workflow_model = WorkflowModel(status=WorkflowStatus.not_started,
|
||||||
|
study=None,
|
||||||
|
user_id=user.uid,
|
||||||
|
workflow_spec_id=workflow_spec_id,
|
||||||
|
last_updated=datetime.now())
|
||||||
|
db.session.add(workflow_model)
|
||||||
|
db.session.commit()
|
||||||
|
return workflow_model
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_standalone_workflow_specs():
|
||||||
|
specs = db.session.query(WorkflowSpecModel).filter_by(standalone=True).all()
|
||||||
|
return specs
|
||||||
|
|
|
@ -266,7 +266,7 @@ class ExampleDataLoader:
|
||||||
from_tests=True)
|
from_tests=True)
|
||||||
|
|
||||||
def create_spec(self, id, name, display_name="", description="", filepath=None, master_spec=False,
|
def create_spec(self, id, name, display_name="", description="", filepath=None, master_spec=False,
|
||||||
category_id=None, display_order=None, from_tests=False):
|
category_id=None, display_order=None, from_tests=False, standalone=False):
|
||||||
"""Assumes that a directory exists in static/bpmn with the same name as the given id.
|
"""Assumes that a directory exists in static/bpmn with the same name as the given id.
|
||||||
further assumes that the [id].bpmn is the primary file for the workflow.
|
further assumes that the [id].bpmn is the primary file for the workflow.
|
||||||
returns an array of data models to be added to the database."""
|
returns an array of data models to be added to the database."""
|
||||||
|
@ -278,7 +278,8 @@ class ExampleDataLoader:
|
||||||
description=description,
|
description=description,
|
||||||
is_master_spec=master_spec,
|
is_master_spec=master_spec,
|
||||||
category_id=category_id,
|
category_id=category_id,
|
||||||
display_order=display_order)
|
display_order=display_order,
|
||||||
|
standalone=standalone)
|
||||||
db.session.add(spec)
|
db.session.add(spec)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
if not filepath and not from_tests:
|
if not filepath and not from_tests:
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
"""empty message
|
||||||
|
|
||||||
|
Revision ID: 8b976945a54e
|
||||||
|
Revises: c872232ebdcb
|
||||||
|
Create Date: 2021-04-18 11:42:41.894378
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '8b976945a54e'
|
||||||
|
down_revision = 'c872232ebdcb'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
op.add_column('workflow', sa.Column('user_id', sa.String(), nullable=True))
|
||||||
|
op.add_column('workflow_spec', sa.Column('standalone', sa.Boolean(), default=False))
|
||||||
|
op.execute("UPDATE workflow_spec SET standalone=False WHERE standalone is null;")
|
||||||
|
op.execute("ALTER TABLE task_event ALTER COLUMN study_id DROP NOT NULL")
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
op.execute("UPDATE workflow SET user_id=NULL WHERE user_id is not NULL")
|
||||||
|
op.drop_column('workflow', 'user_id')
|
||||||
|
op.drop_column('workflow_spec', 'standalone')
|
||||||
|
op.execute("ALTER TABLE task_event ALTER COLUMN study_id SET NOT NULL ")
|
|
@ -175,11 +175,6 @@ class BaseTest(unittest.TestCase):
|
||||||
specs = session.query(WorkflowSpecModel).all()
|
specs = session.query(WorkflowSpecModel).all()
|
||||||
self.assertIsNotNone(specs)
|
self.assertIsNotNone(specs)
|
||||||
|
|
||||||
for spec in specs:
|
|
||||||
files = session.query(FileModel).filter_by(workflow_spec_id=spec.id).all()
|
|
||||||
self.assertIsNotNone(files)
|
|
||||||
self.assertGreater(len(files), 0)
|
|
||||||
|
|
||||||
for spec in specs:
|
for spec in specs:
|
||||||
files = session.query(FileModel).filter_by(workflow_spec_id=spec.id).all()
|
files = session.query(FileModel).filter_by(workflow_spec_id=spec.id).all()
|
||||||
self.assertIsNotNone(files)
|
self.assertIsNotNone(files)
|
||||||
|
@ -379,6 +374,10 @@ class BaseTest(unittest.TestCase):
|
||||||
|
|
||||||
def complete_form(self, workflow_in, task_in, dict_data, update_all=False, error_code=None, terminate_loop=None,
|
def complete_form(self, workflow_in, task_in, dict_data, update_all=False, error_code=None, terminate_loop=None,
|
||||||
user_uid="dhf8r"):
|
user_uid="dhf8r"):
|
||||||
|
# workflow_in should be a workflow, not a workflow_api
|
||||||
|
# we were passing in workflow_api in many of our tests, and
|
||||||
|
# this caused problems testing standalone workflows
|
||||||
|
standalone = getattr(workflow_in.workflow_spec, 'standalone', False)
|
||||||
prev_completed_task_count = workflow_in.completed_tasks
|
prev_completed_task_count = workflow_in.completed_tasks
|
||||||
if isinstance(task_in, dict):
|
if isinstance(task_in, dict):
|
||||||
task_id = task_in["id"]
|
task_id = task_in["id"]
|
||||||
|
@ -421,6 +420,7 @@ class BaseTest(unittest.TestCase):
|
||||||
.order_by(TaskEventModel.date.desc()).all()
|
.order_by(TaskEventModel.date.desc()).all()
|
||||||
self.assertGreater(len(task_events), 0)
|
self.assertGreater(len(task_events), 0)
|
||||||
event = task_events[0]
|
event = task_events[0]
|
||||||
|
if not standalone:
|
||||||
self.assertIsNotNone(event.study_id)
|
self.assertIsNotNone(event.study_id)
|
||||||
self.assertEqual(user_uid, event.user_uid)
|
self.assertEqual(user_uid, event.user_uid)
|
||||||
self.assertEqual(workflow.id, event.workflow_id)
|
self.assertEqual(workflow.id, event.workflow_id)
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" id="Definitions_0ixyfs0" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="3.5.0">
|
||||||
|
<bpmn:process id="Process_HelloWorld" name="Hello World Process" isExecutable="true">
|
||||||
|
<bpmn:documentation>This workflow asks for a name and says hello</bpmn:documentation>
|
||||||
|
<bpmn:startEvent id="StartEvent_1">
|
||||||
|
<bpmn:outgoing>SequenceFlow_0qyd2b7</bpmn:outgoing>
|
||||||
|
</bpmn:startEvent>
|
||||||
|
<bpmn:sequenceFlow id="SequenceFlow_0qyd2b7" sourceRef="StartEvent_1" targetRef="Task_GetName" />
|
||||||
|
<bpmn:userTask id="Task_GetName" name="Get Name" camunda:formKey="Name">
|
||||||
|
<bpmn:documentation>Hello</bpmn:documentation>
|
||||||
|
<bpmn:extensionElements>
|
||||||
|
<camunda:formData>
|
||||||
|
<camunda:formField id="name" label="Name" type="string" defaultValue="World" />
|
||||||
|
</camunda:formData>
|
||||||
|
</bpmn:extensionElements>
|
||||||
|
<bpmn:incoming>SequenceFlow_0qyd2b7</bpmn:incoming>
|
||||||
|
<bpmn:outgoing>SequenceFlow_1h46b40</bpmn:outgoing>
|
||||||
|
</bpmn:userTask>
|
||||||
|
<bpmn:sequenceFlow id="SequenceFlow_1h46b40" sourceRef="Task_GetName" targetRef="Task_SayHello" />
|
||||||
|
<bpmn:manualTask id="Task_SayHello" name="Say Hello">
|
||||||
|
<bpmn:documentation>Hello {{name}}</bpmn:documentation>
|
||||||
|
<bpmn:incoming>SequenceFlow_1h46b40</bpmn:incoming>
|
||||||
|
<bpmn:outgoing>SequenceFlow_0lqrc6e</bpmn:outgoing>
|
||||||
|
</bpmn:manualTask>
|
||||||
|
<bpmn:endEvent id="EndEvent_1l03lqw">
|
||||||
|
<bpmn:incoming>SequenceFlow_0lqrc6e</bpmn:incoming>
|
||||||
|
</bpmn:endEvent>
|
||||||
|
<bpmn:sequenceFlow id="SequenceFlow_0lqrc6e" sourceRef="Task_SayHello" targetRef="EndEvent_1l03lqw" />
|
||||||
|
</bpmn:process>
|
||||||
|
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
|
||||||
|
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_HelloWorld">
|
||||||
|
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
|
||||||
|
<dc:Bounds x="179" y="99" width="36" height="36" />
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNEdge id="SequenceFlow_0qyd2b7_di" bpmnElement="SequenceFlow_0qyd2b7">
|
||||||
|
<di:waypoint x="215" y="117" />
|
||||||
|
<di:waypoint x="270" y="117" />
|
||||||
|
</bpmndi:BPMNEdge>
|
||||||
|
<bpmndi:BPMNShape id="UserTask_0fbucz7_di" bpmnElement="Task_GetName">
|
||||||
|
<dc:Bounds x="270" y="77" width="100" height="80" />
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNEdge id="SequenceFlow_1h46b40_di" bpmnElement="SequenceFlow_1h46b40">
|
||||||
|
<di:waypoint x="370" y="117" />
|
||||||
|
<di:waypoint x="430" y="117" />
|
||||||
|
</bpmndi:BPMNEdge>
|
||||||
|
<bpmndi:BPMNShape id="ManualTask_1tia2zr_di" bpmnElement="Task_SayHello">
|
||||||
|
<dc:Bounds x="430" y="77" width="100" height="80" />
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNShape id="EndEvent_1l03lqw_di" bpmnElement="EndEvent_1l03lqw">
|
||||||
|
<dc:Bounds x="592" y="99" width="36" height="36" />
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNEdge id="SequenceFlow_0lqrc6e_di" bpmnElement="SequenceFlow_0lqrc6e">
|
||||||
|
<di:waypoint x="530" y="117" />
|
||||||
|
<di:waypoint x="592" y="117" />
|
||||||
|
</bpmndi:BPMNEdge>
|
||||||
|
</bpmndi:BPMNPlane>
|
||||||
|
</bpmndi:BPMNDiagram>
|
||||||
|
</bpmn:definitions>
|
|
@ -72,7 +72,7 @@ class TestStudyCancellations(BaseTest):
|
||||||
workflow, study_id = self.load_workflow()
|
workflow, study_id = self.load_workflow()
|
||||||
workflow_api, first_task = self.get_first_task(workflow)
|
workflow_api, first_task = self.get_first_task(workflow)
|
||||||
|
|
||||||
self.complete_form(workflow_api, first_task, {})
|
self.complete_form(workflow, first_task, {})
|
||||||
|
|
||||||
study_result = self.put_study_on_hold(study_id)
|
study_result = self.put_study_on_hold(study_id)
|
||||||
self.assertEqual('New Title', study_result.title)
|
self.assertEqual('New Title', study_result.title)
|
||||||
|
@ -82,10 +82,10 @@ class TestStudyCancellations(BaseTest):
|
||||||
workflow, study_id = self.load_workflow()
|
workflow, study_id = self.load_workflow()
|
||||||
workflow_api, first_task = self.get_first_task(workflow)
|
workflow_api, first_task = self.get_first_task(workflow)
|
||||||
|
|
||||||
self.complete_form(workflow_api, first_task, {})
|
self.complete_form(workflow, first_task, {})
|
||||||
|
|
||||||
workflow_api, next_task = self.get_second_task(workflow)
|
workflow_api, next_task = self.get_second_task(workflow)
|
||||||
self.complete_form(workflow_api, next_task, {'how_many': 3})
|
self.complete_form(workflow, next_task, {'how_many': 3})
|
||||||
|
|
||||||
study_result = self.put_study_on_hold(study_id)
|
study_result = self.put_study_on_hold(study_id)
|
||||||
self.assertEqual('Second Title', study_result.title)
|
self.assertEqual('Second Title', study_result.title)
|
||||||
|
@ -95,13 +95,13 @@ class TestStudyCancellations(BaseTest):
|
||||||
workflow, study_id = self.load_workflow()
|
workflow, study_id = self.load_workflow()
|
||||||
workflow_api, first_task = self.get_first_task(workflow)
|
workflow_api, first_task = self.get_first_task(workflow)
|
||||||
|
|
||||||
self.complete_form(workflow_api, first_task, {})
|
self.complete_form(workflow, first_task, {})
|
||||||
|
|
||||||
workflow_api, second_task = self.get_second_task(workflow)
|
workflow_api, second_task = self.get_second_task(workflow)
|
||||||
self.complete_form(workflow_api, second_task, {'how_many': 3})
|
self.complete_form(workflow, second_task, {'how_many': 3})
|
||||||
|
|
||||||
workflow_api, third_task = self.get_third_task(workflow)
|
workflow_api, third_task = self.get_third_task(workflow)
|
||||||
self.complete_form(workflow_api, third_task, {})
|
self.complete_form(workflow, third_task, {})
|
||||||
|
|
||||||
study_result = self.put_study_on_hold(study_id)
|
study_result = self.put_study_on_hold(study_id)
|
||||||
self.assertEqual('Beer consumption in the bipedal software engineer', study_result.title)
|
self.assertEqual('Beer consumption in the bipedal software engineer', study_result.title)
|
||||||
|
|
|
@ -13,7 +13,8 @@ class TestAutoSetPrimaryBPMN(BaseTest):
|
||||||
category_id = session.query(WorkflowSpecCategoryModel).first().id
|
category_id = session.query(WorkflowSpecCategoryModel).first().id
|
||||||
# Add a workflow spec
|
# Add a workflow spec
|
||||||
spec = WorkflowSpecModel(id='make_cookies', name='make_cookies', display_name='Cooooookies',
|
spec = WorkflowSpecModel(id='make_cookies', name='make_cookies', display_name='Cooooookies',
|
||||||
description='Om nom nom delicious cookies', category_id=category_id)
|
description='Om nom nom delicious cookies', category_id=category_id,
|
||||||
|
standalone=False)
|
||||||
rv = self.app.post('/v1.0/workflow-specification',
|
rv = self.app.post('/v1.0/workflow-specification',
|
||||||
headers=self.logged_in_headers(),
|
headers=self.logged_in_headers(),
|
||||||
content_type="application/json",
|
content_type="application/json",
|
||||||
|
|
|
@ -23,7 +23,6 @@ class TestEmailScript(BaseTest):
|
||||||
|
|
||||||
first_task = self.get_workflow_api(workflow).next_task
|
first_task = self.get_workflow_api(workflow).next_task
|
||||||
|
|
||||||
workflow = self.get_workflow_api(workflow)
|
|
||||||
self.complete_form(workflow, first_task, {'subject': 'My Email Subject', 'recipients': 'test@example.com'})
|
self.complete_form(workflow, first_task, {'subject': 'My Email Subject', 'recipients': 'test@example.com'})
|
||||||
|
|
||||||
self.assertEqual(1, len(outbox))
|
self.assertEqual(1, len(outbox))
|
||||||
|
@ -49,7 +48,6 @@ class TestEmailScript(BaseTest):
|
||||||
def test_bad_email_address_1(self):
|
def test_bad_email_address_1(self):
|
||||||
workflow = self.create_workflow('email_script')
|
workflow = self.create_workflow('email_script')
|
||||||
first_task = self.get_workflow_api(workflow).next_task
|
first_task = self.get_workflow_api(workflow).next_task
|
||||||
workflow = self.get_workflow_api(workflow)
|
|
||||||
|
|
||||||
with self.assertRaises(AssertionError):
|
with self.assertRaises(AssertionError):
|
||||||
self.complete_form(workflow, first_task, {'recipients': 'test@example'})
|
self.complete_form(workflow, first_task, {'recipients': 'test@example'})
|
||||||
|
@ -57,7 +55,6 @@ class TestEmailScript(BaseTest):
|
||||||
def test_bad_email_address_2(self):
|
def test_bad_email_address_2(self):
|
||||||
workflow = self.create_workflow('email_script')
|
workflow = self.create_workflow('email_script')
|
||||||
first_task = self.get_workflow_api(workflow).next_task
|
first_task = self.get_workflow_api(workflow).next_task
|
||||||
workflow = self.get_workflow_api(workflow)
|
|
||||||
|
|
||||||
with self.assertRaises(AssertionError):
|
with self.assertRaises(AssertionError):
|
||||||
self.complete_form(workflow, first_task, {'recipients': 'test'})
|
self.complete_form(workflow, first_task, {'recipients': 'test'})
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
from tests.base_test import BaseTest
|
||||||
|
|
||||||
|
from crc import session
|
||||||
|
from crc.models.user import UserModel
|
||||||
|
from crc.services.workflow_service import WorkflowService
|
||||||
|
|
||||||
|
from example_data import ExampleDataLoader
|
||||||
|
|
||||||
|
|
||||||
|
class TestNoStudyWorkflow(BaseTest):
|
||||||
|
|
||||||
|
def test_no_study_workflow(self):
|
||||||
|
self.load_example_data()
|
||||||
|
spec = ExampleDataLoader().create_spec('hello_world', 'Hello World', standalone=True, from_tests=True)
|
||||||
|
user = session.query(UserModel).first()
|
||||||
|
self.assertIsNotNone(user)
|
||||||
|
workflow_model = WorkflowService.get_workflow_from_spec(spec.id, user)
|
||||||
|
workflow_api = self.get_workflow_api(workflow_model)
|
||||||
|
first_task = workflow_api.next_task
|
||||||
|
self.complete_form(workflow_model, first_task, {'name': 'Big Guy'})
|
||||||
|
workflow_api = self.get_workflow_api(workflow_model)
|
||||||
|
second_task = workflow_api.next_task
|
||||||
|
self.assertEqual(second_task.documentation, 'Hello Big Guy')
|
|
@ -13,10 +13,10 @@ class TestMessageEvent(BaseTest):
|
||||||
# Start the workflow.
|
# Start the workflow.
|
||||||
first_task = self.get_workflow_api(workflow).next_task
|
first_task = self.get_workflow_api(workflow).next_task
|
||||||
self.assertEqual('Activity_GetData', first_task.name)
|
self.assertEqual('Activity_GetData', first_task.name)
|
||||||
workflow = self.get_workflow_api(workflow)
|
|
||||||
self.complete_form(workflow, first_task, {'formdata': 'asdf'})
|
self.complete_form(workflow, first_task, {'formdata': 'asdf'})
|
||||||
workflow = self.get_workflow_api(workflow)
|
workflow_api = self.get_workflow_api(workflow)
|
||||||
self.assertEqual('Activity_HowMany', workflow.next_task.name)
|
self.assertEqual('Activity_HowMany', workflow_api.next_task.name)
|
||||||
|
|
||||||
# reset the workflow
|
# reset the workflow
|
||||||
# this ultimately calls crc.api.workflow.set_current_task
|
# this ultimately calls crc.api.workflow.set_current_task
|
||||||
|
|
|
@ -67,14 +67,14 @@ class TestMultiinstanceTasksApi(BaseTest):
|
||||||
content_type="application/json")
|
content_type="application/json")
|
||||||
self.assert_success(rv)
|
self.assert_success(rv)
|
||||||
json_data = json.loads(rv.get_data(as_text=True))
|
json_data = json.loads(rv.get_data(as_text=True))
|
||||||
workflow = WorkflowApiSchema().load(json_data)
|
workflow_api = WorkflowApiSchema().load(json_data)
|
||||||
data = workflow.next_task.data
|
data = workflow_api.next_task.data
|
||||||
data['investigator']['email'] = "dhf8r@virginia.edu"
|
data['investigator']['email'] = "dhf8r@virginia.edu"
|
||||||
self.complete_form(workflow, workflow.next_task, data)
|
self.complete_form(workflow, workflow_api.next_task, data)
|
||||||
#tasks = self.get_workflow_api(workflow).user_tasks
|
#tasks = self.get_workflow_api(workflow).user_tasks
|
||||||
|
|
||||||
workflow = self.get_workflow_api(workflow)
|
workflow_api = self.get_workflow_api(workflow)
|
||||||
self.assertEqual(WorkflowStatus.complete, workflow.status)
|
self.assertEqual(WorkflowStatus.complete, workflow_api.status)
|
||||||
|
|
||||||
|
|
||||||
@patch('crc.services.protocol_builder.requests.get')
|
@patch('crc.services.protocol_builder.requests.get')
|
||||||
|
|
|
@ -386,15 +386,15 @@ class TestTasksApi(BaseTest):
|
||||||
# Start the workflow.
|
# Start the workflow.
|
||||||
first_task = self.get_workflow_api(workflow).next_task
|
first_task = self.get_workflow_api(workflow).next_task
|
||||||
self.complete_form(workflow, first_task, {"has_bananas": True})
|
self.complete_form(workflow, first_task, {"has_bananas": True})
|
||||||
workflow = self.get_workflow_api(workflow)
|
workflow_api = self.get_workflow_api(workflow)
|
||||||
self.assertEqual('Task_Num_Bananas', workflow.next_task.name)
|
self.assertEqual('Task_Num_Bananas', workflow_api.next_task.name)
|
||||||
|
|
||||||
# Trying to re-submit the initial task, and answer differently, should result in an error.
|
# Trying to re-submit the initial task, and answer differently, should result in an error.
|
||||||
self.complete_form(workflow, first_task, {"has_bananas": False}, error_code="invalid_state")
|
self.complete_form(workflow, first_task, {"has_bananas": False}, error_code="invalid_state")
|
||||||
|
|
||||||
# Go ahead and set the number of bananas.
|
# Go ahead and set the number of bananas.
|
||||||
workflow = self.get_workflow_api(workflow)
|
workflow_api = self.get_workflow_api(workflow)
|
||||||
task = workflow.next_task
|
task = workflow_api.next_task
|
||||||
|
|
||||||
self.complete_form(workflow, task, {"num_bananas": 4})
|
self.complete_form(workflow, task, {"num_bananas": 4})
|
||||||
# We are now at the end of the workflow.
|
# We are now at the end of the workflow.
|
||||||
|
@ -405,19 +405,19 @@ class TestTasksApi(BaseTest):
|
||||||
content_type="application/json")
|
content_type="application/json")
|
||||||
self.assert_success(rv)
|
self.assert_success(rv)
|
||||||
json_data = json.loads(rv.get_data(as_text=True))
|
json_data = json.loads(rv.get_data(as_text=True))
|
||||||
workflow = WorkflowApiSchema().load(json_data)
|
workflow_api = WorkflowApiSchema().load(json_data)
|
||||||
|
|
||||||
# Assure the Next Task is the one we just reset the token to be on.
|
# Assure the Next Task is the one we just reset the token to be on.
|
||||||
self.assertEqual("Task_Has_Bananas", workflow.next_task.name)
|
self.assertEqual("Task_Has_Bananas", workflow_api.next_task.name)
|
||||||
|
|
||||||
# Go ahead and get that workflow one more time, it should still be right.
|
# Go ahead and get that workflow one more time, it should still be right.
|
||||||
workflow = self.get_workflow_api(workflow)
|
workflow_api = self.get_workflow_api(workflow)
|
||||||
|
|
||||||
# Assure the Next Task is the one we just reset the token to be on.
|
# Assure the Next Task is the one we just reset the token to be on.
|
||||||
self.assertEqual("Task_Has_Bananas", workflow.next_task.name)
|
self.assertEqual("Task_Has_Bananas", workflow_api.next_task.name)
|
||||||
|
|
||||||
# The next task should be a different value.
|
# The next task should be a different value.
|
||||||
self.complete_form(workflow, workflow.next_task, {"has_bananas": False})
|
self.complete_form(workflow, workflow_api.next_task, {"has_bananas": False})
|
||||||
workflow = self.get_workflow_api(workflow)
|
workflow_api = self.get_workflow_api(workflow)
|
||||||
self.assertEqual('Task_Why_No_Bananas', workflow.next_task.name)
|
self.assertEqual('Task_Why_No_Bananas', workflow_api.next_task.name)
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ class TestBooleanDefault(BaseTest):
|
||||||
workflow = self.create_workflow('boolean_default_value')
|
workflow = self.create_workflow('boolean_default_value')
|
||||||
workflow_api = self.get_workflow_api(workflow)
|
workflow_api = self.get_workflow_api(workflow)
|
||||||
set_default_task = workflow_api.next_task
|
set_default_task = workflow_api.next_task
|
||||||
result = self.complete_form(workflow_api, set_default_task, {'yes_no': yes_no})
|
result = self.complete_form(workflow, set_default_task, {'yes_no': yes_no})
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def test_boolean_true_string(self):
|
def test_boolean_true_string(self):
|
||||||
|
|
|
@ -7,35 +7,35 @@ class TestWorkflowEnumDefault(BaseTest):
|
||||||
def test_enum_default_from_value_expression(self):
|
def test_enum_default_from_value_expression(self):
|
||||||
workflow = self.create_workflow('enum_value_expression')
|
workflow = self.create_workflow('enum_value_expression')
|
||||||
|
|
||||||
first_task = self.get_workflow_api(workflow).next_task
|
|
||||||
self.assertEqual('Activity_UserInput', first_task.name)
|
|
||||||
workflow_api = self.get_workflow_api(workflow)
|
workflow_api = self.get_workflow_api(workflow)
|
||||||
|
first_task = workflow_api.next_task
|
||||||
|
self.assertEqual('Activity_UserInput', first_task.name)
|
||||||
|
|
||||||
result = self.complete_form(workflow_api, first_task, {'user_input': True})
|
result = self.complete_form(workflow, first_task, {'user_input': True})
|
||||||
self.assertIn('user_input', result.next_task.data)
|
self.assertIn('user_input', result.next_task.data)
|
||||||
self.assertEqual(True, result.next_task.data['user_input'])
|
self.assertEqual(True, result.next_task.data['user_input'])
|
||||||
self.assertIn('lookup_output', result.next_task.data)
|
self.assertIn('lookup_output', result.next_task.data)
|
||||||
self.assertEqual('black', result.next_task.data['lookup_output'])
|
self.assertEqual('black', result.next_task.data['lookup_output'])
|
||||||
|
|
||||||
workflow_api = self.get_workflow_api(workflow)
|
workflow_api = self.get_workflow_api(workflow)
|
||||||
self.assertEqual('Activity_PickColor', self.get_workflow_api(workflow_api).next_task.name)
|
self.assertEqual('Activity_PickColor', workflow_api.next_task.name)
|
||||||
self.assertEqual({'value': 'black', 'label': 'Black'}, workflow_api.next_task.data['color_select'])
|
self.assertEqual({'value': 'black', 'label': 'Black'}, workflow_api.next_task.data['color_select'])
|
||||||
|
|
||||||
#
|
#
|
||||||
workflow = self.create_workflow('enum_value_expression')
|
workflow = self.create_workflow('enum_value_expression')
|
||||||
|
|
||||||
first_task = self.get_workflow_api(workflow).next_task
|
|
||||||
self.assertEqual('Activity_UserInput', first_task.name)
|
|
||||||
workflow_api = self.get_workflow_api(workflow)
|
workflow_api = self.get_workflow_api(workflow)
|
||||||
|
first_task = workflow_api.next_task
|
||||||
|
self.assertEqual('Activity_UserInput', first_task.name)
|
||||||
|
|
||||||
result = self.complete_form(workflow_api, first_task, {'user_input': False})
|
result = self.complete_form(workflow, first_task, {'user_input': False})
|
||||||
self.assertIn('user_input', result.next_task.data)
|
self.assertIn('user_input', result.next_task.data)
|
||||||
self.assertEqual(False, result.next_task.data['user_input'])
|
self.assertEqual(False, result.next_task.data['user_input'])
|
||||||
self.assertIn('lookup_output', result.next_task.data)
|
self.assertIn('lookup_output', result.next_task.data)
|
||||||
self.assertEqual('white', result.next_task.data['lookup_output'])
|
self.assertEqual('white', result.next_task.data['lookup_output'])
|
||||||
|
|
||||||
workflow_api = self.get_workflow_api(workflow)
|
workflow_api = self.get_workflow_api(workflow)
|
||||||
self.assertEqual('Activity_PickColor', self.get_workflow_api(workflow_api).next_task.name)
|
self.assertEqual('Activity_PickColor', workflow_api.next_task.name)
|
||||||
self.assertEqual({'value': 'white', 'label': 'White'}, workflow_api.next_task.data['color_select'])
|
self.assertEqual({'value': 'white', 'label': 'White'}, workflow_api.next_task.data['color_select'])
|
||||||
|
|
||||||
def test_enum_value_expression_and_default(self):
|
def test_enum_value_expression_and_default(self):
|
||||||
|
|
|
@ -18,7 +18,7 @@ class TestFormFieldName(BaseTest):
|
||||||
|
|
||||||
workflow_api = self.get_workflow_api(workflow)
|
workflow_api = self.get_workflow_api(workflow)
|
||||||
first_task = workflow_api.next_task
|
first_task = workflow_api.next_task
|
||||||
self.complete_form(workflow_api, first_task, {})
|
self.complete_form(workflow, first_task, {})
|
||||||
|
|
||||||
workflow_api = self.get_workflow_api(workflow)
|
workflow_api = self.get_workflow_api(workflow)
|
||||||
second_task = workflow_api.next_task
|
second_task = workflow_api.next_task
|
||||||
|
|
|
@ -34,14 +34,13 @@ class TestWorkflowHiddenRequiredField(BaseTest):
|
||||||
|
|
||||||
first_task = workflow_api.next_task
|
first_task = workflow_api.next_task
|
||||||
self.assertEqual('Activity_Hello', first_task.name)
|
self.assertEqual('Activity_Hello', first_task.name)
|
||||||
workflow_api = self.get_workflow_api(workflow)
|
|
||||||
|
|
||||||
self.complete_form(workflow_api, first_task, {})
|
self.complete_form(workflow, first_task, {})
|
||||||
workflow_api = self.get_workflow_api(workflow)
|
workflow_api = self.get_workflow_api(workflow)
|
||||||
|
|
||||||
second_task = workflow_api.next_task
|
second_task = workflow_api.next_task
|
||||||
self.assertEqual('Activity_HiddenField', second_task.name)
|
self.assertEqual('Activity_HiddenField', second_task.name)
|
||||||
self.complete_form(workflow_api, second_task, {})
|
self.complete_form(workflow, second_task, {})
|
||||||
workflow_api = self.get_workflow_api(workflow)
|
workflow_api = self.get_workflow_api(workflow)
|
||||||
|
|
||||||
# The color field is hidden and required. Make sure we use the default value
|
# The color field is hidden and required. Make sure we use the default value
|
||||||
|
|
|
@ -12,20 +12,20 @@ class TestWorkflowRestart(BaseTest):
|
||||||
|
|
||||||
workflow = self.create_workflow('message_event')
|
workflow = self.create_workflow('message_event')
|
||||||
|
|
||||||
first_task = self.get_workflow_api(workflow).next_task
|
|
||||||
self.assertEqual('Activity_GetData', first_task.name)
|
|
||||||
workflow_api = self.get_workflow_api(workflow)
|
workflow_api = self.get_workflow_api(workflow)
|
||||||
|
first_task = workflow_api.next_task
|
||||||
|
self.assertEqual('Activity_GetData', first_task.name)
|
||||||
|
|
||||||
result = self.complete_form(workflow_api, first_task, {'formdata': 'asdf'})
|
result = self.complete_form(workflow, first_task, {'formdata': 'asdf'})
|
||||||
self.assertIn('formdata', result.next_task.data)
|
self.assertIn('formdata', result.next_task.data)
|
||||||
self.assertEqual('asdf', result.next_task.data['formdata'])
|
self.assertEqual('asdf', result.next_task.data['formdata'])
|
||||||
|
|
||||||
workflow_api = self.get_workflow_api(workflow)
|
workflow_api = self.get_workflow_api(workflow)
|
||||||
self.assertEqual('Activity_HowMany', self.get_workflow_api(workflow_api).next_task.name)
|
self.assertEqual('Activity_HowMany', workflow_api.next_task.name)
|
||||||
|
|
||||||
# restart with data. should land at beginning with data
|
# restart with data. should land at beginning with data
|
||||||
workflow_api = self.restart_workflow_api(result)
|
workflow_api = self.restart_workflow_api(result)
|
||||||
first_task = self.get_workflow_api(workflow_api).next_task
|
first_task = workflow_api.next_task
|
||||||
self.assertEqual('Activity_GetData', first_task.name)
|
self.assertEqual('Activity_GetData', first_task.name)
|
||||||
self.assertIn('formdata', workflow_api.next_task.data)
|
self.assertIn('formdata', workflow_api.next_task.data)
|
||||||
self.assertEqual('asdf', workflow_api.next_task.data['formdata'])
|
self.assertEqual('asdf', workflow_api.next_task.data['formdata'])
|
||||||
|
@ -36,7 +36,6 @@ class TestWorkflowRestart(BaseTest):
|
||||||
self.assertEqual('Activity_GetData', first_task.name)
|
self.assertEqual('Activity_GetData', first_task.name)
|
||||||
self.assertNotIn('formdata', workflow_api.next_task.data)
|
self.assertNotIn('formdata', workflow_api.next_task.data)
|
||||||
|
|
||||||
|
|
||||||
def test_workflow_restart_delete_files(self):
|
def test_workflow_restart_delete_files(self):
|
||||||
self.load_example_data()
|
self.load_example_data()
|
||||||
irb_code = 'Study_Protocol_Document'
|
irb_code = 'Study_Protocol_Document'
|
||||||
|
@ -80,14 +79,14 @@ class TestWorkflowRestart(BaseTest):
|
||||||
study_id = workflow.study_id
|
study_id = workflow.study_id
|
||||||
|
|
||||||
# Start the workflow.
|
# Start the workflow.
|
||||||
first_task = self.get_workflow_api(workflow).next_task
|
|
||||||
self.assertEqual('Activity_GetData', first_task.name)
|
|
||||||
workflow_api = self.get_workflow_api(workflow)
|
workflow_api = self.get_workflow_api(workflow)
|
||||||
self.complete_form(workflow_api, first_task, {'formdata': 'asdf'})
|
first_task = workflow_api.next_task
|
||||||
|
self.assertEqual('Activity_GetData', first_task.name)
|
||||||
|
self.complete_form(workflow, first_task, {'formdata': 'asdf'})
|
||||||
workflow_api = self.get_workflow_api(workflow)
|
workflow_api = self.get_workflow_api(workflow)
|
||||||
self.assertEqual('Activity_HowMany', workflow_api.next_task.name)
|
self.assertEqual('Activity_HowMany', workflow_api.next_task.name)
|
||||||
|
|
||||||
workflow_api = self.restart_workflow_api(workflow)
|
self.restart_workflow_api(workflow)
|
||||||
study_result = session.query(StudyModel).filter(StudyModel.id == study_id).first()
|
study_result = session.query(StudyModel).filter(StudyModel.id == study_id).first()
|
||||||
self.assertEqual('New Title', study_result.title)
|
self.assertEqual('New Title', study_result.title)
|
||||||
|
|
||||||
|
@ -106,17 +105,16 @@ class TestWorkflowRestart(BaseTest):
|
||||||
study_id = workflow.study_id
|
study_id = workflow.study_id
|
||||||
|
|
||||||
# Start the workflow.
|
# Start the workflow.
|
||||||
first_task = self.get_workflow_api(workflow).next_task
|
|
||||||
self.assertEqual('Activity_GetData', first_task.name)
|
|
||||||
workflow_api = self.get_workflow_api(workflow)
|
workflow_api = self.get_workflow_api(workflow)
|
||||||
self.complete_form(workflow_api, first_task, {'formdata': 'asdf'})
|
first_task = workflow_api.next_task
|
||||||
|
self.assertEqual('Activity_GetData', first_task.name)
|
||||||
|
self.complete_form(workflow, first_task, {'formdata': 'asdf'})
|
||||||
|
|
||||||
workflow_api = self.get_workflow_api(workflow)
|
workflow_api = self.get_workflow_api(workflow)
|
||||||
next_task = workflow_api.next_task
|
next_task = workflow_api.next_task
|
||||||
self.assertEqual('Activity_HowMany', next_task.name)
|
self.assertEqual('Activity_HowMany', next_task.name)
|
||||||
self.complete_form(workflow_api, next_task, {'how_many': 3})
|
self.complete_form(workflow, next_task, {'how_many': 3})
|
||||||
|
|
||||||
workflow_api = self.restart_workflow_api(workflow)
|
|
||||||
study_result = session.query(StudyModel).filter(StudyModel.id == study_id).first()
|
study_result = session.query(StudyModel).filter(StudyModel.id == study_id).first()
|
||||||
self.assertEqual('Beer consumption in the bipedal software engineer', study_result.title)
|
self.assertEqual('Beer consumption in the bipedal software engineer', study_result.title)
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,9 @@ import json
|
||||||
from tests.base_test import BaseTest
|
from tests.base_test import BaseTest
|
||||||
from crc import session
|
from crc import session
|
||||||
from crc.models.file import FileModel
|
from crc.models.file import FileModel
|
||||||
from crc.models.workflow import WorkflowSpecModel, WorkflowSpecModelSchema, WorkflowModel, WorkflowSpecCategoryModel
|
from crc.models.workflow import WorkflowSpecModel, WorkflowSpecModelSchema, WorkflowModel, WorkflowSpecCategoryModel, WorkflowSpecCategoryModelSchema
|
||||||
|
|
||||||
|
from example_data import ExampleDataLoader
|
||||||
|
|
||||||
|
|
||||||
class TestWorkflowSpec(BaseTest):
|
class TestWorkflowSpec(BaseTest):
|
||||||
|
@ -28,7 +30,8 @@ class TestWorkflowSpec(BaseTest):
|
||||||
category_id = session.query(WorkflowSpecCategoryModel).first().id
|
category_id = session.query(WorkflowSpecCategoryModel).first().id
|
||||||
category_count = session.query(WorkflowSpecModel).filter_by(category_id=category_id).count()
|
category_count = session.query(WorkflowSpecModel).filter_by(category_id=category_id).count()
|
||||||
spec = WorkflowSpecModel(id='make_cookies', name='make_cookies', display_name='Cooooookies',
|
spec = WorkflowSpecModel(id='make_cookies', name='make_cookies', display_name='Cooooookies',
|
||||||
description='Om nom nom delicious cookies', category_id=category_id)
|
description='Om nom nom delicious cookies', category_id=category_id,
|
||||||
|
standalone=False)
|
||||||
rv = self.app.post('/v1.0/workflow-specification',
|
rv = self.app.post('/v1.0/workflow-specification',
|
||||||
headers=self.logged_in_headers(),
|
headers=self.logged_in_headers(),
|
||||||
content_type="application/json",
|
content_type="application/json",
|
||||||
|
@ -101,3 +104,60 @@ class TestWorkflowSpec(BaseTest):
|
||||||
num_workflows_after = session.query(WorkflowModel).filter_by(workflow_spec_id=spec_id).count()
|
num_workflows_after = session.query(WorkflowModel).filter_by(workflow_spec_id=spec_id).count()
|
||||||
self.assertEqual(num_files_after + num_workflows_after, 0)
|
self.assertEqual(num_files_after + num_workflows_after, 0)
|
||||||
|
|
||||||
|
def test_get_standalone_workflow_specs(self):
|
||||||
|
self.load_example_data()
|
||||||
|
category = session.query(WorkflowSpecCategoryModel).first()
|
||||||
|
ExampleDataLoader().create_spec('hello_world', 'Hello World', category_id=category.id,
|
||||||
|
standalone=True, from_tests=True)
|
||||||
|
rv = self.app.get('/v1.0/workflow-specification/standalone', headers=self.logged_in_headers())
|
||||||
|
self.assertEqual(1, len(rv.json))
|
||||||
|
|
||||||
|
ExampleDataLoader().create_spec('email_script', 'Email Script', category_id=category.id,
|
||||||
|
standalone=True, from_tests=True)
|
||||||
|
|
||||||
|
rv = self.app.get('/v1.0/workflow-specification/standalone', headers=self.logged_in_headers())
|
||||||
|
self.assertEqual(2, len(rv.json))
|
||||||
|
|
||||||
|
def test_get_workflow_from_workflow_spec(self):
|
||||||
|
self.load_example_data()
|
||||||
|
spec = ExampleDataLoader().create_spec('hello_world', 'Hello World', standalone=True, from_tests=True)
|
||||||
|
rv = self.app.post(f'/v1.0/workflow-specification/{spec.id}', headers=self.logged_in_headers())
|
||||||
|
self.assert_success(rv)
|
||||||
|
self.assertEqual('hello_world', rv.json['workflow_spec_id'])
|
||||||
|
self.assertEqual('Task_GetName', rv.json['next_task']['name'])
|
||||||
|
|
||||||
|
def test_add_workflow_spec_category(self):
|
||||||
|
self.load_example_data()
|
||||||
|
count = session.query(WorkflowSpecCategoryModel).count()
|
||||||
|
category = WorkflowSpecCategoryModel(
|
||||||
|
id=count,
|
||||||
|
name='another_test_category',
|
||||||
|
display_name='Another Test Category',
|
||||||
|
display_order=0
|
||||||
|
)
|
||||||
|
rv = self.app.post(f'/v1.0/workflow-specification-category',
|
||||||
|
headers=self.logged_in_headers(),
|
||||||
|
content_type="application/json",
|
||||||
|
data=json.dumps(WorkflowSpecCategoryModelSchema().dump(category))
|
||||||
|
)
|
||||||
|
self.assert_success(rv)
|
||||||
|
result = session.query(WorkflowSpecCategoryModel).filter(WorkflowSpecCategoryModel.name=='another_test_category').first()
|
||||||
|
self.assertEqual('Another Test Category', result.display_name)
|
||||||
|
self.assertEqual(count, result.id)
|
||||||
|
|
||||||
|
def test_update_workflow_spec_category(self):
|
||||||
|
self.load_example_data()
|
||||||
|
category = session.query(WorkflowSpecCategoryModel).first()
|
||||||
|
category_name_before = category.name
|
||||||
|
new_category_name = category_name_before + '_asdf'
|
||||||
|
self.assertNotEqual(category_name_before, new_category_name)
|
||||||
|
|
||||||
|
category.name = new_category_name
|
||||||
|
|
||||||
|
rv = self.app.put(f'/v1.0/workflow-specification-category/{category.id}',
|
||||||
|
content_type="application/json",
|
||||||
|
headers=self.logged_in_headers(),
|
||||||
|
data=json.dumps(WorkflowSpecCategoryModelSchema().dump(category)))
|
||||||
|
self.assert_success(rv)
|
||||||
|
json_data = json.loads(rv.get_data(as_text=True))
|
||||||
|
self.assertEqual(new_category_name, json_data['name'])
|
||||||
|
|
|
@ -9,7 +9,7 @@ class TestValueExpression(BaseTest):
|
||||||
|
|
||||||
workflow_api = self.get_workflow_api(workflow)
|
workflow_api = self.get_workflow_api(workflow)
|
||||||
first_task = workflow_api.next_task
|
first_task = workflow_api.next_task
|
||||||
self.complete_form(workflow_api, first_task, {'value_expression_value': ''})
|
self.complete_form(workflow, first_task, {'value_expression_value': ''})
|
||||||
|
|
||||||
workflow_api = self.get_workflow_api(workflow)
|
workflow_api = self.get_workflow_api(workflow)
|
||||||
second_task = workflow_api.next_task
|
second_task = workflow_api.next_task
|
||||||
|
@ -26,7 +26,7 @@ class TestValueExpression(BaseTest):
|
||||||
|
|
||||||
workflow_api = self.get_workflow_api(workflow)
|
workflow_api = self.get_workflow_api(workflow)
|
||||||
first_task = workflow_api.next_task
|
first_task = workflow_api.next_task
|
||||||
self.complete_form(workflow_api, first_task, {'value_expression_value': 'black'})
|
self.complete_form(workflow, first_task, {'value_expression_value': 'black'})
|
||||||
|
|
||||||
workflow_api = self.get_workflow_api(workflow)
|
workflow_api = self.get_workflow_api(workflow)
|
||||||
second_task = workflow_api.next_task
|
second_task = workflow_api.next_task
|
||||||
|
|
Loading…
Reference in New Issue