Adds status spec when adding a study, and adds/removes workflows from study based on output data from status spec.

This commit is contained in:
Aaron Louie 2020-03-15 15:54:13 -04:00
parent e4af6f77d0
commit bdd07685c6
10 changed files with 191 additions and 113 deletions

View File

@ -23,16 +23,46 @@ def all_studies():
@auth.login_required
def add_study(body):
study = StudyModelSchema().load(body, session=session)
study: StudyModel = StudyModelSchema().load(body, session=session)
status_spec = __get_status_spec(study.status_spec_id)
# Get latest status spec version
if status_spec is not None:
study.status_spec_id = status_spec.id
study.status_spec_version = WorkflowProcessor.get_latest_version_string(status_spec.id)
session.add(study)
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():
WorkflowProcessor.create(study.id, spec.id)
__add_study_workflows_from_status(study.id, status_spec)
return StudyModelSchema().dump(study)
def __get_status_spec(status_spec_id):
if status_spec_id is None:
return session.query(WorkflowSpecModel).filter_by(is_status=True).first()
else:
return session.query(WorkflowSpecModel).filter_by(id=status_spec_id).first()
def __add_study_workflows_from_status(study_id, status_spec):
all_specs = session.query(WorkflowSpecModel).all()
if status_spec is not None:
# Run status spec to get list of workflow specs applicable to this study
status_processor = WorkflowProcessor.create(study_id, status_spec)
status_processor.do_engine_steps()
status_data = status_processor.next_task().data
# Only add workflow specs listed in status spec
for spec in all_specs:
if spec.id in status_data and status_data[spec.id]:
WorkflowProcessor.create(study_id, spec.id)
else:
# No status spec. Just add all workflows.
for spec in all_specs:
WorkflowProcessor.create(study_id, spec.id)
@auth.login_required
def update_study(study_id, body):
if study_id is None:
@ -130,12 +160,43 @@ def post_update_study_from_protocol_builder(study_id):
@auth.login_required
def get_study_workflows(study_id):
workflow_models = session.query(WorkflowModel).filter_by(study_id=study_id).all()
# Get study
study: StudyModel = session.query(StudyModel).filter_by(id=study_id).first()
# Get study status spec
status_spec: WorkflowSpecModel = session.query(WorkflowSpecModel)\
.filter_by(is_status=True).first()
status_data = None
if status_spec is not None:
# Run status spec
status_workflow_model: WorkflowModel = session.query(WorkflowModel)\
.filter_by(study_id=study.id)\
.filter_by(workflow_spec_id=status_spec.id)\
.first()
status_processor = WorkflowProcessor(status_workflow_model)
# Get list of active workflow specs for study
status_processor.do_engine_steps()
status_data = status_processor.bpmn_workflow.last_task.data
# Get study workflows
workflow_models = session.query(WorkflowModel)\
.filter_by(study_id=study_id)\
.filter(WorkflowModel.workflow_spec_id != status_spec.id)\
.all()
else:
# Get study 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_model.bpmn_workflow_json)
api_models.append(__get_workflow_api_model(processor))
api_models.append(__get_workflow_api_model(processor, status_data))
schema = WorkflowApiSchema(many=True)
return schema.dump(api_models)
@ -193,3 +254,4 @@ def map_pb_study_to_study(pb_study):
study_info['inactive'] = False
return study_info

View File

