Fixing a rogue comma that made something a tuple and not a string, which drives me CRAZY.
This commit is contained in:
parent
002207cbca
commit
1e8a095760
|
@ -337,10 +337,10 @@
|
|||
},
|
||||
"idna": {
|
||||
"hashes": [
|
||||
"sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407",
|
||||
"sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"
|
||||
"sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb",
|
||||
"sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa"
|
||||
],
|
||||
"version": "==2.8"
|
||||
"version": "==2.9"
|
||||
},
|
||||
"imagesize": {
|
||||
"hashes": [
|
||||
|
@ -476,11 +476,11 @@
|
|||
},
|
||||
"marshmallow": {
|
||||
"hashes": [
|
||||
"sha256:7669b944d6233b81f68739d5826f1176c3841cc31cf6b856841083b5a72f5ca9",
|
||||
"sha256:c9d277f6092f32300395fb83d343be9f61b5e99d66d22bae1e5e7cd82608fee6"
|
||||
"sha256:3a94945a7461f2ab4df9576e51c97d66bee2c86155d3d3933fab752b31effab8",
|
||||
"sha256:4b95c7735f93eb781dfdc4dded028108998cad759dda8dd9d4b5b4ac574cbf13"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.4.0"
|
||||
"version": "==3.5.0"
|
||||
},
|
||||
"marshmallow-enum": {
|
||||
"hashes": [
|
||||
|
@ -646,11 +646,11 @@
|
|||
},
|
||||
"requests": {
|
||||
"hashes": [
|
||||
"sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4",
|
||||
"sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31"
|
||||
"sha256:43999036bfa82904b6af1d99e4882b560e5e2c68e5c4b0aa03b655f3d7d73fee",
|
||||
"sha256:b3f43d496c6daba4493e7c431722aeb7dbc6288f52a6e04e7b6023b0247817e6"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.22.0"
|
||||
"version": "==2.23.0"
|
||||
},
|
||||
"six": {
|
||||
"hashes": [
|
||||
|
@ -726,7 +726,7 @@
|
|||
"spiffworkflow": {
|
||||
"editable": true,
|
||||
"git": "https://github.com/sartography/SpiffWorkflow.git",
|
||||
"ref": "d86ff8edb495e593c11ed0ac87141a5e612a5e60"
|
||||
"ref": "bab9a280f94b8a94ade5107c0bce104aade9ad04"
|
||||
},
|
||||
"sqlalchemy": {
|
||||
"hashes": [
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import enum
|
||||
|
||||
import marshmallow
|
||||
from jinja2 import Environment, BaseLoader
|
||||
from marshmallow import INCLUDE
|
||||
from marshmallow_enum import EnumField
|
||||
from marshmallow_sqlalchemy import ModelSchema
|
||||
|
@ -54,7 +55,7 @@ class Task(object):
|
|||
try:
|
||||
documentation = spiff_task.task_spec.documentation
|
||||
except AttributeError:
|
||||
documentation = None
|
||||
documentation = ""
|
||||
instance = cls(spiff_task.id,
|
||||
spiff_task.task_spec.name,
|
||||
spiff_task.task_spec.description,
|
||||
|
@ -65,8 +66,16 @@ class Task(object):
|
|||
spiff_task.data)
|
||||
if hasattr(spiff_task.task_spec, "form"):
|
||||
instance.form = spiff_task.task_spec.form
|
||||
if documentation != "" and documentation is not None:
|
||||
instance.process_documentation(documentation)
|
||||
return instance
|
||||
|
||||
def process_documentation(self, documentation):
|
||||
'''Runs markdown documentation through the Jinja2 processor to inject data
|
||||
create loops, etc...'''
|
||||
rtemplate = Environment(loader=BaseLoader).from_string(documentation)
|
||||
self.documentation = rtemplate.render(**self.data)
|
||||
|
||||
|
||||
class OptionSchema(ma.Schema):
|
||||
class Meta:
|
||||
|
|
|
@ -3,6 +3,7 @@ import xml.etree.ElementTree as ElementTree
|
|||
from SpiffWorkflow import Task as SpiffTask, Workflow
|
||||
from SpiffWorkflow.bpmn.BpmnScriptEngine import BpmnScriptEngine
|
||||
from SpiffWorkflow.bpmn.serializer.BpmnSerializer import BpmnSerializer
|
||||
from SpiffWorkflow.bpmn.specs.EndEvent import EndEvent
|
||||
from SpiffWorkflow.bpmn.workflow import BpmnWorkflow
|
||||
from SpiffWorkflow.camunda.parser.CamundaParser import CamundaParser
|
||||
from SpiffWorkflow.dmn.parser.BpmnDmnParser import BpmnDmnParser
|
||||
|
@ -140,20 +141,39 @@ class WorkflowProcessor(object):
|
|||
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:
|
||||
"""Returns the next task that should be completed
|
||||
even if there are parallel tasks and multiple options are
|
||||
available.
|
||||
If the workflow is complete
|
||||
it will return the final end task.
|
||||
"""
|
||||
|
||||
# If the whole blessed mess is done, return the end_event task in the tree
|
||||
if self.bpmn_workflow.is_completed():
|
||||
last_task = None
|
||||
for task in SpiffTask.Iterator(self.bpmn_workflow.task_tree, SpiffTask.ANY_MASK):
|
||||
if isinstance(task.task_spec, EndEvent):
|
||||
return task
|
||||
|
||||
# If there are ready tasks to complete, return the next ready task, but return the one
|
||||
# in the active parallel path if possible.
|
||||
ready_tasks = self.bpmn_workflow.get_tasks(SpiffTask.READY)
|
||||
if len(ready_tasks) > 0:
|
||||
for task in ready_tasks:
|
||||
if task.parent == self.bpmn_workflow.last_task:
|
||||
return task
|
||||
return ready_tasks[0]
|
||||
|
||||
# If there are no ready tasks, but the thing isn't complete yet, find the first non-complete task
|
||||
# and return that
|
||||
next_task = None
|
||||
for task in SpiffTask.Iterator(self.bpmn_workflow.task_tree, SpiffTask.NOT_FINISHED_MASK):
|
||||
next_task = task
|
||||
return next_task
|
||||
|
||||
|
||||
|
||||
|
||||
def complete_task(self, task):
|
||||
self.bpmn_workflow.complete_task_from_id(task.id)
|
||||
|
||||
|
|
|
@ -20,6 +20,14 @@
|
|||
<bpmn:outgoing>SequenceFlow_0grui6f</bpmn:outgoing>
|
||||
</bpmn:businessRuleTask>
|
||||
<bpmn:endEvent id="EndEvent_0tsqkyu">
|
||||
<bpmn:documentation># Great Work!
|
||||
|
||||
Based on the information you provided (Ginger left {{num_presents}}, we recommend the following statement be provided to Ginger:
|
||||
|
||||
## {{message}}
|
||||
|
||||
We hope you both have an excellent day!
|
||||
</bpmn:documentation>
|
||||
<bpmn:incoming>SequenceFlow_0grui6f</bpmn:incoming>
|
||||
</bpmn:endEvent>
|
||||
<bpmn:sequenceFlow id="SequenceFlow_0grui6f" sourceRef="Task_0sgafty" targetRef="EndEvent_0tsqkyu" />
|
||||
|
|
|
@ -104,10 +104,7 @@ Autoconverted link https://github.com/nodeca/pica (enable linkify to see)
|
|||
## Images
|
||||
|
||||
![Minion](https://octodex.github.com/images/minion.png)
|
||||
![Stormtroopocat](https://octodex.github.com/images/stormtroopocat.jpg "The Stormtroopocat")
|
||||
|
||||
|
||||
</bpmn:documentation>
|
||||
![Stormtroopocat](https://octodex.github.com/images/stormtroopocat.jpg "The Stormtroopocat")</bpmn:documentation>
|
||||
<bpmn:extensionElements>
|
||||
<camunda:formData>
|
||||
<camunda:formField id="type" label="Type" type="enum" defaultValue="cat">
|
||||
|
@ -124,6 +121,8 @@ Autoconverted link https://github.com/nodeca/pica (enable linkify to see)
|
|||
<bpmn:outgoing>SequenceFlow_0641sh6</bpmn:outgoing>
|
||||
</bpmn:userTask>
|
||||
<bpmn:scriptTask id="Task_Get_Fact_From_API" name="Display Fact">
|
||||
<bpmn:documentation>
|
||||
</bpmn:documentation>
|
||||
<bpmn:extensionElements>
|
||||
<camunda:inputOutput>
|
||||
<camunda:inputParameter name="Fact.type" />
|
||||
|
@ -134,6 +133,12 @@ Autoconverted link https://github.com/nodeca/pica (enable linkify to see)
|
|||
<bpmn:script>scripts.FactService</bpmn:script>
|
||||
</bpmn:scriptTask>
|
||||
<bpmn:endEvent id="EndEvent_0u1cgrf">
|
||||
<bpmn:documentation># Great Job!
|
||||
You have completed the random fact generator.
|
||||
You chose to receive a random fact of the type: "{{type}}"
|
||||
|
||||
Your random fact is:
|
||||
{{details}}</bpmn:documentation>
|
||||
<bpmn:incoming>SequenceFlow_0t29gjo</bpmn:incoming>
|
||||
</bpmn:endEvent>
|
||||
<bpmn:sequenceFlow id="SequenceFlow_0c7wlth" sourceRef="StartEvent_1" targetRef="Task_User_Select_Type" />
|
||||
|
|
|
@ -4,7 +4,7 @@ from crc import session
|
|||
from crc.models.file import FileModelSchema
|
||||
from crc.models.study import StudyModel
|
||||
from crc.models.workflow import WorkflowSpecModel, WorkflowSpecModelSchema, WorkflowModel, \
|
||||
WorkflowApiSchema, WorkflowStatus
|
||||
WorkflowApiSchema, WorkflowStatus, Task
|
||||
from tests.base_test import BaseTest
|
||||
|
||||
|
||||
|
@ -132,11 +132,55 @@ class TestTasksApi(BaseTest):
|
|||
"last_name": "Mr. Wolf"
|
||||
}
|
||||
workflow_api = self.complete_form(workflow, tasks[0], data)
|
||||
# workflow_api = self.get_workflow_api(workflow)
|
||||
self.assertIsNone(workflow_api.next_task)
|
||||
self.assertIsNotNone(workflow_api.next_task)
|
||||
self.assertEquals("EndEvent_0evb22x", workflow_api.next_task['name'])
|
||||
self.assertTrue(workflow_api.status == WorkflowStatus.complete)
|
||||
rv = self.app.get('/v1.0/file?workflow_id=%i' % workflow.id)
|
||||
self.assert_success(rv)
|
||||
json_data = json.loads(rv.get_data(as_text=True))
|
||||
files = FileModelSchema(many=True).load(json_data, session=session)
|
||||
self.assertTrue(len(files) == 1)
|
||||
|
||||
def test_documentation_processing_handles_replacements(self):
|
||||
|
||||
docs = "Some simple docs"
|
||||
task = Task(1, "bill", "bill", "", "started", {}, docs, {})
|
||||
task.process_documentation(docs)
|
||||
self.assertEqual(docs, task.documentation)
|
||||
|
||||
task.data = {"replace_me": "new_thing"}
|
||||
task.process_documentation("{{replace_me}}")
|
||||
self.assertEqual("new_thing", task.documentation)
|
||||
|
||||
documentation = """
|
||||
# Bigger Test
|
||||
|
||||
* bullet one
|
||||
* bullet two has {{replace_me}}
|
||||
|
||||
# other stuff.
|
||||
"""
|
||||
expected = """
|
||||
# Bigger Test
|
||||
|
||||
* bullet one
|
||||
* bullet two has new_thing
|
||||
|
||||
# other stuff.
|
||||
"""
|
||||
task.process_documentation(documentation)
|
||||
self.assertEqual(expected, task.documentation)
|
||||
|
||||
def test_get_documentation_populated_in_end(self):
|
||||
self.load_example_data()
|
||||
workflow = self.create_workflow('random_fact')
|
||||
workflow_api = self.get_workflow_api(workflow)
|
||||
tasks = workflow_api.user_tasks
|
||||
self.assertEqual("Task_User_Select_Type", tasks[0].name)
|
||||
self.assertEqual(3, len(tasks[0].form["fields"][0]["options"]))
|
||||
self.assertIsNotNone(tasks[0].documentation)
|
||||
self.complete_form(workflow, workflow_api.user_tasks[0], {"type": "norris"})
|
||||
workflow_api = self.get_workflow_api(workflow)
|
||||
self.assertEqual("EndEvent_0u1cgrf", workflow_api.next_task['name'])
|
||||
self.assertIsNotNone(workflow_api.next_task['documentation'])
|
||||
self.assertTrue("norris" in workflow_api.next_task['documentation'])
|
|
@ -1,6 +1,8 @@
|
|||
import string
|
||||
import random
|
||||
|
||||
from SpiffWorkflow.bpmn.specs.EndEvent import EndEvent
|
||||
|
||||
from crc import session
|
||||
from crc.api.common import ApiError
|
||||
from crc.models.file import FileModel, FileDataModel
|
||||
|
@ -71,6 +73,9 @@ class TestWorkflowProcessor(BaseTest):
|
|||
self.assertIsNotNone(data)
|
||||
self.assertIn("message", data)
|
||||
self.assertEqual("Oh, Ginger.", data.get('message'))
|
||||
self.assertEqual("End", processor.bpmn_workflow.last_task.task_spec.name)
|
||||
self.assertEqual("Oh, Ginger.", processor.bpmn_workflow.last_task.data.get('message'))
|
||||
|
||||
|
||||
def test_workflow_with_parallel_forms(self):
|
||||
self.load_example_data()
|
||||
|
@ -134,6 +139,23 @@ class TestWorkflowProcessor(BaseTest):
|
|||
self.assertEqual(4, len(next_user_tasks))
|
||||
self.assertEqual(task.children[0], processor.next_task())
|
||||
|
||||
def test_workflow_processor_returns_next_task_as_end_task_if_complete(self):
|
||||
self.load_example_data()
|
||||
workflow_spec_model = session.query(WorkflowSpecModel).filter_by(id="random_fact").first()
|
||||
study = session.query(StudyModel).first()
|
||||
processor = WorkflowProcessor.create(study.id, workflow_spec_model.id)
|
||||
processor.do_engine_steps()
|
||||
task = processor.next_task()
|
||||
task.data = {"type": "buzzword"}
|
||||
processor.complete_task(task)
|
||||
self.assertEqual(WorkflowStatus.waiting, processor.get_status())
|
||||
processor.do_engine_steps()
|
||||
self.assertEqual(WorkflowStatus.complete, processor.get_status())
|
||||
task = processor.next_task()
|
||||
self.assertIsNotNone(task)
|
||||
self.assertIn("details", task.data)
|
||||
self.assertIsInstance(task.task_spec, EndEvent)
|
||||
|
||||
def test_workflow_with_bad_expression_raises_sensible_error(self):
|
||||
self.load_example_data()
|
||||
|
||||
|
|
Loading…
Reference in New Issue