Skip to content

Commit

Permalink
Intoduce explicit flow site
Browse files Browse the repository at this point in the history
  • Loading branch information
kmmbvnr committed Aug 12, 2014
1 parent d12b3b4 commit 2f703a5
Show file tree
Hide file tree
Showing 16 changed files with 295 additions and 7 deletions.
4 changes: 4 additions & 0 deletions tests/examples/helloworld/flows.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from viewflow import flow
from viewflow.base import this, Flow
from viewflow.site import flowsite
from viewflow.views import StartView, ProcessView
from viewflow.lock import select_for_update_lock

Expand Down Expand Up @@ -37,3 +38,6 @@ class HelloWorldFlow(Flow):
.Next(this.end)

end = flow.End()


flowsite.register(HelloWorldFlow)
4 changes: 4 additions & 0 deletions tests/examples/shipment/flows.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from viewflow import flow
from viewflow.base import this, Flow
from viewflow.site import flowsite
from viewflow.views import ProcessView
from viewflow.lock import select_for_update_lock

Expand Down Expand Up @@ -69,3 +70,6 @@ class ShipmentFlow(Flow):
.Assign(this.package_goods.owner)

end = flow.End()


flowsite.register(ShipmentFlow)
2 changes: 1 addition & 1 deletion tests/unit/tests/test_meta.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def test_get_node_by_name_succeed(self):

def test_namespace_resolved(self):
self.assertEqual('unit', SingleTaskFlow._meta.app_label)
self.assertEqual('unit/SingleTaskFlow', SingleTaskFlow._meta.namespace)
self.assertEqual('unit/singletask', SingleTaskFlow._meta.namespace)


class TestMetaConstruction(TestCase):
Expand Down
4 changes: 2 additions & 2 deletions tests/urls.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from django.conf.urls import patterns, include, url
from django.contrib import admin
from django.views.generic.base import TemplateView
from viewflow.site import flowsite
from unit.views import TestFormView


Expand All @@ -9,10 +10,9 @@

urlpatterns = patterns('', # NOQA
url(r'^$', TemplateView.as_view(template_name='index.html')),
url(r'^flows/', include(flowsite.urls)),
url(r'^viewflow/$', TemplateView.as_view(template_name='viewflow/process_index.html')),
url(r'^admin/', include(admin.site.urls)),
url(r'^examples/shipment/', include('tests.examples.shipment.urls')),
url(r'^examples/helloworld/', include('tests.examples.helloworld.urls')),
url(r'^examples/form/$', TestFormView.as_view()),
url(r'^', include('examples.website')),
)
6 changes: 5 additions & 1 deletion viewflow/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,14 @@ def __init__(self, app_label, flow_cls, nodes):

@property
def namespace(self):
return "{}/{}".format(self.app_label, self.flow_label)

@property
def flow_label(self):
module = "{}.{}".format(self.flow_cls.__module__, self.flow_cls.__name__)
app_config = apps.get_containing_app_config(module)
subpath = module.lstrip(app_config.module.__package__+'.flows.')
return "{}/{}".format(app_config.label, subpath)
return subpath.lower().rstrip('flow').replace('.', '/')

