processlib 0.9.0__tar.gz → 0.10.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. {processlib-0.9.0/processlib.egg-info → processlib-0.10.0}/PKG-INFO +3 -2
  2. {processlib-0.9.0 → processlib-0.10.0}/processlib/__init__.py +1 -4
  3. {processlib-0.9.0 → processlib-0.10.0}/processlib/activity.py +4 -45
  4. {processlib-0.9.0 → processlib-0.10.0}/processlib/flow.py +4 -7
  5. {processlib-0.9.0 → processlib-0.10.0}/processlib/models.py +6 -11
  6. {processlib-0.9.0 → processlib-0.10.0}/processlib/services.py +22 -8
  7. {processlib-0.9.0 → processlib-0.10.0}/processlib/signals.py +22 -13
  8. {processlib-0.9.0 → processlib-0.10.0}/processlib/tests.py +68 -18
  9. {processlib-0.9.0 → processlib-0.10.0}/processlib/urls.py +10 -2
  10. {processlib-0.9.0 → processlib-0.10.0}/processlib/views.py +10 -5
  11. {processlib-0.9.0 → processlib-0.10.0/processlib.egg-info}/PKG-INFO +3 -2
  12. processlib-0.10.0/setup.py +16 -0
  13. processlib-0.9.0/setup.py +0 -14
  14. {processlib-0.9.0 → processlib-0.10.0}/LICENSE +0 -0
  15. {processlib-0.9.0 → processlib-0.10.0}/MANIFEST.in +0 -0
  16. {processlib-0.9.0 → processlib-0.10.0}/README.md +0 -0
  17. {processlib-0.9.0 → processlib-0.10.0}/processlib/apps.py +0 -0
  18. {processlib-0.9.0 → processlib-0.10.0}/processlib/assignment.py +0 -0
  19. {processlib-0.9.0 → processlib-0.10.0}/processlib/forms.py +0 -0
  20. {processlib-0.9.0 → processlib-0.10.0}/processlib/migrations/0001_initial.py +0 -0
  21. {processlib-0.9.0 → processlib-0.10.0}/processlib/migrations/0002_auto_20220525_1136.py +0 -0
  22. {processlib-0.9.0 → processlib-0.10.0}/processlib/migrations/__init__.py +0 -0
  23. {processlib-0.9.0 → processlib-0.10.0}/processlib/serializers.py +0 -0
  24. {processlib-0.9.0 → processlib-0.10.0}/processlib/tasks.py +0 -0
  25. {processlib-0.9.0 → processlib-0.10.0}/processlib/templates/processlib/layout.html +0 -0
  26. {processlib-0.9.0 → processlib-0.10.0}/processlib/templates/processlib/process_cancel.html +0 -0
  27. {processlib-0.9.0 → processlib-0.10.0}/processlib/templates/processlib/process_detail.html +0 -0
  28. {processlib-0.9.0 → processlib-0.10.0}/processlib/templates/processlib/process_details_partial.html +0 -0
  29. {processlib-0.9.0 → processlib-0.10.0}/processlib/templates/processlib/process_list.html +0 -0
  30. {processlib-0.9.0 → processlib-0.10.0}/processlib/templates/processlib/process_list_item.html +0 -0
  31. {processlib-0.9.0 → processlib-0.10.0}/processlib/templates/processlib/process_to_do_partial.html +0 -0
  32. {processlib-0.9.0 → processlib-0.10.0}/processlib/templates/processlib/view_activity.html +0 -0
  33. {processlib-0.9.0 → processlib-0.10.0}/processlib/templatetags/__init__.py +0 -0
  34. {processlib-0.9.0 → processlib-0.10.0}/processlib/templatetags/processlib_tags.py +0 -0
  35. {processlib-0.9.0 → processlib-0.10.0}/processlib/test_settings.py +0 -0
  36. {processlib-0.9.0 → processlib-0.10.0}/processlib/test_urls.py +0 -0
  37. {processlib-0.9.0 → processlib-0.10.0}/processlib.egg-info/SOURCES.txt +0 -0
  38. {processlib-0.9.0 → processlib-0.10.0}/processlib.egg-info/dependency_links.txt +0 -0
  39. {processlib-0.9.0 → processlib-0.10.0}/processlib.egg-info/requires.txt +0 -0
  40. {processlib-0.9.0 → processlib-0.10.0}/processlib.egg-info/top_level.txt +0 -0
  41. {processlib-0.9.0 → processlib-0.10.0}/setup.cfg +0 -0
