Skip to content

Commit

Permalink
Allow provide process and task descriptions in docstrings. Close #58
Browse files Browse the repository at this point in the history
  • Loading branch information
kmmbvnr committed Jul 1, 2014
1 parent afa491e commit 2eba2b2
Show file tree
Hide file tree
Showing 10 changed files with 78 additions and 11 deletions.
10 changes: 10 additions & 0 deletions tests/examples/helloworld/flows.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@


class HelloWorldFlow(Flow):
"""
Hello world process
This process demonstrates hello world approval request flow.
1. User with `helloworld.can_start_process` permission creates hello world request
2. Manage, who have `helloworld.can_approve_request`approves it
3. And if request was approved, background celery job sends it to the world
4. Elsewhere, request became canncelled
"""
process_cls = HelloWorldProcess
lock_impl = select_for_update_lock

Expand Down
3 changes: 3 additions & 0 deletions tests/examples/shipment/flows.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@


class ShipmentFlow(Flow):
"""
Shipment delivery process
"""
process_cls = ShipmentProcess
task_cls = ShipmentTask
lock_impl = select_for_update_lock
Expand Down
11 changes: 11 additions & 0 deletions viewflow/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,14 @@ def __new__(cls, name, bases, attrs):
for name, node in nodes.items():
node.flow_cls = new_class

# description
if new_class.__doc__:
docstring = new_class.__doc__.split('\n\n', maxsplit=1)
if 'process_title' not in attrs and len(docstring) > 0:
new_class.process_title = docstring[0].strip()
if 'process_description' not in attrs and len(docstring) > 1:
new_class.process_description = docstring[1].strip()

# index view
if not getattr(new_class, 'index_view', None):
from viewflow.views import index
Expand Down Expand Up @@ -118,6 +126,9 @@ class Flow(object, metaclass=FlowMetaClass):
management_form_cls = ActivationDataForm
lock_impl = lock.no_lock

process_title = None
process_description = None

@property
def urls(self):
"""
Expand Down
30 changes: 29 additions & 1 deletion viewflow/flow/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ class Node(object):
task_type = None
activation_cls = None

def __init__(self, activation_cls=None):
def __init__(self, activation_cls=None, **kwargs):
self._incoming_edges = []

self.flow_cls = None
Expand Down Expand Up @@ -179,3 +179,31 @@ def ready(self):
self._owner_permission = '{}.{}'.format(self.flow_cls.process_cls._meta.app_label, self._owner_permission)

super(PermissionMixin, self).ready()


class TaskDescriptionMixin(object):
"""
Extract task desctiption from view docstring
"""
task_title = None
task_description = None

def __init__(self, **kwargs):
task_title = kwargs.get('task_title', None)
task_description = kwargs.get('task_description', None)
view_or_cls = kwargs.get('view_or_cls', None)

if task_title:
self.task_title = task_title
if task_description:
self.task_description = task_description

if view_or_cls:
if view_or_cls.__doc__ and (self.task_title is None or self.task_description is None):
docstring = view_or_cls.__doc__.split('\n\n', maxsplit=1)
if task_title is None and len(docstring) > 0:
self.task_title = docstring[0].strip()
if task_description is None and len(docstring) > 1:
self.task_description = docstring[1].strip()

super(TaskDescriptionMixin, self).__init__(**kwargs)
14 changes: 9 additions & 5 deletions viewflow/flow/start.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

from viewflow.activation import StartActivation
from viewflow.exceptions import FlowRuntimeError
from viewflow.flow.base import Event, Edge, PermissionMixin
from viewflow.flow.base import Event, Edge, PermissionMixin, TaskDescriptionMixin
from viewflow import shortcuts


Expand Down Expand Up @@ -243,7 +243,7 @@ def dispatch(self, request, *args, **kwargs):
return super(StartView, self).dispatch(request, *args, **kwargs)


class Start(PermissionMixin, Event):
class Start(PermissionMixin, TaskDescriptionMixin, Event):
"""
Start process event
Expand Down Expand Up @@ -283,11 +283,17 @@ def start_process(request, activation):
task_type = 'START'
activation_cls = StartViewActivation

