Fixing a rogue comma that made something a tuple and not a string, which drives me CRAZY.

This commit is contained in:
Dan Funk 2020-02-25 12:01:25 -05:00
parent 002207cbca
commit 1e8a095760
7 changed files with 135 additions and 27 deletions

20
Pipfile.lock generated
View File

@ -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": [

View File

@ -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:

View File

@ -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)

View File

@ -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" />

View File

@ -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" />

View File

@ -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'])

View File

@ -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()