@@ -1,9 +1,10 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: processlib
3
- Version: 0.9.0
3
+ Version: 0.10.0
4
4
  Summary: A workflow library for python
5
5
  Home-page: https://github.com/RaphaelKimmig/processlib
6
- Download-URL: https://github.com/RaphaelKimmig/processlib/archive/0.9.0.tar.gz
6
+ Download-URL: https://github.com/RaphaelKimmig/processlib/archive/0.10.0.tar.gz
7
7
  Author: Raphael Kimmig
8
8
  Author-email: raphael@ampad.de
9
9
  License-File: LICENSE
10
+ Requires-Dist: django>=3.2
@@ -5,7 +5,4 @@ def autodiscover_flows():
5
5
  autodiscover_modules("flows")
6
6
 
7
7
 
8
- default_app_config = "processlib.apps.ProcesslibAppConfig"
9
-
10
-
11
- __all__ = ["autodiscover_flows", "default_app_config"]
8
+ __all__ = ["autodiscover_flows"]
@@ -1,16 +1,10 @@
1
- import django
2
- import six
3
- from django.http import HttpResponseRedirect
4
-
5
- if django.VERSION[0] < 2:
6
- from django.core.urlresolvers import reverse
7
- else:
8
- from django.urls import reverse
1
+ import logging
9
2
 
10
3
  from django.db import transaction
4
+ from django.http import HttpResponseRedirect
5
+ from django.urls import reverse
11
6
  from django.utils import timezone
12
7
 
13
- import logging
14
8
  from processlib.assignment import inherit
15
9
  from processlib.tasks import run_async_activity
16
10
 
@@ -18,7 +12,6 @@ from processlib.tasks import run_async_activity
18
12
  logger = logging.getLogger(__name__)
19
13
 
20
14
 
21
- @six.python_2_unicode_compatible
22
15
  class Activity(object):
23
16
  def __init__(
24
17
  self,
@@ -61,7 +54,7 @@ class Activity(object):
61
54
  return False
62
55
 
63
56
  def __str__(self):
64
- return six.text_type(self.verbose_name or self.name)
57
+ return str(self.verbose_name or self.name)
65
58
 
66
59
  def __repr__(self):
67
60
  return '{}(name="{}")'.format(self.__class__.__name__, self.name)
@@ -346,40 +339,6 @@ class EndActivity(Activity):
346
339
  self.process.save(update_fields=update_fields)
347
340
 
348
341
 
349
- class EndRedirectActivity(EndActivity):
350
- def __init__(self, redirect_url_callback=None, **kwargs):
351
- self.redirect_url_callback = redirect_url_callback
352
- super(EndActivity, self).__init__(**kwargs)
353
-
354
- def instantiate(self, **kwargs):
355
- # HACK: we skip the EndActivity implementation
356
- # because it would finish the activity right away
357
- super(EndActivity, self).instantiate(**kwargs)
358
-
359
- def has_view(self):
360
- return True
361
-
362
- def get_absolute_url(self):
363
- return reverse(
364
- "processlib:process-activity",
365
- kwargs={"flow_label": self.flow.label, "activity_id": self.instance.pk},
366
- )
367
-
368
- def dispatch(self, request, *args, **kwargs):
369
- self.start()
370
- url = reverse(
371
- "processlib:process-detail", kwargs={"pk": self.instance.process.pk}
372
- )
373
- try:
374
- if self.redirect_url_callback:
375
- url = self.redirect_url_callback(self)
376
- self.finish()
377
- except Exception as e:
378
- logger.exception(e)
379
- self.error(exception=e)
380
- return HttpResponseRedirect(url)
381
-
382
-
383
342
  class FormActivity(Activity):
384
343
  def __init__(self, form_class=None, **kwargs):
385
344
  self.form_class = form_class
@@ -3,9 +3,7 @@ from __future__ import unicode_literals
3
3
  from collections import OrderedDict, defaultdict
4
4
 
5
5
  import logging
6
- import six
7
6
  from django.utils import timezone
8
- from six import python_2_unicode_compatible
9
7
 
10
8
  from .models import Process, ActivityInstance
11
9
 
@@ -34,7 +32,6 @@ def register_flow(flow):
34
32
  _FLOWS[flow.label] = flow
35
33
 
36
34
 
37
- @python_2_unicode_compatible
38
35
  class Flow(object):
39
36
  def __init__(
40
37
  self,
@@ -68,7 +65,7 @@ class Flow(object):
68
65
  )
69
66
 
70
67
  def __str__(self):
71
- return six.text_type(self.verbose_name or self.name)
68
+ return str(self.verbose_name or self.name)
72
69
 
73
70
  def start_with(self, activity_name, activity, **activity_kwargs):
74
71
  if self._activities:
@@ -98,7 +95,7 @@ class Flow(object):
98
95
  predecessors = [after] if after else []
99
96
 
100
97
  if wait_for:
101
- if isinstance(wait_for, six.string_types):
98
+ if isinstance(wait_for, str):
102
99
  raise TypeError("wait_for should be a list or tuple")
103
100
 
104
101
  activity_kwargs["wait_for"] = wait_for
@@ -125,7 +122,7 @@ class Flow(object):
125
122
  process=process,
126
123
  instance=None,
127
124
  name=activity_name,
128
- **self._activity_kwargs[activity_name]
125
+ **self._activity_kwargs[activity_name],
129
126
  )