def __init__(self, view_or_cls=None, activation_cls=None, **kwargs):
def __init__(self, view_or_cls=None, activation_cls=None, task_title=None, task_description=None, **kwargs):
"""
Accepts view callable or CBV View class with view kwargs,
if CBV view implements StartActivation, it used as activation_cls
"""
super(Start, self).__init__(
activation_cls=activation_cls,
task_title=task_title,
task_description=task_description,
**kwargs)

self._activate_next = []
self._view, self._view_cls, self._view_args = None, None, None

Expand All @@ -300,8 +306,6 @@ def __init__(self, view_or_cls=None, activation_cls=None, **kwargs):
else:
self._view = view_or_cls

super(Start, self).__init__(activation_cls=activation_cls)

def _outgoing(self):
for next_node in self._activate_next:
yield Edge(src=self, dst=next_node, edge_class='next')
Expand Down
4 changes: 2 additions & 2 deletions viewflow/flow/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

from viewflow.activation import ViewActivation
from viewflow.exceptions import FlowRuntimeError
from viewflow.flow.base import Task, Edge, PermissionMixin
from viewflow.flow.base import Task, Edge, PermissionMixin, TaskDescriptionMixin
from viewflow import shortcuts


Expand Down Expand Up @@ -223,7 +223,7 @@ def dispatch(self, request, *args, **kwargs):
return super(ProcessView, self).dispatch(request, *args, **kwargs)


class View(PermissionMixin, Task):
class View(PermissionMixin, TaskDescriptionMixin, Task):
"""
View task
Expand Down
2 changes: 1 addition & 1 deletion viewflow/templates/viewflow/base_site.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{% extends "viewflow/base.html" %}

{% block title %}Viewflow | {{ activation.app_label|title }} | {{ activation.flow_cls }}{% endblock %}
{% block title %}Viewflow{% endblock %}
{% block branding %}Viewflow{% endblock %}

8 changes: 8 additions & 0 deletions viewflow/templates/viewflow/flow/index.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
{% extends 'viewflow/base_site.html' %}

{% block title %}{{ block.super}} | {{ flow_cls.process_title }}{% endblock %}

{% block content %}
{% if flow_cls.process_title %}
<div class="page-header">
<h1>{{ flow_cls.process_title }}</h1>
</div>
{% endif %}

{% if has_start_permission %}
<a class="btn btn-default" href="{% url 'viewflow:start' %}">Start New<a>
{% endif %}
Expand Down
4 changes: 3 additions & 1 deletion viewflow/templates/viewflow/flow/start.html
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
{% extends 'viewflow/base_site.html' %}
{% load bootstrap3 %}

{% block title %}{{ block.super}} | {{ activation.flow_cls.process_title }}{% endblock %}

{% block content %}
<form role="form" method="POST">
{% csrf_token %}
<div class="box box-primary">
<div class="box-header">
<h3 class="box-title">Start {{ activation.process.flow_cls }}</h3>
<h3 class="box-title">{% if activation.flow_task.task_title %}{{ activation.flow_task.task_title }}{% else %}Start {{ activation.flow_cls.process_title|lower }}{% endif %}</h3>
</div>
<div class="box-body">
{% bootstrap_form form %}
Expand Down
3 changes: 2 additions & 1 deletion viewflow/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ def index(request, flow_cls):
'{}/flow/index.html'.format(flow_cls._meta.app_label),
'viewflow/flow/index.html')

return render(request, templates, {'process_list': get_page(request, process_list),
return render(request, templates, {'flow_cls': flow_cls,
'process_list': get_page(request, process_list),
'has_start_permission': flow_cls.start.has_perm(request.user)},
current_app=flow_cls._meta.namespace)

Expand Down

0 comments on commit 2eba2b2

Please sign in to comment.