def nodes(self):
"""
Expand Down
4 changes: 4 additions & 0 deletions viewflow/site/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
from .base import ViewSite
from .viewform import (Layout, Row, Column, Fieldset, Inline, # NOQA
Field, Span2, Span3, Span4, Span5, Span6,
Span7, Span8, Span9, Span10, Span11, Span12,
LayoutMixin)


flowsite = ViewSite()
61 changes: 61 additions & 0 deletions viewflow/site/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
from django.conf.urls import patterns, url, include

from . import views


class ViewSite(object):
login_view = staticmethod(views.LoginView.as_view())
logout_view = staticmethod(views.LogoutView.as_view())

# all process views
processes_list_view = staticmethod(views.processes_list_view)
tasks_list_view = staticmethod(views.tasks_list_view)
queues_view = staticmethod(views.queues_view)

# per-process views
process_list_view = staticmethod(views.ProcessListView.as_view())
process_detail_view = staticmethod(views.process_detail_view)
task_list_view = staticmethod(views.task_list_view)
queue_view = staticmethod(views.queue_view)

def __init__(self, app_name='viewsite_default'):
self.app_name = app_name
self.flows = []

@property
def urls(self):
site_patterns = patterns(
'',
url('^$', self.processes_list_view, {'flow_site': self}, name="index"),
url('^login/$', self.login_view, {'flow_site': self}, name="login"),
url('^logout/$', self.logout_view, {'flow_site': self}, name="logout"),
url('^tasks/$', self.tasks_list_view, {'flow_site': self}, name="tasks"),
url('^queues/$', self.queues_view, {'flow_site': self}, name="queues"),
)

urls = [
url('', include(site_patterns, self.app_name, 'viewflow_site'))
]

for flow_cls in self.flows:
flow_patterns, namespace, app_name = flow_cls.instance.urls

list_patterns = patterns(
'',
url('^$', self.process_list_view,
{'flow_site': self, 'flow_cls': flow_cls}, name='index'),
url('^tasks/$', self.task_list_view,
{'flow_site': self, 'flow_cls': flow_cls}, name='tasks'),
url('^queues/$', self.queue_view,
{'flow_site': self, 'flow_cls': flow_cls}, name='queue')
)

urls.append(
url('^{}/'.format(flow_cls._meta.flow_label),
include(list_patterns + flow_patterns, app_name, namespace)))

return patterns('', *urls)

def register(self, flow_cls):
if flow_cls not in self.flows:
self.flows.append(flow_cls)
7 changes: 7 additions & 0 deletions viewflow/site/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from django import forms


class LoginForm(forms.Form):
username = forms.CharField(max_length=250)
password = forms.CharField(max_length=250, widget=forms.PasswordInput)
remember = forms.BooleanField(default=False)
4 changes: 4 additions & 0 deletions viewflow/site/static/viewflow/viewform.css
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,7 @@ body {
background: none;
color: #285e8e;
}

.login-panel {
margin-top: 25%;
}
1 change: 0 additions & 1 deletion viewflow/site/templates/viewflow/base_site.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,3 @@

{% block title %}Viewflow{% endblock %}
{% block branding %}Viewflow{% endblock %}

11 changes: 9 additions & 2 deletions viewflow/site/templates/viewflow/flow/index.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{% extends 'viewflow/base_site.html' %}
{% load viewform %}

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

Expand All @@ -9,8 +10,11 @@ <h1>{{ flow_cls.process_title }}</h1>
</div>
{% endif %}

{% if has_start_permission %}
<a class="btn btn-default" href="{% url 'viewflow:start' %}">Start New<a>
{% if start_actions %}
{% for action_url, action_name in start_actions %}
<a class="btn btn-default" href="{{ action_url }}">{{ action_name|title }}</a>
{% endfor %}
</div>
{% endif %}
<table class="table">
<thead>
Expand Down Expand Up @@ -39,4 +43,7 @@ <h1>{{ flow_cls.process_title }}</h1>
{% endfor %}
</tbody>
</table>

{% pagination page_obj %}

{% endblock %}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{% extends 'viewflow/form/bootstrap3/widgets/textinput.html' %}
19 changes: 19 additions & 0 deletions viewflow/site/templates/viewflow/login.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{% extends "viewflow/base_site.html" %}
{% load viewform %}

{% block content %}
<div class="col-md-4 col-md-offset-4">
<div class="login-panel panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Please Sign In</h3>
</div>
<div class="panel-body">
<form role="form" method="POST">
{% csrf_token %}
{% viewform 'viewflow/form/bootstrap3/form.html' form=form layout=view.layout %} {% endviewform %}
<button type="submit" name="start" class="btn btn-lg btn-success btn-block">Login</button>
</form>
</div>
</div>
</div>
{% endblock content %}
27 changes: 27 additions & 0 deletions viewflow/site/templates/viewflow/pagination.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{% if page.has_other_pages %}
<div class="row text-center">
<ul class="pagination">
{% if page.has_previous %}
<li><a href="?page={{ page.previous_page_number }}">&laquo;</a></li>
{% else %}
<li class="disabled"><a href="#">&laquo;</a></li>
{% endif %}
{% for page_num in page_range %}
{% if page_num != '.' %}
{% if page_num == page.number %}
<li class="active"><a href="#">{{ page_num }} <span class="sr-only">(current)</span></a></li>
{% else %}
<li><a href="?page={{ page_num }}">{{ page_num }}</a></li>
{% endif %}
{% else %}
<li><span>...</span></li>
{% endif %}
{% endfor %}
{% if page.has_next %}
<li><a href="?page={{ page.next_page_number }}">&raquo;</a></li>
{% else %}
<li class="disabled"><a href="#">&raquo;</a></li>
{% endif %}
</ul>
</div>
{% endif %}
30 changes: 30 additions & 0 deletions viewflow/site/templatetags/viewform.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,36 @@ def render_tag(self, context):
return re.sub('[\n ]+', ' ', value).strip()


@register.inclusion_tag('viewflow/pagination.html')
def pagination(page, on_each_side=3, on_ends=2):
paginator = page.paginator

page_range = []
if paginator.num_pages <= 10:
page_range = range(1, paginator.num_pages+1)
else:
# start part
if page.number > (on_each_side + on_ends + 1):
page_range.extend(range(1, on_ends+1))
page_range.append('.')
page_range.extend(range(page.number - on_each_side, page.number + 1))
else:
page_range.extend(range(1, page.number + 1))

# end part
if page.number < (paginator.num_pages - on_each_side - on_ends):
page_range.extend(range(page.number + 1, page.number + on_each_side+1))
page_range.append('.')
page_range.extend(range(paginator.num_pages - on_ends+1, paginator.num_pages+1))
else:
page_range.extend(range(page.number+1, paginator.num_pages+1))

return {
'page': page,
'page_range': page_range
}


@register.filter
def datepicker_format(field):
input_format = field.input_formats[0]
Expand Down
117 changes: 117 additions & 0 deletions viewflow/site/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
from django.contrib.auth import login as auth_login
from django.contrib.auth import logout as auth_logout
from django.contrib.auth.forms import AuthenticationForm
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect
from django.views import generic

from viewflow import flow


class FlowSiteMixin(object):
flow_site = None

def dispatch(self, request, *args, **kwargs):
if 'flow_site' in kwargs:
self.flow_site = kwargs['flow_site']
if 'flow_cls' in kwargs:
self.flow_cls = kwargs['flow_cls']

return super(FlowSiteMixin, self).dispatch(request, *args, **kwargs)


class LoginView(FlowSiteMixin, generic.FormView):
form_class = AuthenticationForm
template_name = 'viewflow/login.html'

def get_success_url(self):
return reverse('viewflow_site:index', current_app=self.flow_site.app_name)

def form_valid(self, form):
auth_login(self.request, form.get_user())
return HttpResponseRedirect(self.get_success_url())


class LogoutView(FlowSiteMixin, generic.View):
def get_success_url(self):
return reverse('viewflow_site:login', current_app=self.flow_site.app_name)

def get(self, request, *args, **kwargs):
auth_logout(request)
return HttpResponseRedirect(self.get_success_url())


def processes_list_view(request, flow_site=None):
"""
All process instances list available for current user
"""
pass


def tasks_list_view(request, flow_site=None):
"""
All tasks from all processes assigned to current user
"""
pass


def queues_view(request, flow_site=None):
"""
All unassigned tasks available for current user
"""
pass


class ProcessListView(FlowSiteMixin, generic.ListView):
paginate_by = 3
paginate_orphans = 0
context_object_name = 'process_list'

def get_template_names(self):
return ('{}/flow/index.html'.format(self.flow_cls._meta.app_label),
'viewflow/flow/index.html')

def available_start_actions(self):
"""
Return list of start flow actions data available for the user
"""

actions = []
for node in self.flow_cls._meta.nodes():
if isinstance(node, flow.Start) and node.has_perm(self.request.user):
node_url = reverse(
'viewflow:{}'.format(node.name),
current_app=self.flow_cls._meta.namespace)

actions.append((node_url, node.name))

return actions

def get_context_data(self, **kwargs):
context = super(ProcessListView, self).get_context_data(**kwargs)
context['start_actions'] = self.available_start_actions()
context['flow_cls'] = self.flow_cls
return context

def get_queryset(self):
return self.flow_cls.process_cls.objects \
.filter(flow_cls=self.flow_cls) \
.order_by('-created')


def process_detail_view(request, flow_site=None, flow_cls=None):
"""
Details for process
"""


def task_list_view(request, flow_site=None, flow_cls=None):
"""
List of specific Flow tasks assigned to current user
"""


def queue_view(request, flow_site=None, flow_cls=None):
"""
List of specific Flow unassigned tasks available for current user
"""

0 comments on commit 2f703a5

Please sign in to comment.