Skip to content

Commit 921fdc2

Browse files
committed
Ref #19 -- Add create_task documentation
1 parent fbec668 commit 921fdc2

File tree

8 files changed

+128
-2
lines changed

8 files changed

+128
-2
lines changed

docs/inbox.rst

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
=====
2+
Inbox
3+
=====
4+
5+
In a multi user workflow it can become handy to assign and reassign tasks and to have
6+
some kind of inbox to keep track of incomplete tasks.
7+
8+
Automatic assigment
9+
-------------------
10+
11+
To implement default assignees you need to override implement your own
12+
:meth:`create_task` method.
13+
14+
15+
.. code-block:: python
16+
17+
from joeflow import tasks
18+
19+
20+
class UpdateWithPrevUserView(tasks.UpdateView):
21+
22+
def create_task(self, workflow, prev_task):
23+
"""Assign a new task to the user who completed the previous task."""
24+
new_task = workflow.task_set.create(
25+
workflow=workflow,
26+
name=self.name,
27+
type=self.type,
28+
)
29+
if prev_task.completed_by_user:
30+
# If the previous tasks was a machine task, no user is set.
31+
new_task.assignees.add(prev_task.completed_by_user)
32+
return new_task
33+
34+
35+
In this implementation the new task is assigned to the user who completed the previous
36+
task. The current workflow and the previous task are always given :meth:`create_task`
37+
method. Note, that a request is not available, since this method might not be called
38+
within a view but in a machine task. The parent task relation must not be created as a
39+
part of the :meth:`create_task` method.
40+
41+
API
42+
===
43+
44+
.. automethod:: joeflow.views.TaskViewMixin.create_task

joeflow/models.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -571,7 +571,7 @@ def start_next_tasks(self, next_nodes: list = None):
571571
for node in next_nodes:
572572
try:
573573
# Some nodes – like Join – implement their own method to create new tasks.
574-
task = node.create_task(self.workflow)
574+
task = node.create_task(self.workflow, self)
575575
except AttributeError:
576576
task = self.workflow.task_set.create(
577577
name=node.name, type=node.type, workflow=self.workflow

joeflow/tasks/machine.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ def __init__(self, *parents: Iterable[str]):
108108
def __call__(self, workflow, task):
109109
return set(task.parent_task_set.values_list("name", flat=True)) == self.parents
110110

111-
def create_task(self, workflow):
111+
def create_task(self, workflow, prev_task):
112112
return workflow.task_set.get_or_create(
113113
name=self.name,
114114
type=self.type,

joeflow/views.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,22 @@ def post(self, request, *args, **kwargs):
7171
task.start_next_tasks()
7272
return response
7373

74+
def create_task(self, workflow, prev_task):
75+
"""
76+
Factory method retuning a new task instance.
77+
78+
Args:
79+
workflow (joeflow.models.Workflow): Current workflow instance.
80+
prev_task (joeflow.models.Task): Instance of the previous Task.
81+
82+
Returns:
83+
joeflow.models.Task: New task instance.
84+
85+
"""
86+
return workflow.task_set.create(
87+
name=self.name, type=self.type, workflow=workflow
88+
)
89+
7490

7591
class WorkflowDetailView(WorkflowTemplateNameViewMixin, generic.DetailView):
7692
pass

tests/test_views.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
from django.urls import reverse
2+
3+
from tests.testapp import workflows
4+
5+
6+
class TestTaskViewMixin:
7+
8+
def test_create_task(self, db, admin_client, admin_user):
9+
url = reverse('simpleworkflow:start_view')
10+
response = admin_client.post(url)
11+
assert response.status_code == 302
12+
13+
wf = workflows.SimpleWorkflow.objects.get()
14+
15+
assert wf.task_set.count() == 2
16+
new_task = wf.task_set.get(name='save_the_princess')
17+
assert new_task.name == 'save_the_princess'
18+
assert new_task.type == 'human'
19+
assert not new_task.assignees.all()
20+
21+
def test_custom_create_task(self, db, admin_client, admin_user):
22+
url = reverse('assigneeworkflow:start_view')
23+
response = admin_client.post(url)
24+
assert response.status_code == 302
25+
26+
wf = workflows.AssigneeWorkflow.objects.get()
27+
28+
assert wf.task_set.count() == 2
29+
new_task = wf.task_set.get(name='save_the_princess')
30+
assert admin_user in new_task.assignees.all()

tests/testapp/urls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
path("admin/", admin.site.urls),
2323
path("shipment/", include(workflows.ShippingWorkflow.urls())),
2424
path("simple/", include(workflows.SimpleWorkflow.urls())),
25+
path("assignee/", include(workflows.AssigneeWorkflow.urls())),
2526
path("gateway/", include(workflows.GatewayWorkflow.urls())),
2627
path("splitjoin/", include(workflows.SplitJoinWorkflow.urls())),
2728
path("loop/", include(workflows.LoopWorkflow.urls())),

tests/testapp/views.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from joeflow import tasks
2+
3+
4+
class UpdateWithPrevUserView(tasks.UpdateView):
5+
6+
def create_task(self, workflow, prev_task):
7+
"""Assign a new task to the user who completed the previous task."""
8+
new_task = workflow.task_set.create(
9+
workflow=workflow,
10+
name=self.name,
11+
type=self.type,
12+
)
13+
if prev_task.completed_by_user:
14+
# If the previous tasks was a machine task, no user is set.
15+
new_task.assignees.add(prev_task.completed_by_user)
16+
return new_task

tests/testapp/workflows.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from joeflow import tasks
66

77
from . import models
8+
from .views import UpdateWithPrevUserView
89

910

1011
class ShippingWorkflow(models.Shipment):
@@ -86,6 +87,24 @@ class Meta:
8687
proxy = True
8788

8889

90+
class AssigneeWorkflow(models.SimpleWorkflowState):
91+
start_view = tasks.StartView(fields="__all__", path="custom/postfix/")
92+
start_method = tasks.Start()
93+
save_the_princess = UpdateWithPrevUserView(fields="__all__")
94+
95+
def end(self):
96+
pass
97+
98+
edges = (
99+
(start_view, save_the_princess),
100+
(start_method, save_the_princess),
101+
(save_the_princess, end),
102+
)
103+
104+
class Meta:
105+
proxy = True
106+
107+
89108
class GatewayWorkflow(models.GatewayWorkflowState):
90109
start = tasks.StartView(fields="__all__")
91110

0 commit comments

Comments
 (0)