@ -17,7 +17,8 @@ def all_specifications():
@auth.login_required
def add_workflow_specification(body):
new_spec = WorkflowSpecModelSchema().load(body, session=session)
new_spec: WorkflowSpecModel = WorkflowSpecModelSchema().load(body, session=session)
new_spec.is_status = new_spec.id == 'status'
session.add(new_spec)
session.commit()
return WorkflowSpecModelSchema().dump(new_spec)
@ -69,9 +70,14 @@ def delete_workflow_specification(spec_id):
session.commit()
def __get_workflow_api_model(processor: WorkflowProcessor):
def __get_workflow_api_model(processor: WorkflowProcessor, status_data=None):
spiff_tasks = processor.get_all_user_tasks()
user_tasks = list(map(Task.from_spiff, spiff_tasks))
is_active = True
if status_data is not None and processor.workflow_spec_id in status_data:
is_active = status_data[processor.workflow_spec_id]
workflow_api = WorkflowApi(
id=processor.get_workflow_id(),
status=processor.get_status(),
@ -80,7 +86,8 @@ def __get_workflow_api_model(processor: WorkflowProcessor):
user_tasks=user_tasks,
workflow_spec_id=processor.workflow_spec_id,
spec_version=processor.get_spec_version(),
is_latest_spec=processor.get_spec_version() == processor.get_latest_version_string(processor.workflow_spec_id)
is_latest_spec=processor.get_spec_version() == processor.get_latest_version_string(processor.workflow_spec_id),
is_active=is_active
)
if processor.next_task():
workflow_api.next_task = Task.from_spiff(processor.next_task())
@ -89,9 +96,8 @@ def __get_workflow_api_model(processor: WorkflowProcessor):
@auth.login_required
def get_workflow(workflow_id, soft_reset=False, hard_reset=False):
workflow_model = session.query(WorkflowModel).filter_by(id=workflow_id).first()
workflow_model: WorkflowModel = session.query(WorkflowModel).filter_by(id=workflow_id).first()
processor = WorkflowProcessor(workflow_model, soft_reset=soft_reset, hard_reset=hard_reset)
workflow_api_model = __get_workflow_api_model(processor)
update_workflow_stats(workflow_model, workflow_api_model)
return WorkflowApiSchema().dump(workflow_api_model)

View File