130
127
 
131
128
  def get_activity_by_instance(self, instance):
@@ -143,7 +140,7 @@ class Flow(object):
143
140
  flow_label=self.label,
144
141
  started_at=timezone.now(),
145
142
  status=self.process_model.STATUS_STARTED,
146
- **(process_kwargs or {})
143
+ **(process_kwargs or {}),
147
144
  )
148
145
  activity = self._get_activity_by_name(process, list(self._activities)[0])
149
146
  activity.instantiate(instance_kwargs=activity_instance_kwargs, request=request)
@@ -1,7 +1,6 @@
1
1
  import uuid
2
2
  import string
3
3
 
4
- import six
5
4
  from django.conf import settings
6
5
  from django.contrib.auth.models import Group
7
6
  from django.core.exceptions import ValidationError
@@ -9,9 +8,6 @@ from django.db import models
9
8
  from django.utils.translation import gettext_lazy as _
10
9
 
11
10
 
12
- from six import python_2_unicode_compatible
13
-
14
-
15
11
  def validate_flow_label(value):
16
12
  from .flow import _FLOWS
17
13
 
@@ -24,14 +20,13 @@ def validate_flow_label(value):
24
20
 
25
21
  def is_format_string(value):
26
22
  try:
27
- parsed = next(string.Formatter().parse(six.text_type(value)))
23
+ parsed = next(string.Formatter().parse(str(value)))
28
24
  except ValueError:
29
25
  return False
30
26
 
31
27
  return parsed[1] is not None
32
28
 
33
29
 
34
- @python_2_unicode_compatible
35
30
  class Process(models.Model):
36
31
  STATUS_STARTED = "started"
37
32
  STATUS_CANCELED = "canceled"
@@ -61,9 +56,9 @@ class Process(models.Model):
61
56
 
62
57
  def __str__(self):
63
58
  if not self.flow:
64
- return six.text_type(self.id)
59
+ return str(self.id)
65
60
  if self.flow.verbose_name:
66
- return six.text_type(self.flow.verbose_name)
61
+ return str(self.flow.verbose_name)
67
62
  return self.flow.name
68
63
 
69
64
  @property
@@ -73,16 +68,16 @@ class Process(models.Model):
73
68
  def can_cancel(self, user=None):
