mirror of
https://github.com/sartography/cr-connect-workflow.git
synced 2025-02-20 11:48:16 +00:00
Returning better cleaner information about workflow with the workflow endpoint. Removes the get_all_tasks and get_user_tasks endpoints as tasks are returned with the workflow. Workflow endpoint also includes the last task and next_task, which may or may not be user tasks. The task "type" returned is now the class name of the task_spec, rather than just the word "task".
This commit is contained in:
parent
349dac6e44
commit
9bd93748be
18
Pipfile.lock
generated
18
Pipfile.lock
generated
@ -25,10 +25,10 @@
|
||||
},
|
||||
"alembic": {
|
||||
"hashes": [
|
||||
"sha256:d412982920653db6e5a44bfd13b1d0db5685cbaaccaf226195749c706e1e862a"
|
||||
"sha256:2df2519a5b002f881517693b95626905a39c5faf4b5a1f94de4f1441095d1d26"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.3.3"
|
||||
"version": "==1.4.0"
|
||||
},
|
||||
"amqp": {
|
||||
"hashes": [
|
||||
@ -293,11 +293,11 @@
|
||||
},
|
||||
"flask-restful": {
|
||||
"hashes": [
|
||||
"sha256:ecd620c5cc29f663627f99e04f17d1f16d095c83dc1d618426e2ad68b03092f8",
|
||||
"sha256:f8240ec12349afe8df1db168ea7c336c4e5b0271a36982bff7394f93275f2ca9"
|
||||
"sha256:5ea9a5991abf2cb69b4aac19793faac6c032300505b325687d7c305ffaa76915",
|
||||
"sha256:d891118b951921f1cec80cabb4db98ea6058a35e6404788f9e70d5b243813ec2"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.3.7"
|
||||
"version": "==0.3.8"
|
||||
},
|
||||
"flask-sqlalchemy": {
|
||||
"hashes": [
|
||||
@ -723,7 +723,7 @@
|
||||
"spiffworkflow": {
|
||||
"editable": true,
|
||||
"git": "https://github.com/sartography/SpiffWorkflow.git",
|
||||
"ref": "7640c6e32d3894b13f8a078849922cf7cb6884a5"
|
||||
"ref": "6091825791a5e9f11c12639ac07688377bad697e"
|
||||
},
|
||||
"sqlalchemy": {
|
||||
"hashes": [
|
||||
@ -778,10 +778,10 @@
|
||||
},
|
||||
"werkzeug": {
|
||||
"hashes": [
|
||||
"sha256:1e0dedc2acb1f46827daa2e399c1485c8fa17c0d8e70b6b875b4e7f54bf408d2",
|
||||
"sha256:b353856d37dec59d6511359f97f6a4b2468442e454bd1c98298ddce53cac1f04"
|
||||
"sha256:169ba8a33788476292d04186ab33b01d6add475033dfc07215e6d219cc077096",
|
||||
"sha256:6dc65cf9091cf750012f56f2cad759fa9e879f511b5ff8685e456b4e3bf90d16"
|
||||
],
|
||||
"version": "==0.16.1"
|
||||
"version": "==1.0.0"
|
||||
},
|
||||
"xlsxwriter": {
|
||||
"hashes": [
|
||||
|
77
crc/api.yml
77
crc/api.yml
@ -487,64 +487,6 @@ paths:
|
||||
responses:
|
||||
'204':
|
||||
description: The workflow was removed
|
||||
/workflow/{workflow_id}/all_tasks:
|
||||
get:
|
||||
operationId: crc.api.workflow.get_all_tasks
|
||||
summary: Return a list of all tasks for this workflow
|
||||
tags:
|
||||
- Workflows and Tasks
|
||||
parameters:
|
||||
- name: workflow_id
|
||||
in: path
|
||||
required: true
|
||||
description: The id of the workflow
|
||||
schema:
|
||||
type: integer
|
||||
format: int32
|
||||
responses:
|
||||
'200':
|
||||
description: Expected response to a valid request
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/Task"
|
||||
default:
|
||||
description: unexpected error
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Error"
|
||||
/workflow/{workflow_id}/tasks:
|
||||
get:
|
||||
operationId: crc.api.workflow.get_ready_user_tasks
|
||||
summary: Returns the list of ready user tasks for this workflow
|
||||
tags:
|
||||
- Workflows and Tasks
|
||||
parameters:
|
||||
- name: workflow_id
|
||||
in: path
|
||||
required: true
|
||||
description: The id of the workflow
|
||||
schema:
|
||||
type: integer
|
||||
format: int32
|
||||
responses:
|
||||
'200':
|
||||
description: Expected response to a valid request
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/Task"
|
||||
default:
|
||||
description: unexpected error
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Error"
|
||||
# /v1.0/workflow/0/task/0
|
||||
/workflow/{workflow_id}/task/{task_id}:
|
||||
parameters:
|
||||
@ -693,21 +635,16 @@ components:
|
||||
format: int64
|
||||
status:
|
||||
type: enum
|
||||
enum: ['user_input_required','waiting','complete']
|
||||
study_id:
|
||||
readOnly: true
|
||||
enum: ['new','user_input_required','waiting','complete']
|
||||
last_task_id:
|
||||
type: integer
|
||||
workflow_spec_id:
|
||||
readOnly: true
|
||||
type: String
|
||||
current_task_ids:
|
||||
next_task_id:
|
||||
type: integer
|
||||
user_tasks:
|
||||
type: array
|
||||
items:
|
||||
type: String
|
||||
messages:
|
||||
type: array
|
||||
items:
|
||||
type: String
|
||||
$ref: "#/components/schemas/Task"
|
||||
|
||||
example:
|
||||
id: 291234
|
||||
status: 'user_input_required'
|
||||
|
@ -2,9 +2,10 @@ from connexion import NoContent
|
||||
|
||||
from crc import session
|
||||
from crc.api.common import ApiError, ApiErrorSchema
|
||||
from crc.api.workflow import __get_workflow_api_model
|
||||
from crc.models.study import StudyModelSchema, StudyModel
|
||||
from crc.models.workflow import WorkflowModel, WorkflowModelSchema, WorkflowSpecModel
|
||||
from crc.workflow_processor import WorkflowProcessor
|
||||
from crc.models.workflow import WorkflowModel, WorkflowApiSchema, WorkflowSpecModel
|
||||
from crc.workflow_processor import Workflow, WorkflowProcessor
|
||||
|
||||
|
||||
def all_studies():
|
||||
@ -19,7 +20,11 @@ def add_study(body):
|
||||
session.commit()
|
||||
# FIXME: We need to ask the protocol builder what workflows to add to the study, not just add them all.
|
||||
for spec in session.query(WorkflowSpecModel).all():
|
||||
workflow = __get_workflow_instance(study.id, spec)
|
||||
processor = WorkflowProcessor.create(spec.id)
|
||||
workflow = WorkflowModel(bpmn_workflow_json=processor.serialize(),
|
||||
status=processor.get_status(),
|
||||
study_id=study.id,
|
||||
workflow_spec_id=spec.id)
|
||||
session.add(workflow)
|
||||
session.commit()
|
||||
return StudyModelSchema().dump(study)
|
||||
@ -56,9 +61,14 @@ def post_update_study_from_protocol_builder(study_id):
|
||||
|
||||
|
||||
def get_study_workflows(study_id):
|
||||
workflows = session.query(WorkflowModel).filter_by(study_id=study_id).all()
|
||||
schema = WorkflowModelSchema(many=True)
|
||||
return schema.dump(workflows)
|
||||
workflow_models = session.query(WorkflowModel).filter_by(study_id=study_id).all()
|
||||
api_models = []
|
||||
for workflow_model in workflow_models:
|
||||
processor = WorkflowProcessor(workflow_model.workflow_spec_id,
|
||||
workflow_model.bpmn_workflow_json)
|
||||
api_models.append( __get_workflow_api_model(workflow_model, processor))
|
||||
schema = WorkflowApiSchema(many=True)
|
||||
return schema.dump(api_models)
|
||||
|
||||
|
||||
def add_workflow_to_study(study_id, body):
|
||||
@ -66,15 +76,12 @@ def add_workflow_to_study(study_id, body):
|
||||
if workflow_spec_model is None:
|
||||
error = ApiError('unknown_spec', 'The specification "' + body['id'] + '" is not recognized.')
|
||||
return ApiErrorSchema.dump(error), 404
|
||||
workflow = __get_workflow_instance(study_id, workflow_spec_model)
|
||||
session.add(workflow)
|
||||
session.commit()
|
||||
return WorkflowModelSchema().dump(workflow)
|
||||
|
||||
def __get_workflow_instance(study_id, workflow_spec_model):
|
||||
processor = WorkflowProcessor.create(workflow_spec_model.id)
|
||||
workflow = WorkflowModel(bpmn_workflow_json=processor.serialize(),
|
||||
status=processor.get_status(),
|
||||
study_id=study_id,
|
||||
workflow_spec_id=workflow_spec_model.id)
|
||||
return workflow
|
||||
session.add(workflow)
|
||||
session.commit()
|
||||
return WorkflowApiSchema().dump(__get_workflow_api_model(workflow, processor))
|
||||
|
||||
|
@ -1,10 +1,12 @@
|
||||
import uuid
|
||||
|
||||
from flask import json
|
||||
|
||||
from crc.api.file import delete_file
|
||||
from crc import session
|
||||
from crc.api.common import ApiError, ApiErrorSchema
|
||||
from crc.models.workflow import WorkflowModel, WorkflowModelSchema, WorkflowSpecModelSchema, WorkflowSpecModel, \
|
||||
Task, TaskSchema
|
||||
from crc.models.workflow import WorkflowModel, WorkflowSpecModelSchema, WorkflowSpecModel, \
|
||||
Task, TaskSchema, WorkflowApiSchema, WorkflowApi
|
||||
from crc.workflow_processor import WorkflowProcessor
|
||||
from crc.models.file import FileModel
|
||||
|
||||
@ -65,37 +67,26 @@ def delete_workflow_specification(spec_id):
|
||||
session.commit()
|
||||
|
||||
|
||||
def __get_workflow_api_model(workflow_model: WorkflowModel, processor: WorkflowProcessor):
|
||||
spiff_tasks = processor.get_all_user_tasks()
|
||||
user_tasks = map(Task.from_spiff, spiff_tasks)
|
||||
return WorkflowApi(id=workflow_model.id, status=workflow_model.status,
|
||||
last_task=Task.from_spiff(processor.bpmn_workflow.last_task),
|
||||
next_task=Task.from_spiff(processor.next_task()),
|
||||
user_tasks=user_tasks)
|
||||
|
||||
|
||||
def get_workflow(workflow_id):
|
||||
schema = WorkflowModelSchema()
|
||||
workflow = session.query(WorkflowModel).filter_by(id=workflow_id).first()
|
||||
return schema.dump(workflow)
|
||||
schema = WorkflowApiSchema()
|
||||
workflow_model = session.query(WorkflowModel).filter_by(id=workflow_id).first()
|
||||
processor = WorkflowProcessor(workflow_model.workflow_spec_id,
|
||||
workflow_model.bpmn_workflow_json)
|
||||
return schema.dump(__get_workflow_api_model(workflow_model, processor))
|
||||
|
||||
|
||||
def delete(workflow_id):
|
||||
session.query(WorkflowModel).filter_by(id=workflow_id).delete()
|
||||
session.commit()
|
||||
3
|
||||
|
||||
|
||||
def get_all_tasks(workflow_id):
|
||||
workflow = session.query(WorkflowModel).filter_by(id=workflow_id).first()
|
||||
processor = WorkflowProcessor(workflow.workflow_spec_id, workflow.bpmn_workflow_json)
|
||||
spiff_tasks = processor.get_all_user_tasks()
|
||||
tasks = []
|
||||
for st in spiff_tasks:
|
||||
tasks.append(Task.from_spiff(st))
|
||||
return TaskSchema(many=True).dump(tasks)
|
||||
|
||||
|
||||
def get_ready_user_tasks(workflow_id):
|
||||
workflow = session.query(WorkflowModel).filter_by(id=workflow_id).first()
|
||||
processor = WorkflowProcessor(workflow.workflow_spec_id, workflow.bpmn_workflow_json)
|
||||
spiff_tasks = processor.get_ready_user_tasks()
|
||||
tasks = []
|
||||
for st in spiff_tasks:
|
||||
tasks.append(Task.from_spiff(st))
|
||||
return TaskSchema(many=True).dump(tasks)
|
||||
|
||||
|
||||
def get_task(workflow_id, task_id):
|
||||
workflow = session.query(WorkflowModel).filter_by(id=workflow_id).first()
|
||||
@ -103,14 +94,16 @@ def get_task(workflow_id, task_id):
|
||||
|
||||
|
||||
def update_task(workflow_id, task_id, body):
|
||||
workflow = session.query(WorkflowModel).filter_by(id=workflow_id).first()
|
||||
processor = WorkflowProcessor(workflow.workflow_spec_id, workflow.bpmn_workflow_json)
|
||||
workflow_model = session.query(WorkflowModel).filter_by(id=workflow_id).first()
|
||||
processor = WorkflowProcessor(workflow_model.workflow_spec_id, workflow_model.bpmn_workflow_json)
|
||||
task_id = uuid.UUID(task_id)
|
||||
task = processor.bpmn_workflow.get_task(task_id)
|
||||
task.data = body
|
||||
processor.complete_task(task)
|
||||
processor.do_engine_steps()
|
||||
workflow.bpmn_workflow_json = processor.serialize()
|
||||
session.add(workflow)
|
||||
workflow_model.last_completed_task_id = task.id
|
||||
workflow_model.bpmn_workflow_json = processor.serialize()
|
||||
session.add(workflow_model)
|
||||
session.commit()
|
||||
return WorkflowModelSchema().dump(workflow)
|
||||
return WorkflowApiSchema().dump(__get_workflow_api_model(workflow_model, processor)
|
||||
)
|
||||
|
@ -1,6 +1,7 @@
|
||||
import enum
|
||||
|
||||
import marshmallow
|
||||
from marshmallow import post_dump, pre_dump, EXCLUDE, INCLUDE
|
||||
from marshmallow_enum import EnumField
|
||||
from marshmallow_sqlalchemy import ModelSchema
|
||||
|
||||
@ -15,7 +16,6 @@ class WorkflowSpecModel(db.Model):
|
||||
description = db.Column(db.Text)
|
||||
primary_process_id = db.Column(db.String)
|
||||
|
||||
|
||||
class WorkflowSpecModelSchema(ModelSchema):
|
||||
class Meta:
|
||||
model = WorkflowSpecModel
|
||||
@ -35,17 +35,10 @@ class WorkflowModel(db.Model):
|
||||
status = db.Column(db.Enum(WorkflowStatus))
|
||||
study_id = db.Column(db.Integer, db.ForeignKey('study.id'))
|
||||
workflow_spec_id = db.Column(db.String, db.ForeignKey('workflow_spec.id'))
|
||||
last_completed_task_id = db.Column(db.String)
|
||||
|
||||
|
||||
class WorkflowModelSchema(ModelSchema):
|
||||
class Meta:
|
||||
model = WorkflowModel
|
||||
include_fk = True # Includes foreign keys
|
||||
|
||||
status = EnumField(WorkflowStatus)
|
||||
|
||||
|
||||
class Task:
|
||||
class Task(object):
|
||||
def __init__(self, id, name, title, type, state, form, documentation, data):
|
||||
self.id = id
|
||||
self.name = name
|
||||
@ -61,13 +54,12 @@ class Task:
|
||||
instance = cls(spiff_task.id,
|
||||
spiff_task.task_spec.name,
|
||||
spiff_task.task_spec.description,
|
||||
"task",
|
||||
spiff_task.task_spec.__class__.__name__,
|
||||
spiff_task.get_state_name(),
|
||||
{},
|
||||
None,
|
||||
spiff_task.task_spec.documentation,
|
||||
spiff_task.data)
|
||||
if hasattr(spiff_task.task_spec, "form"):
|
||||
instance.type = "form"
|
||||
instance.form = spiff_task.task_spec.form
|
||||
return instance
|
||||
|
||||
@ -108,8 +100,32 @@ class TaskSchema(ma.Schema):
|
||||
fields = ["id", "name", "title", "type", "state", "form", "documentation", "data"]
|
||||
|
||||
documentation = marshmallow.fields.String(required=False, allow_none=True)
|
||||
form = marshmallow.fields.Nested(FormSchema)
|
||||
form = marshmallow.fields.Nested(FormSchema, required=False, allow_none=True)
|
||||
title = marshmallow.fields.String(required=False, allow_none=True)
|
||||
|
||||
@marshmallow.post_load
|
||||
def make_task(self, data, **kwargs):
|
||||
return Task(**data)
|
||||
|
||||
|
||||
class WorkflowApi(object):
|
||||
def __init__(self, id, status, user_tasks, last_task, next_task):
|
||||
self.id = id
|
||||
self.status = status
|
||||
self.user_tasks = user_tasks
|
||||
self.last_task = last_task
|
||||
self.next_task = next_task
|
||||
|
||||
class WorkflowApiSchema(ma.Schema):
|
||||
class Meta:
|
||||
model = WorkflowApi
|
||||
fields = ["id", "status", "user_tasks", "last_task", "next_task"]
|
||||
unknown = INCLUDE
|
||||
status = EnumField(WorkflowStatus)
|
||||
user_tasks = marshmallow.fields.List(marshmallow.fields.Nested(TaskSchema, dump_only=True))
|
||||
last_task = marshmallow.fields.Nested(TaskSchema, dump_only=True)
|
||||
next_task = marshmallow.fields.Nested(TaskSchema, dump_only=True)
|
||||
|
||||
@marshmallow.post_load
|
||||
def make_workflow(self, data, **kwargs):
|
||||
return WorkflowApi(**data)
|
@ -1,9 +1,7 @@
|
||||
import xml.etree.ElementTree as ElementTree
|
||||
|
||||
from SpiffWorkflow import Task as SpiffTask
|
||||
from SpiffWorkflow import Task as SpiffTask, Workflow
|
||||
from SpiffWorkflow.bpmn.BpmnScriptEngine import BpmnScriptEngine
|
||||
from SpiffWorkflow.bpmn.parser.task_parsers import UserTaskParser
|
||||
from SpiffWorkflow.bpmn.parser.util import full_tag
|
||||
from SpiffWorkflow.bpmn.serializer.BpmnSerializer import BpmnSerializer
|
||||
from SpiffWorkflow.bpmn.workflow import BpmnWorkflow
|
||||
from SpiffWorkflow.camunda.parser.CamundaParser import CamundaParser
|
||||
@ -54,6 +52,7 @@ class CustomBpmnScriptEngine(BpmnScriptEngine):
|
||||
details=str(ne))
|
||||
|
||||
|
||||
|
||||
class MyCustomParser(BpmnDmnParser):
|
||||
"""
|
||||
A BPMN and DMN parser that can also parse Camunda forms.
|
||||
@ -61,8 +60,7 @@ class MyCustomParser(BpmnDmnParser):
|
||||
OVERRIDE_PARSER_CLASSES = BpmnDmnParser.OVERRIDE_PARSER_CLASSES
|
||||
OVERRIDE_PARSER_CLASSES.update(CamundaParser.OVERRIDE_PARSER_CLASSES)
|
||||
|
||||
|
||||
class WorkflowProcessor:
|
||||
class WorkflowProcessor(object):
|
||||
_script_engine = CustomBpmnScriptEngine()
|
||||
_serializer = BpmnSerializer()
|
||||
|
||||
@ -96,6 +94,8 @@ class WorkflowProcessor:
|
||||
raise(Exception("There is no primary BPMN model defined for workflow %s" % workflow_spec_id))
|
||||
return parser.get_spec(process_id)
|
||||
|
||||
|
||||
|
||||
@classmethod
|
||||
def create(cls, workflow_spec_id):
|
||||
spec = WorkflowProcessor.get_spec(workflow_spec_id)
|
||||
@ -123,6 +123,21 @@ class WorkflowProcessor:
|
||||
def next_user_tasks(self):
|
||||
return self.bpmn_workflow.get_ready_user_tasks()
|
||||
|
||||
def next_task(self):
|
||||
"""Returns the next user task that should be completed
|
||||
even if there are parallel tasks and mulitple options are
|
||||
available."""
|
||||
ready_tasks = self.bpmn_workflow.get_ready_user_tasks()
|
||||
if len(ready_tasks) == 0:
|
||||
return None
|
||||
elif len(ready_tasks) == 1:
|
||||
return ready_tasks[0]
|
||||
else:
|
||||
for task in ready_tasks:
|
||||
if task.parent == self.bpmn_workflow.last_task:
|
||||
return task;
|
||||
return ready_tasks[0]
|
||||
|
||||
def complete_task(self, task):
|
||||
self.bpmn_workflow.complete_task_from_id(task.id)
|
||||
|
||||
|
@ -5,7 +5,7 @@ from crc import session
|
||||
from crc.models.file import FileModel
|
||||
from crc.models.study import StudyModel, StudyModelSchema, ProtocolBuilderStatus
|
||||
from crc.models.workflow import WorkflowSpecModel, WorkflowSpecModelSchema, WorkflowModel, WorkflowStatus, \
|
||||
WorkflowModelSchema, TaskSchema
|
||||
WorkflowApiSchema
|
||||
from tests.base_test import BaseTest
|
||||
|
||||
|
||||
@ -162,15 +162,15 @@ class TestStudy(BaseTest):
|
||||
data=json.dumps(WorkflowSpecModelSchema().dump(spec)))
|
||||
self.assert_success(rv)
|
||||
self.assertEqual(1, session.query(WorkflowModel).count())
|
||||
workflow = session.query(WorkflowModel).first()
|
||||
self.assertEqual(study.id, workflow.study_id)
|
||||
self.assertEqual(WorkflowStatus.user_input_required, workflow.status)
|
||||
self.assertIsNotNone(workflow.bpmn_workflow_json)
|
||||
self.assertEqual(spec.id, workflow.workflow_spec_id)
|
||||
workflow_model = session.query(WorkflowModel).first()
|
||||
self.assertEqual(study.id, workflow_model.study_id)
|
||||
self.assertEqual(WorkflowStatus.user_input_required, workflow_model.status)
|
||||
self.assertIsNotNone(workflow_model.bpmn_workflow_json)
|
||||
self.assertEqual(spec.id, workflow_model.workflow_spec_id)
|
||||
|
||||
json_data = json.loads(rv.get_data(as_text=True))
|
||||
workflow2 = WorkflowModelSchema().load(json_data, session=session)
|
||||
self.assertEqual(workflow.id, workflow2.id)
|
||||
workflow2 = WorkflowApiSchema().load(json_data)
|
||||
self.assertEqual(workflow_model.id, workflow2.id)
|
||||
|
||||
def test_delete_workflow(self):
|
||||
self.load_example_data()
|
||||
@ -180,8 +180,7 @@ class TestStudy(BaseTest):
|
||||
data=json.dumps(WorkflowSpecModelSchema().dump(spec)))
|
||||
self.assertEqual(1, session.query(WorkflowModel).count())
|
||||
json_data = json.loads(rv.get_data(as_text=True))
|
||||
workflow = WorkflowModelSchema().load(json_data, session=session)
|
||||
workflow = WorkflowApiSchema().load(json_data)
|
||||
rv = self.app.delete('/v1.0/workflow/%i' % workflow.id)
|
||||
self.assert_success(rv)
|
||||
self.assertEqual(0, session.query(WorkflowModel).count())
|
||||
|
||||
|
@ -4,8 +4,8 @@ from datetime import datetime
|
||||
from crc import session
|
||||
from crc.models.file import FileModel
|
||||
from crc.models.study import StudyModel, StudyModelSchema, ProtocolBuilderStatus
|
||||
from crc.models.workflow import WorkflowSpecModel, WorkflowSpecModelSchema, WorkflowModel, WorkflowStatus, \
|
||||
WorkflowModelSchema, TaskSchema
|
||||
from crc.models.workflow import WorkflowSpecModel, WorkflowSpecModelSchema, WorkflowModel, \
|
||||
WorkflowStatus, TaskSchema, WorkflowApiSchema
|
||||
from tests.base_test import BaseTest
|
||||
|
||||
|
||||
@ -19,18 +19,11 @@ class TestTasksApi(BaseTest):
|
||||
workflow = session.query(WorkflowModel).filter_by(study_id = study.id, workflow_spec_id=workflow_name).first()
|
||||
return workflow
|
||||
|
||||
def get_tasks(self, workflow):
|
||||
rv = self.app.get('/v1.0/workflow/%i/tasks' % workflow.id, content_type="application/json")
|
||||
def get_workflow_api(self, workflow):
|
||||
rv = self.app.get('/v1.0/workflow/%i' % workflow.id, content_type="application/json")
|
||||
json_data = json.loads(rv.get_data(as_text=True))
|
||||
tasks = TaskSchema(many=True).load(json_data)
|
||||
return tasks
|
||||
|
||||
def get_all_tasks(self, workflow):
|
||||
rv = self.app.get('/v1.0/workflow/%i/all_tasks' % workflow.id, content_type="application/json")
|
||||
self.assert_success(rv)
|
||||
json_data = json.loads(rv.get_data(as_text=True))
|
||||
all_tasks = TaskSchema(many=True).load(json_data)
|
||||
return all_tasks
|
||||
workflow_api = WorkflowApiSchema().load(json_data)
|
||||
return workflow_api
|
||||
|
||||
def complete_form(self, workflow, task, dict_data):
|
||||
rv = self.app.put('/v1.0/workflow/%i/task/%s/data' % (workflow.id, task.id),
|
||||
@ -38,13 +31,13 @@ class TestTasksApi(BaseTest):
|
||||
data=json.dumps(dict_data))
|
||||
self.assert_success(rv)
|
||||
json_data = json.loads(rv.get_data(as_text=True))
|
||||
workflow = WorkflowModelSchema().load(json_data, session=session)
|
||||
workflow = WorkflowApiSchema().load(json_data)
|
||||
return workflow
|
||||
|
||||
def test_get_current_user_tasks(self):
|
||||
self.load_example_data()
|
||||
workflow = self.create_workflow('random_fact')
|
||||
tasks = self.get_tasks(workflow)
|
||||
tasks = self.get_workflow_api(workflow).user_tasks
|
||||
self.assertEqual("Task_User_Select_Type", tasks[0].name)
|
||||
self.assertEqual(3, len(tasks[0].form["fields"][0]["options"]))
|
||||
|
||||
@ -53,22 +46,22 @@ class TestTasksApi(BaseTest):
|
||||
self.load_example_data()
|
||||
workflow = self.create_workflow('two_forms')
|
||||
# get the first form in the two form workflow.
|
||||
tasks = self.get_tasks(workflow)
|
||||
self.assertEqual(1, len(tasks))
|
||||
self.assertIsNotNone(tasks[0].form)
|
||||
self.assertEqual("StepOne", tasks[0].name)
|
||||
self.assertEqual(1, len(tasks[0].form['fields']))
|
||||
workflow_api = self.get_workflow_api(workflow)
|
||||
self.assertEqual(2, len(workflow_api.user_tasks))
|
||||
self.assertIsNotNone(workflow_api.user_tasks[0].form)
|
||||
self.assertEqual("UserTask", workflow_api.next_task['type'])
|
||||
self.assertEqual("StepOne", workflow_api.next_task['name'])
|
||||
self.assertEqual(1, len(workflow_api.next_task['form']['fields']))
|
||||
|
||||
# Complete the form for Step one and post it.
|
||||
self.complete_form(workflow, tasks[0], {"color": "blue"})
|
||||
self.complete_form(workflow, workflow_api.user_tasks[0], {"color": "blue"})
|
||||
|
||||
# Get the next Task
|
||||
tasks = self.get_tasks(workflow)
|
||||
self.assertEqual("StepTwo", tasks[0].name)
|
||||
workflow_api = self.get_workflow_api(workflow)
|
||||
self.assertEqual("StepTwo", workflow_api.next_task['name'])
|
||||
|
||||
# Get all user Tasks and check that the data have been saved
|
||||
all_tasks = self.get_all_tasks(workflow)
|
||||
for task in all_tasks:
|
||||
for task in workflow_api.user_tasks:
|
||||
self.assertIsNotNone(task.data)
|
||||
for val in task.data.values():
|
||||
self.assertIsNotNone(val)
|
||||
@ -78,18 +71,40 @@ class TestTasksApi(BaseTest):
|
||||
workflow = self.create_workflow('exclusive_gateway')
|
||||
|
||||
# get the first form in the two form workflow.
|
||||
tasks = self.get_tasks(workflow)
|
||||
tasks = self.get_workflow_api(workflow).user_tasks
|
||||
self.complete_form(workflow, tasks[0], {"has_bananas": True})
|
||||
|
||||
|
||||
def test_workflow_with_parallel_forms(self):
|
||||
self.load_example_data()
|
||||
workflow = self.create_workflow('exclusive_gateway')
|
||||
|
||||
# get the first form in the two form workflow.
|
||||
tasks = self.get_tasks(workflow)
|
||||
tasks = self.get_workflow_api(workflow).user_tasks
|
||||
self.complete_form(workflow, tasks[0], {"has_bananas": True})
|
||||
|
||||
# Get the next Task
|
||||
tasks = self.get_tasks(workflow)
|
||||
self.assertEqual("Task_Num_Bananas", tasks[0].name)
|
||||
workflow_api = self.get_workflow_api(workflow)
|
||||
self.assertEqual("Task_Num_Bananas", workflow_api.next_task['name'])
|
||||
|
||||
def test_get_workflow_contains_details_about_last_task_data(self):
|
||||
self.load_example_data()
|
||||
workflow = self.create_workflow('exclusive_gateway')
|
||||
|
||||
# get the first form in the two form workflow.
|
||||
tasks = self.get_workflow_api(workflow).user_tasks
|
||||
workflow_api = self.complete_form(workflow, tasks[0], {"has_bananas": True})
|
||||
|
||||
self.assertIsNotNone(workflow_api.last_task)
|
||||
self.assertEquals({"has_bananas": True}, workflow_api.last_task['data'])
|
||||
|
||||
def test_get_workflow_contains_reference_to_last_task_and_next_task(self):
|
||||
self.load_example_data()
|
||||
workflow = self.create_workflow('exclusive_gateway')
|
||||
|
||||
# get the first form in the two form workflow.
|
||||
tasks = self.get_workflow_api(workflow).user_tasks
|
||||
self.complete_form(workflow, tasks[0], {"has_bananas": True})
|
||||
|
||||
workflow_api = self.get_workflow_api(workflow)
|
||||
self.assertIsNotNone(workflow_api.last_task)
|
||||
self.assertIsNotNone(workflow_api.next_task)
|
||||
|
@ -6,7 +6,7 @@ from crc.api.rest_exception import RestException
|
||||
from crc.models.file import FileModel
|
||||
from crc.models.workflow import WorkflowSpecModel, WorkflowStatus
|
||||
from tests.base_test import BaseTest
|
||||
from crc.workflow_processor import WorkflowProcessor
|
||||
from crc.workflow_processor import Workflow, WorkflowProcessor
|
||||
|
||||
|
||||
class TestWorkflowProcessor(BaseTest):
|
||||
@ -17,7 +17,7 @@ class TestWorkflowProcessor(BaseTest):
|
||||
letters = string.ascii_lowercase
|
||||
return ''.join(random.choice(letters) for i in range(stringLength))
|
||||
|
||||
def _complete_form_with_random_data(self, task):
|
||||
def _populate_form_with_random_data(self, task):
|
||||
form_data = {}
|
||||
for field in task.task_spec.form.fields:
|
||||
form_data[field.id] = self._randomString()
|
||||
@ -79,10 +79,10 @@ class TestWorkflowProcessor(BaseTest):
|
||||
self.assertEqual(WorkflowStatus.user_input_required, processor.get_status())
|
||||
next_user_tasks = processor.next_user_tasks()
|
||||
self.assertEqual(4, len(next_user_tasks))
|
||||
self._complete_form_with_random_data(next_user_tasks[0])
|
||||
self._complete_form_with_random_data(next_user_tasks[1])
|
||||
self._complete_form_with_random_data(next_user_tasks[2])
|
||||
self._complete_form_with_random_data(next_user_tasks[3])
|
||||
self._populate_form_with_random_data(next_user_tasks[0])
|
||||
self._populate_form_with_random_data(next_user_tasks[1])
|
||||
self._populate_form_with_random_data(next_user_tasks[2])
|
||||
self._populate_form_with_random_data(next_user_tasks[3])
|
||||
processor.complete_task(next_user_tasks[0])
|
||||
processor.complete_task(next_user_tasks[1])
|
||||
processor.complete_task(next_user_tasks[2])
|
||||
@ -90,10 +90,10 @@ class TestWorkflowProcessor(BaseTest):
|
||||
# There are another 4 tasks to complete (each task, had a follow up task in the parallel list)
|
||||
next_user_tasks = processor.next_user_tasks()
|
||||
self.assertEqual(4, len(next_user_tasks))
|
||||
self._complete_form_with_random_data(next_user_tasks[0])
|
||||
self._complete_form_with_random_data(next_user_tasks[1])
|
||||
self._complete_form_with_random_data(next_user_tasks[2])
|
||||
self._complete_form_with_random_data(next_user_tasks[3])
|
||||
self._populate_form_with_random_data(next_user_tasks[0])
|
||||
self._populate_form_with_random_data(next_user_tasks[1])
|
||||
self._populate_form_with_random_data(next_user_tasks[2])
|
||||
self._populate_form_with_random_data(next_user_tasks[3])
|
||||
processor.complete_task(next_user_tasks[0])
|
||||
processor.complete_task(next_user_tasks[1])
|
||||
processor.complete_task(next_user_tasks[2])
|
||||
@ -101,13 +101,34 @@ class TestWorkflowProcessor(BaseTest):
|
||||
processor.do_engine_steps()
|
||||
self.assertTrue(processor.bpmn_workflow.is_completed())
|
||||
|
||||
def test_workflow_processor_knows_the_text_task_even_when_parallel(self):
|
||||
self.load_example_data()
|
||||
workflow_spec_model = session.query(WorkflowSpecModel).filter_by(id="parallel_tasks").first()
|
||||
processor = WorkflowProcessor.create(workflow_spec_model.id)
|
||||
self.assertEqual(WorkflowStatus.user_input_required, processor.get_status())
|
||||
next_user_tasks = processor.next_user_tasks()
|
||||
self.assertEqual(4, len(next_user_tasks))
|
||||
self.assertEqual(next_user_tasks[0], processor.next_task(), "First task in list of 4")
|
||||
|
||||
# Complete the third open task, so do things out of order
|
||||
# this should cause the system to recommend the first ready task that is a
|
||||
# child of the last completed task.
|
||||
task = next_user_tasks[2]
|
||||
self._populate_form_with_random_data(task)
|
||||
processor.complete_task(task)
|
||||
next_user_tasks = processor.next_user_tasks()
|
||||
self.assertEqual(processor.bpmn_workflow.last_task, task)
|
||||
self.assertEqual(4, len(next_user_tasks))
|
||||
self.assertEqual(task.children[0], processor.next_task())
|
||||
|
||||
|
||||
def test_workflow_with_bad_expression_raises_sensible_error(self):
|
||||
workflow_spec_model = self.load_test_spec("invalid_expression")
|
||||
processor = WorkflowProcessor.create(workflow_spec_model.id)
|
||||
processor.do_engine_steps()
|
||||
next_user_tasks = processor.next_user_tasks()
|
||||
self.assertEqual(1, len(next_user_tasks))
|
||||
self._complete_form_with_random_data(next_user_tasks[0])
|
||||
self._populate_form_with_random_data(next_user_tasks[0])
|
||||
processor.complete_task(next_user_tasks[0])
|
||||
with self.assertRaises(RestException) as context:
|
||||
processor.do_engine_steps()
|
||||
|
Loading…
x
Reference in New Issue
Block a user