@ -82,11 +82,11 @@ class ExampleDataLoader:
returns an array of data models to be added to the database."""
global file
file_service = FileService()
spec = WorkflowSpecModel(id=id,
name=name,
display_name=display_name,
description=description)
description=description,
is_status=id == 'status')
db.session.add(spec)
db.session.commit()
if not filepath:
@ -96,7 +96,7 @@ class ExampleDataLoader:
noise, file_extension = os.path.splitext(file_path)
filename = os.path.basename(file_path)
is_status = filename.lower() == 'status'
is_status = filename.lower() == 'status.bpmn'
is_primary = filename.lower() == id + '.bpmn'
try:
file = open(file_path, 'rb')

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/DMN/20151101/dmn.xsd" xmlns:biodi="http://bpmn.io/schema/dmn/biodi/1.0" id="Definitions_1p34ouw" name="DRD" namespace="http://camunda.org/schema/1.0/dmn" exporter="Camunda Modeler" exporterVersion="3.7.0">
<decision id="should_enable_pb_responses" name="should_enable_pb_responses">
<decision id="should_enable_crc2_training_session_data_security_plan" name="should_enable_crc2_training_session_data_security_plan">
<extensionElements>
<biodi:bounds x="190" y="80" width="180" height="80" />
</extensionElements>
@ -8,11 +8,19 @@
<input id="InputClause_18pwfqu" label="some_input">
<inputExpression id="LiteralExpression_1y84stb" typeRef="boolean" expressionLanguage="feel" />
</input>
<output id="OutputClause_05y0j7c" label="pb_responses" name="pb_responses" typeRef="boolean" />
<input id="InputClause_0ahp5b9">
<inputExpression id="LiteralExpression_1ptkp9l" typeRef="string">
<text></text>
</inputExpression>
</input>
<output id="OutputClause_05y0j7c" label="crc2_training_session_data_security_plan" name="crc2_training_session_data_security_plan" typeRef="boolean" />
<rule id="DecisionRule_17xsr74">
<inputEntry id="UnaryTests_05ldcq4">
<text>false</text>
</inputEntry>
<inputEntry id="UnaryTests_1ebers6">
<text></text>
</inputEntry>
<outputEntry id="LiteralExpression_09oao3s">
<text>false</text>
</outputEntry>
@ -21,6 +29,9 @@
<inputEntry id="UnaryTests_09xdkib">
<text>true</text>
</inputEntry>
<inputEntry id="UnaryTests_1hqb6bs">
<text></text>
</inputEntry>
<outputEntry id="LiteralExpression_0y2v9zc">
<text>true</text>
</outputEntry>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/DMN/20151101/dmn.xsd" xmlns:biodi="http://bpmn.io/schema/dmn/biodi/1.0" id="Definitions_1p34ouw" name="DRD" namespace="http://camunda.org/schema/1.0/dmn" exporter="Camunda Modeler" exporterVersion="3.7.0">
<decision id="should_enable_random_fact" name="should_enable_random_fact">
<decision id="should_enable_crc2_training_session_enter_core_info" name="should_enable_crc2_training_session_enter_core_info">
<extensionElements>
<biodi:bounds x="170" y="60" width="180" height="80" />
</extensionElements>
@ -10,7 +10,7 @@
<text></text>
</inputExpression>
</input>
<output id="output_1" label="random_fact" name="random_fact" typeRef="boolean" />
<output id="output_1" label="crc2_training_session_enter_core_info" name="crc2_training_session_enter_core_info" typeRef="boolean" />
<rule id="DecisionRule_10oo3ms">
<inputEntry id="UnaryTests_1ozg74s">
<text>false</text>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/DMN/20151101/dmn.xsd" xmlns:biodi="http://bpmn.io/schema/dmn/biodi/1.0" id="Definitions_1p34ouw" name="DRD" namespace="http://camunda.org/schema/1.0/dmn" exporter="Camunda Modeler" exporterVersion="3.7.0">
<decision id="should_enable_study_details" name="should_enable_study_details">
<decision id="should_enable_crc2_training_session_sponsor_funding_source" name="should_enable_crc2_training_session_sponsor_funding_source">
<extensionElements>
<biodi:bounds x="190" y="70" width="180" height="80" />
</extensionElements>
@ -8,7 +8,7 @@
<input id="InputClause_02n3ccs" label="some_input">
<inputExpression id="LiteralExpression_1ju4o1o" typeRef="boolean" expressionLanguage="feel" />
</input>
<output id="OutputClause_1ybi1ud" label="study_details" name="study_details" typeRef="boolean" />
<output id="OutputClause_1ybi1ud" label="crc2_training_session_sponsor_funding_source" name="crc2_training_session_sponsor_funding_source" typeRef="boolean" />
<rule id="DecisionRule_1t97mw4">
<inputEntry id="UnaryTests_0ym4ln2">
<text>false</text>

View File

@ -5,41 +5,33 @@
<bpmn:outgoing>SequenceFlow_1ees8ka</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:sequenceFlow id="SequenceFlow_1ees8ka" sourceRef="StartEvent_1" targetRef="Activity_00rh8pw" />
<bpmn:businessRuleTask id="Activity_1k5eeun" name="Enable pb_responses" camunda:decisionRef="should_enable_pb_responses">
<bpmn:businessRuleTask id="Activity_1k5eeun" name="Enable crc2_training_session_data_security_plan" camunda:decisionRef="should_enable_crc2_training_session_data_security_plan">
<bpmn:incoming>Flow_1nimppb</bpmn:incoming>
<bpmn:outgoing>Flow_1txrak2</bpmn:outgoing>
</bpmn:businessRuleTask>
<bpmn:businessRuleTask id="Activity_1yqy50i" name="Enable random_fact" camunda:decisionRef="should_enable_random_fact">
<bpmn:businessRuleTask id="Activity_1yqy50i" name="Enable crc2_training_session_enter_core_info" camunda:decisionRef="should_enable_crc2_training_session_enter_core_info">
<bpmn:incoming>Flow_1m8285h</bpmn:incoming>
<bpmn:outgoing>Flow_1sggkit</bpmn:outgoing>
</bpmn:businessRuleTask>
<bpmn:businessRuleTask id="Activity_16cm213" name="Enable study_details" camunda:decisionRef="should_enable_study_details">
<bpmn:businessRuleTask id="Activity_16cm213" name="Enable crc2_training_session_sponsor_funding_source" camunda:decisionRef="should_enable_crc2_training_session_sponsor_funding_source">
<bpmn:incoming>Flow_18pl92p</bpmn:incoming>
<bpmn:outgoing>Flow_0x9580l</bpmn:outgoing>
</bpmn:businessRuleTask>
<bpmn:businessRuleTask id="Activity_0rbzgsm" name="Enable two_forms" camunda:decisionRef="should_enable_two_forms">
<bpmn:incoming>Flow_03u23vt</bpmn:incoming>
<bpmn:outgoing>Flow_0pkxa8l</bpmn:outgoing>
</bpmn:businessRuleTask>
<bpmn:parallelGateway id="Gateway_1nta7st">
<bpmn:incoming>Flow_024q2cw</bpmn:incoming>
<bpmn:outgoing>Flow_1m8285h</bpmn:outgoing>
<bpmn:outgoing>Flow_1nimppb</bpmn:outgoing>
<bpmn:outgoing>Flow_03u23vt</bpmn:outgoing>
<bpmn:outgoing>Flow_18pl92p</bpmn:outgoing>
</bpmn:parallelGateway>
<bpmn:sequenceFlow id="Flow_1m8285h" sourceRef="Gateway_1nta7st" targetRef="Activity_1yqy50i" />
<bpmn:sequenceFlow id="Flow_1nimppb" sourceRef="Gateway_1nta7st" targetRef="Activity_1k5eeun" />
<bpmn:sequenceFlow id="Flow_18pl92p" sourceRef="Gateway_1nta7st" targetRef="Activity_16cm213" />
<bpmn:sequenceFlow id="Flow_03u23vt" sourceRef="Gateway_1nta7st" targetRef="Activity_0rbzgsm" />
<bpmn:parallelGateway id="Gateway_12tpgcy">
<bpmn:incoming>Flow_1txrak2</bpmn:incoming>
<bpmn:incoming>Flow_0pkxa8l</bpmn:incoming>
<bpmn:incoming>Flow_1sggkit</bpmn:incoming>
<bpmn:incoming>Flow_0x9580l</bpmn:incoming>
<bpmn:outgoing>Flow_0pwtiqm</bpmn:outgoing>
</bpmn:parallelGateway>
<bpmn:sequenceFlow id="Flow_0pkxa8l" sourceRef="Activity_0rbzgsm" targetRef="Gateway_12tpgcy" />
<bpmn:endEvent id="Event_135x8jg">
<bpmn:incoming>Flow_0pwtiqm</bpmn:incoming>
</bpmn:endEvent>
@ -61,83 +53,68 @@
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_0jhpidf">
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
<dc:Bounds x="152" y="281" width="36" height="36" />
<dc:Bounds x="152" y="211" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="SequenceFlow_1ees8ka_di" bpmnElement="SequenceFlow_1ees8ka">
<di:waypoint x="188" y="299" />
<di:waypoint x="230" y="299" />
<di:waypoint x="188" y="229" />
<di:waypoint x="230" y="229" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="Activity_1k5eeun_di" bpmnElement="Activity_1k5eeun">
<dc:Bounds x="460" y="84" width="100" height="80" />
<dc:Bounds x="460" y="189" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_1yqy50i_di" bpmnElement="Activity_1yqy50i">
<dc:Bounds x="460" y="199" width="100" height="80" />
<dc:Bounds x="460" y="80" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_16cm213_di" bpmnElement="Activity_16cm213">
<dc:Bounds x="460" y="319" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0rbzgsm_di" bpmnElement="Activity_0rbzgsm">
<dc:Bounds x="460" y="440" width="100" height="80" />
<dc:Bounds x="460" y="300" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Gateway_1m22g4p_di" bpmnElement="Gateway_1nta7st">
<dc:Bounds x="378" y="274" width="50" height="50" />
<dc:Bounds x="378" y="204" width="50" height="50" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_1m8285h_di" bpmnElement="Flow_1m8285h">
<di:waypoint x="403" y="274" />
<di:waypoint x="403" y="239" />
<di:waypoint x="460" y="239" />
<di:waypoint x="403" y="204" />
<di:waypoint x="403" y="120" />
<di:waypoint x="460" y="120" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1nimppb_di" bpmnElement="Flow_1nimppb">
<di:waypoint x="403" y="274" />
<di:waypoint x="403" y="124" />
<di:waypoint x="460" y="124" />
<di:waypoint x="428" y="229" />
<di:waypoint x="460" y="229" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_18pl92p_di" bpmnElement="Flow_18pl92p">
<di:waypoint x="403" y="324" />
<di:waypoint x="403" y="359" />
<di:waypoint x="460" y="359" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_03u23vt_di" bpmnElement="Flow_03u23vt">
<di:waypoint x="403" y="324" />
<di:waypoint x="403" y="480" />
<di:waypoint x="460" y="480" />
<di:waypoint x="403" y="254" />
<di:waypoint x="403" y="340" />
<di:waypoint x="460" y="340" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="Gateway_1kk6x70_di" bpmnElement="Gateway_12tpgcy">
<dc:Bounds x="595" y="274" width="50" height="50" />
<dc:Bounds x="595" y="204" width="50" height="50" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_0pkxa8l_di" bpmnElement="Flow_0pkxa8l">
<di:waypoint x="560" y="480" />
<di:waypoint x="620" y="480" />
<di:waypoint x="620" y="324" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="Event_135x8jg_di" bpmnElement="Event_135x8jg">
<dc:Bounds x="682" y="281" width="36" height="36" />
<dc:Bounds x="682" y="211" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_0pwtiqm_di" bpmnElement="Flow_0pwtiqm">
<di:waypoint x="645" y="299" />
<di:waypoint x="682" y="299" />
<di:waypoint x="645" y="229" />
<di:waypoint x="682" y="229" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0x9580l_di" bpmnElement="Flow_0x9580l">
<di:waypoint x="560" y="359" />
<di:waypoint x="620" y="359" />
<di:waypoint x="620" y="324" />
<di:waypoint x="560" y="340" />
<di:waypoint x="620" y="340" />
<di:waypoint x="620" y="254" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1txrak2_di" bpmnElement="Flow_1txrak2">
<di:waypoint x="560" y="124" />
<di:waypoint x="620" y="124" />
<di:waypoint x="620" y="274" />
<di:waypoint x="560" y="229" />
<di:waypoint x="595" y="229" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1sggkit_di" bpmnElement="Flow_1sggkit">
<di:waypoint x="560" y="239" />
<di:waypoint x="620" y="239" />
<di:waypoint x="620" y="274" />
<di:waypoint x="560" y="120" />
<di:waypoint x="620" y="120" />
<di:waypoint x="620" y="204" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_024q2cw_di" bpmnElement="Flow_024q2cw">
<di:waypoint x="330" y="299" />
<di:waypoint x="378" y="299" />
<di:waypoint x="330" y="229" />
<di:waypoint x="378" y="229" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="Activity_0vfs7g0_di" bpmnElement="Activity_00rh8pw">
<dc:Bounds x="230" y="259" width="100" height="80" />
<dc:Bounds x="230" y="189" width="100" height="80" />
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>

View File

@ -1,30 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/DMN/20151101/dmn.xsd" xmlns:biodi="http://bpmn.io/schema/dmn/biodi/1.0" id="Definitions_1p34ouw" name="DRD" namespace="http://camunda.org/schema/1.0/dmn" exporter="Camunda Modeler" exporterVersion="3.7.0">
<decision id="should_enable_two_forms" name="should_enable_two_forms">
<extensionElements>
<biodi:bounds x="150" y="80" width="180" height="80" />
</extensionElements>
<decisionTable id="DecisionTable_15l972l">
<input id="InputClause_0lms091" label="some_input">
<inputExpression id="LiteralExpression_0ypio3f" typeRef="boolean" expressionLanguage="feel" />
</input>
<output id="OutputClause_1f7iind" label="two_forms" name="two_forms" typeRef="boolean" />
<rule id="DecisionRule_0914coi">
<inputEntry id="UnaryTests_1efs7cx">
<text>false</text>
</inputEntry>
<outputEntry id="LiteralExpression_0utnzi4">
<text>false</text>
</outputEntry>
</rule>
<rule id="DecisionRule_0zcj74u">
<inputEntry id="UnaryTests_0v2q8t3">
<text>true</text>
</inputEntry>
<outputEntry id="LiteralExpression_1jh1u0c">
<text>true</text>
</outputEntry>
</rule>
</decisionTable>
</decision>
</definitions>

View File

@ -3,7 +3,7 @@ from datetime import datetime, timezone
from unittest.mock import patch, Mock
from crc import session
from crc.models.api_models import WorkflowApiSchema
from crc.models.api_models import WorkflowApiSchema, WorkflowApi
from crc.models.study import StudyModel, StudyModelSchema
from crc.models.protocol_builder import ProtocolBuilderStatus, ProtocolBuilderStudyDetailsSchema, \
ProtocolBuilderStudySchema
@ -160,9 +160,6 @@ class TestStudyApi(BaseTest):
rv = self.app.delete('/v1.0/study/%i' % study.id)
self.assert_failure(rv, error_code="study_integrity_error")
def test_delete_workflow(self):
self.load_example_data()
study = session.query(StudyModel).first()
@ -207,3 +204,56 @@ class TestStudyApi(BaseTest):
json_data_after = json.loads(response_after.get_data(as_text=True))
workflows_after = WorkflowApiSchema(many=True).load(json_data_after)
self.assertEqual(1, len(workflows_after))
"""
Workflow Specs that have been made available (or not) to a particular study via the status.bpmn should be flagged
as available (or not) when the list of a study's workflows is retrieved.
"""
def test_workflow_spec_status(self):
self.load_example_data()
study = session.query(StudyModel).first()
# Add status workflow
self.load_test_spec('status')
# Add status workflow to the study
status_spec = session.query(WorkflowSpecModel).filter_by(is_status=True).first()
add_status_response = self.app.post('/v1.0/study/%i/workflows' % study.id,
content_type="application/json",
headers=self.logged_in_headers(),
data=json.dumps(WorkflowSpecModelSchema().dump(status_spec)))
self.assert_success(add_status_response)
json_data_status = json.loads(add_status_response.get_data(as_text=True))
status_workflow: WorkflowApi = WorkflowApiSchema().load(json_data_status)
status_task_id = status_workflow.next_task['id']
# Add all available non-status workflows to the study
specs = session.query(WorkflowSpecModel).filter_by(is_status=False).all()
for spec in specs:
add_response = self.app.post('/v1.0/study/%i/workflows' % study.id,
content_type="application/json",
headers=self.logged_in_headers(),
data=json.dumps(WorkflowSpecModelSchema().dump(spec)))
self.assert_success(add_response)
for is_active in [False, True]:
# Update status task data
update_status_response = self.app.put('/v1.0/workflow/%i/task/%s/data' % (status_workflow.id, status_task_id),
headers=self.logged_in_headers(),
content_type="application/json",
data=json.dumps({'some_input': is_active}))
self.assert_success(update_status_response)
# List workflows for study
response_after = self.app.get('/v1.0/study/%i/workflows' % study.id,
content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(response_after)
json_data_after = json.loads(response_after.get_data(as_text=True))
workflows_after = WorkflowApiSchema(many=True).load(json_data_after)
self.assertEqual(len(specs), len(workflows_after))
for workflow in workflows_after:
self.assertEqual(workflow.is_active, is_active)

View File

@ -326,6 +326,9 @@ class TestWorkflowProcessor(BaseTest):
def test_status_bpmn(self):
self.load_example_data()
specs = session.query(WorkflowSpecModel).all()
study = session.query(StudyModel).first()
workflow_spec_model = self.load_test_spec("status")
@ -345,6 +348,5 @@ class TestWorkflowProcessor(BaseTest):
self.assertEqual(processor.get_status(), WorkflowStatus.complete)
# Enabled status of all specs should match the value set in the first task
for spec_id in ['two_forms', 'random_fact', 'pb_responses', 'study_details']:
self.assertEqual(task.data[spec_id], enabled)
for spec in specs:
self.assertEqual(task.data[spec.id], enabled)