74
69
  return (
75
70
  self.status not in (self.STATUS_DONE, self.STATUS_CANCELED)
76
- and not
71
+ and
77
72
  # FIXME allow cancelation of scheduled instances
78
- self._activity_instances.filter(
73
+ not self._activity_instances.filter(
79
74
  status=ActivityInstance.STATUS_SCHEDULED
80
75
  ).exists()
81
76
  )
82
77
 
83
78
  @property
84
79
  def description(self):
85
- flow_description = six.text_type(self.flow.description or self.flow)
80
+ flow_description = str(self.flow.description or self.flow)
86
81
  if not is_format_string(flow_description):
87
82
  return flow_description
88
83
  try:
@@ -87,6 +87,9 @@ def cancel_process(process, user):
87
87
 
88
88
 
89
89
  def get_user_processes(user, include_unassigned=True):
90
+ if not user.is_authenticated:
91
+ return Process.objects.none()
92
+
90
93
  q = Q(_activity_instances__assigned_group__in=user.groups.all()) & ~Q(
91
94
  _activity_instances__status=ActivityInstance.STATUS_CANCELED
92
95
  ) | Q(_activity_instances__assigned_user=user) & ~Q(
@@ -104,6 +107,9 @@ def get_user_processes(user, include_unassigned=True):
104
107
 
105
108
 
106
109
  def get_user_current_processes(user, include_unassigned=True):
110
+ if not user.is_authenticated:
111
+ return Process.objects.none()
112
+
107
113
  q = Q(
108
114
  _activity_instances__assigned_group__in=user.groups.all(),
109
115
  _activity_instances__status__in=(
@@ -129,17 +135,23 @@ def get_user_current_processes(user, include_unassigned=True):
129
135
  )
130
136
 
131
137
  q &= get_permission_filter(user)
132
- return Process.objects.filter(
133
- status=Process.STATUS_STARTED,
134
- ).filter(q).distinct()
138
+ return (
139
+ Process.objects.filter(
140
+ status=Process.STATUS_STARTED,
141
+ )
142
+ .filter(q)
143
+ .distinct()
144
+ )
135
145
 
136
146
 
137
147
  def get_permission_filter(user):
138
148
  flows = get_flows()
139
- flows_label_list = [label for (label, flow) in flows if not flow.permission or user.has_perm(flow.permission)]
140
- return Q(
141
- _activity_instances__process__flow_label__in=flows_label_list
142
- )
149
+ flows_label_list = [
150
+ label
151
+ for (label, flow) in flows
152
+ if not flow.permission or user.has_perm(flow.permission)
153
+ ]
154
+ return Q(_activity_instances__process__flow_label__in=flows_label_list)
143
155
 
144
156
 
145
157
  def user_has_any_process_perm(user, process):
@@ -162,7 +174,9 @@ def user_has_activity_perm(user, activity):
162
174
  # if there are no required permissions we grant access
163
175
  return True
164
176
  elif activity.permission and activity.flow.permission:
165
- return user.has_perm(activity.permission) and user.has_perm(activity.flow.permission)
177
+ return user.has_perm(activity.permission) and user.has_perm(
178
+ activity.flow.permission
179
+ )
166
180
  elif activity.permission:
167
181
  return user.has_perm(activity.permission)
168
182
  elif activity.flow.permission:
@@ -1,6 +1,5 @@
1
1
  from logging import getLogger
2
2
 
3
- import six
4
3
  from django.contrib.auth.models import Permission
5
4
  from django.contrib.contenttypes.models import ContentType
6
5
  from django.db.models.signals import pre_migrate
@@ -28,15 +27,21 @@ def create_flow_permissions(app_config, **kwargs):
28
27
  flow.permission, app_label, process_content_type.app_label
29
28
  )
30
29
  )
31
- if Permission.objects.filter(codename=codename).exclude(content_type=process_content_type).exists():
30
+ if (
31
+ Permission.objects.filter(codename=codename)
32
+ .exclude(content_type=process_content_type)
33
+ .exists()
34
+ ):
32
35
  # app_label.codename should match process_content_type, otherwise that doesn't really make sense
33
- raise ValueError("A permission for {}.{} with a different content type already"
34
- " exists. Set auto_create_permission to False or remove the other"
35
- " permission")
36
+ raise ValueError(
37
+ "A permission for {}.{} with a different content type already"
38
+ " exists. Set auto_create_permission to False or remove the other"
39
+ " permission"
40
+ )
36
41
  Permission.objects.update_or_create(
37
42
  content_type=process_content_type,
38
43
  codename=codename,
39
- defaults={"name": six.text_type(flow)},
44
+ defaults={"name": str(flow)},
40
45
  )
41
46
  activity_content_type = ContentType.objects.get_for_model(flow.process_model)
42
47
  for activity_name in flow._activities:
@@ -47,11 +52,17 @@ def create_flow_permissions(app_config, **kwargs):
47
52
  # split like django
48
53
  app_label, codename = activity.permission.split(".", 1)
49
54
 
50
- if Permission.objects.filter(codename=codename).exclude(content_type=activity_content_type).exists():
55
+ if (
56
+ Permission.objects.filter(codename=codename)
57
+ .exclude(content_type=activity_content_type)
58
+ .exists()
59
+ ):
51
60
  # app_label.codename should match activity_content_type, otherwise that doesn't really make sense
52
- raise ValueError("A permission for {}.{} with a different content type already"
53
- " exists. Set auto_create_permission to False or remove the other"
54
- " permission")
61
+ raise ValueError(
62
+ "A permission for {}.{} with a different content type already"
63
+ " exists. Set auto_create_permission to False or remove the other"
64
+ " permission"
65
+ )
55
66
 
56
67
  if app_label != activity_content_type.app_label:
57
68
  raise ValueError(
@@ -65,9 +76,7 @@ def create_flow_permissions(app_config, **kwargs):
65
76
  Permission.objects.update_or_create(
66
77
  content_type=activity_content_type,
67
78
  codename=codename,
68
- defaults={
69
- "name": "{} - {}".format(six.text_type(flow), six.text_type(name))
70
- },
79
+ defaults={"name": "{} - {}".format(str(flow), str(name))},
71
80
  )
72
81
 
73
82
 
@@ -9,7 +9,13 @@ from django.urls import reverse
9
9
 
10
10
  from processlib.activity import FunctionActivity, AsyncActivity
11
11
  from processlib.forms import ProcessCancelForm
12
- from .activity import StartActivity, EndActivity, ViewActivity, Wait, StartViewActivity
12
+ from .activity import (
13
+ StartActivity,
14
+ EndActivity,
15
+ ViewActivity,
16
+ Wait,
17
+ StartViewActivity,
18
+ )
13
19
  from .assignment import inherit, nobody, request_user
14
20
  from .flow import Flow
15
21
  from .models import ActivityInstance
@@ -393,9 +399,7 @@ class ProcesslibViewPermissionTest(TestCase):
393
399
  self.post_with_permissions = RequestFactory().post("/")
394
400
  self.post_with_permissions.user = self.user_with_perms
395
401
 
396
- def test_process_list_view_respects_flow_permission(
397
- self
398
- ):
402
+ def test_process_list_view_respects_flow_permission(self):
399
403
  no_perm_response = ProcessListView.as_view()(
400
404
  self.get_no_permissions,
401
405
  )
@@ -406,9 +410,7 @@ class ProcesslibViewPermissionTest(TestCase):
406
410
  )
407
411
  self.assertContains(perm_response, self.process.pk)
408
412
 
409
- def test_user_process_list_view_respects_flow_permission(
410
- self
411
- ):
413
+ def test_user_process_list_view_respects_flow_permission(self):
412
414
  no_perm_response = UserProcessListView.as_view()(
413
415
  self.get_no_permissions,
414
416
  )
@@ -419,9 +421,7 @@ class ProcesslibViewPermissionTest(TestCase):
419
421
  )
420
422
  self.assertContains(perm_response, self.process.pk)
421
423
 
422
- def test_user_current_process_list_view_respects_flow_permission(
423
- self
424
- ):
424
+ def test_user_current_process_list_view_respects_flow_permission(self):
425
425
  no_perm_response = UserCurrentProcessListView.as_view()(
426
426
  self.get_no_permissions,
427
427
  )
@@ -433,7 +433,7 @@ class ProcesslibViewPermissionTest(TestCase):
433
433
  self.assertContains(perm_response, self.process.pk)
434
434
 
435
435
  def test_process_detail_view_raises_permission_denied_with_missing_permissions(
436
- self
436
+ self,
437
437
  ):
438
438
  with self.assertRaises(PermissionDenied):
439
439
  ProcessDetailView.as_view()(self.get_no_permissions, pk=self.process.id)
@@ -443,7 +443,7 @@ class ProcesslibViewPermissionTest(TestCase):
443
443
  self.assertEqual(response.status_code, 200)
444
444
 
445
445
  def test_process_cancel_view_raises_permission_denied_with_missing_permissions(
446
- self
446
+ self,
447
447
  ):
448
448
  with self.assertRaises(PermissionDenied):
449
449
  ProcessCancelView.as_view()(self.get_no_permissions, pk=self.process.id)
@@ -474,7 +474,7 @@ class ProcesslibViewPermissionTest(TestCase):
474
474
  self.assertEqual(response.status_code, 302)
475
475
 
476
476
  def test_process_activity_view_raises_permission_denied_with_missing_permissions(
477
- self
477
+ self,
478
478
  ):
479
479
  next_activity = next(get_current_activities_in_process(self.process))
480
480
 
@@ -506,7 +506,7 @@ class ProcesslibViewPermissionTest(TestCase):
506
506
  self.assertEqual(response.status_code, 302)
507
507
 
508
508
  def test_retry_activity_view_raises_permission_denied_with_missing_permissions(
509
- self
509
+ self,
510
510
  ):
511
511
  with self.assertRaises(PermissionDenied):
512
512
  ActivityRetryView.as_view()(
@@ -522,7 +522,7 @@ class ProcesslibViewPermissionTest(TestCase):
522
522
  self.assertEqual(response.status_code, 302)
523
523
 
524
524
  def test_cancel_activity_view_raises_permission_denied_with_missing_permissions(
525
- self
525
+ self,
526
526
  ):
527
527
  next_activity = next(get_current_activities_in_process(self.process))
528
528
  with self.assertRaises(PermissionDenied):
@@ -762,6 +762,56 @@ class ProcesslibViewTest(TestCase):
762
762
  self.assertContains(response, str(self.process.pk))
763
763
 
764
764
 
765
+ end_direct_test_flow = (
766
+ Flow("end_direct_test_flow")
767
+ .start_with("start", StartActivity)
768
+ .and_then(
769
+ "success-view",
770
+ ViewActivity,
771
+ view=ProcessUpdateView.as_view(
772
+ fields=[],
773
+ success_url=lambda a: "/custom/{}".format(a),
774
+ ),
775
+ )
776
+ .and_then("end", EndActivity)
777
+ )
778
+
779
+
780
+ class ProcesslibRedirectViewTest(TestCase):
781
+ def setUp(self):
782
+ self.user = User.objects.create(username="testuser")
783
+ self.user.set_password("password")
784
+ self.user.save()
785
+
786
+ self.start = end_direct_test_flow.get_start_activity()
787
+ self.start.start()
788
+ self.start.finish()
789
+ self.process = self.start.process
790
+ self.next_activity = next(get_current_activities_in_process(self.process))
791
+
792
+ self.get = RequestFactory().get("/")
793
+ self.get.user = self.user
794
+ self.client.login(username="testuser", password="password")
795
+
796
+ def test_end_redirect(self):
797
+ activity_instance = self.next_activity.instance
798
+ url = reverse(
799
+ "processlib:process-activity",
800
+ kwargs={
801
+ "flow_label": self.process.flow_label,
802
+ "activity_id": activity_instance.id,
803
+ },
804
+ )
805
+ response = self.client.post(url, {"_finish_go_to_next": True})
806
+
807
+ self.assertEqual(response.status_code, 302)
808
+ self.assertEqual(response.headers["location"], "/custom/success-view")
809
+
810
+ self.process.refresh_from_db()
811
+
812
+ self.assertIsNotNone(self.process.finished_at)
813
+
814
+
765
815
  class ActivityTest(TestCase):
766
816
  def test_function_activity_with_error_records_error(self):
767
817
  function_error_flow = (
@@ -798,9 +848,9 @@ class ActivityTest(TestCase):
798
848
  activity.instance.assigned_group = Group.objects.create(name="side-effect")
799
849
  activity.instance.save()
800
850
 
801
- function_error_retry_flow._activity_kwargs["function"][
802
- "callback"
803
- ] = working_callback
851
+ function_error_retry_flow._activity_kwargs["function"]["callback"] = (
852
+ working_callback
853
+ )
804
854
 
805
855
  activity_instance.activity.retry()
806
856
  activity_instance.refresh_from_db()
@@ -13,6 +13,10 @@ from processlib.views import (
13
13
  ProcessCancelView,
14
14
  )
15
15
 
16
+
17
+ app_name = "processlib"
18
+
19
+
16
20
  urlpatterns = [
17
21
  re_path(r"^process/$", ProcessListView.as_view(), name="process-list"),
18
22
  re_path(
@@ -20,7 +24,9 @@ urlpatterns = [
20
24
  UserCurrentProcessListView.as_view(),
21
25
  name="process-list-user-current",
22
26
  ),
23
- re_path(r"^process/user/$", UserProcessListView.as_view(), name="process-list-user"),
27
+ re_path(
28
+ r"^process/user/$", UserProcessListView.as_view(), name="process-list-user"
29
+ ),
24
30
  re_path(
25
31
  r"^process/start/(?P<flow_label>.*)/$",
26
32
  ProcessStartView.as_view(),
@@ -46,7 +52,9 @@ urlpatterns = [
46
52
  ActivityRetryView.as_view(),
47
53
  name="activity-retry",
48
54
  ),
49
- re_path(r"^process/(?P<pk>.*)/$", ProcessDetailView.as_view(), name="process-detail"),
55
+ re_path(
56
+ r"^process/(?P<pk>.*)/$", ProcessDetailView.as_view(), name="process-detail"
57
+ ),
50
58
  re_path(
51
59
  r"^process/(?P<pk>.*)/cancel$",
52
60
  ProcessCancelView.as_view(),
@@ -1,4 +1,3 @@
1
- import six
2
1
  from django.contrib import messages
3
2
  from django.core.exceptions import ImproperlyConfigured, PermissionDenied
4
3
  from django.db.models import Q
@@ -58,7 +57,7 @@ class ProcessListView(CurrentAppMixin, ListView):
58
57
  def get_queryset(self):
59
58
  qs = super(ProcessListView, self).get_queryset()
60
59
  qs = self.filter_queryset(qs)
61
- return qs.filter(get_permission_filter(self.request.user))
60
+ return qs.filter(get_permission_filter(self.request.user)).distinct()
62
61
 
63
62
  def get_search_query(self):
64
63
  return self.request.GET.get("search", "").strip()
@@ -273,6 +272,7 @@ class ActivityMixin(CurrentAppMixin):
273
272
  Mixin used by view activities, e.g. those defined as an ActivityView.
274
273
  """
275
274
 
275
+ success_url = None
276
276
  activity = None
277
277
  _finish_go_to_next = False
278
278
 
@@ -323,9 +323,7 @@ class ActivityMixin(CurrentAppMixin):
323
323
  if self.activity.instance.status == self.activity.instance.STATUS_DONE:
324
324
  messages.info(
325
325
  request,
326
- _("The activity {} has already been done.").format(
327
- six.text_type(self.activity)
328
- ),
326
+ _("The activity {} has already been done.").format(str(self.activity)),
329
327
  )
330
328
  return HttpResponseRedirect(
331
329
  reverse(
@@ -356,6 +354,13 @@ class ActivityMixin(CurrentAppMixin):
356
354
  },
357
355
  current_app=self.get_current_app(),
358
356
  )
357
+
358
+ if self.success_url is not None:
359
+ if callable(self.success_url):
360
+ return self.success_url(self.activity)
361
+ else:
362
+ return str(self.success_url)
363
+
359
364
  return reverse(
360
365
  "processlib:process-detail",
361
366
  args=(self.activity.process.id,),
@@ -1,9 +1,10 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: processlib
3
- Version: 0.9.0
3
+ Version: 0.10.0
4
4
  Summary: A workflow library for python
5
5
  Home-page: https://github.com/RaphaelKimmig/processlib
6
- Download-URL: https://github.com/RaphaelKimmig/processlib/archive/0.9.0.tar.gz
6
+ Download-URL: https://github.com/RaphaelKimmig/processlib/archive/0.10.0.tar.gz
7
7
  Author: Raphael Kimmig
8
8
  Author-email: raphael@ampad.de
9
9
  License-File: LICENSE
10
+ Requires-Dist: django>=3.2
@@ -0,0 +1,16 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ setup(
4
+ name="processlib",
5
+ version="0.10.0",
6
+ url="https://github.com/RaphaelKimmig/processlib",
7
+ download_url="https://github.com/RaphaelKimmig/processlib/archive/0.10.0.tar.gz",
8
+ author="Raphael Kimmig",
9
+ author_email="raphael@ampad.de",
10
+ description="A workflow library for python",
11
+ include_package_data=True,
12
+ packages=find_packages(),
13
+ install_requires=[
14
+ "django >= 3.2",
15
+ ],
16
+ )
processlib-0.9.0/setup.py DELETED
@@ -1,14 +0,0 @@
1
- from setuptools import setup, find_packages
2
-
3
- setup(
4
- name='processlib',
5
- version='0.9.0',
6
- url='https://github.com/RaphaelKimmig/processlib',
7
- download_url='https://github.com/RaphaelKimmig/processlib/archive/0.9.0.tar.gz',
8
- author='Raphael Kimmig',
9
- author_email='raphael@ampad.de',
10
- description='A workflow library for python',
11
- include_package_data=True,
12
- packages=find_packages(),
13
- install_requires=['django >= 3.2', ],
14
- )
File without changes
File without changes
File without changes
File without changes