Skip to content

Commit 10cf234

Browse files
committed
Add optional django-reversion support
1 parent 64109c7 commit 10cf234

File tree

14 files changed

+156
-28
lines changed

14 files changed

+156
-28
lines changed

.travis.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ env:
2020
- DJANGO=21
2121
- DJANGO=master
2222
- TOXENV=docs
23+
- TOXENV=reversion
2324
matrix:
2425
fast_finish: true
2526
allow_failures:
@@ -29,6 +30,10 @@ matrix:
2930
env: TOXENV=docs
3031
- python: "3.7"
3132
env: TOXENV=docs
33+
- python: "3.5"
34+
env: TOXENV=reversion
35+
- python: "3.6"
36+
env: TOXENV=reversion
3237
before_script:
3338
- |
3439
if [[ -z $TOXENV ]]; then

galahad/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
"""The workflow automation framework for machines with heart."""
2+
default_app_config = 'galahad.apps.GalahadConfig'

galahad/admin.py

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,31 @@
44
from django.utils.translation import ugettext_lazy as t
55

66
from . import models
7+
from .contrib.reversion import VersionAdmin
8+
9+
10+
__all__ = (
11+
'ProcessAdmin',
12+
)
13+
14+
15+
def rerun(modeladmin, request, queryset):
16+
succeeded = queryset.succeeded().count()
17+
if succeeded:
18+
messages.warning(request, "Only failed tasks can be retried. %s tasks have been skipped" % succeeded)
19+
counter = 0
20+
for obj in queryset.not_succeeded().iterator():
21+
obj.enqueue()
22+
counter += 1
23+
messages.success(request, "%s tasks have been successfully queued" % counter)
24+
25+
26+
rerun.short_description = t('Rerun selected tasks')
27+
rerun.allowed_permissions = ('rerun',)
728

829

930
@admin.register(models.Task)
10-
class TaskAdmin(admin.ModelAdmin):
11-
@staticmethod
12-
def rerun(modeladmin, request, queryset):
13-
succeeded = queryset.succeeded().count()
14-
if succeeded:
15-
messages.warning(request, "Only failed tasks can be retried. %s tasks have been skipped" % succeeded)
16-
counter = 0
17-
for obj in queryset.not_succeeded().iterator():
18-
obj.enqueue()
19-
counter += 1
20-
messages.success(request, "%s tasks have been successfully queued" % counter)
21-
rerun.short_description = t('Rerun tasks')
22-
rerun.allowed_permissions = ('rerun',)
31+
class TaskAdmin(VersionAdmin):
2332

2433
def has_rerun_permission(self, request):
2534
opts = self.opts
@@ -28,13 +37,15 @@ def has_rerun_permission(self, request):
2837

2938
def pretty_stacktrace(self, obj):
3039
return format_html('<pre class="readonly collapse">{}<pre>', obj.stacktrace)
40+
3141
pretty_stacktrace.short_description = t('Traceback')
3242

3343
def child_tasks(self, obj):
3444
return ", ".join(str(task) for task in obj.child_task_set.all().iterator())
45+
3546
child_tasks.short_description = t('Child tasks')
3647

37-
actions = ('rerun',)
48+
actions = (rerun,)
3849

3950
list_display = (
4051
'node_name',
@@ -85,3 +96,15 @@ def child_tasks(self, obj):
8596
'fields': ('pretty_stacktrace',),
8697
}),
8798
)
99+
100+
101+
class ProcessAdmin(VersionAdmin):
102+
readonly_fields = (
103+
'modified',
104+
'created',
105+
)
106+
107+
list_filter = (
108+
'modified',
109+
'created',
110+
)

galahad/apps.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from django.apps import AppConfig
2+
from django.utils.translation import ugettext_lazy as t
3+
4+
5+
class GalahadConfig(AppConfig):
6+
name = 'galahad'
7+
verbose_name = t('Galahad')
8+
9+
def ready(self):
10+
from .contrib.reversion import register_processes
11+
register_processes()

galahad/celery.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from django.apps import apps
66
from django.db import transaction
77

8+
from galahad.contrib.reversion import with_reversion
89
from . import locking
910

1011
logger = logging.getLogger('galahad')
@@ -34,10 +35,12 @@ def task_wrapper(self, task_pk, process_pk):
3435
try:
3536
logger.info("Executing %r", task)
3637
node = getattr(type(process), task.node_name)
37-
if getattr(node, 'with_task', False):
38-
result = node(process, task)
39-
else:
40-
result = node(process)
38+
with_task = getattr(node, 'with_task', False)
39+
kwargs = {}
40+
if with_task:
41+
kwargs['task'] = task
42+
with with_reversion(task):
43+
result = node(process, **kwargs)
4144
except: # NoQA
4245
task.fail()
4346
logger.exception("Execution of %r failed", task)

galahad/contrib/__init__.py

Whitespace-only changes.

galahad/contrib/reversion.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
from contextlib import contextmanager
2+
3+
from galahad import utils
4+
5+
__all__ = (
6+
'register_processes',
7+
'RevisionMixin',
8+
'with_reversion',
9+
'VersionAdmin',
10+
)
11+
12+
13+
def register_processes():
14+
try:
15+
from reversion import revisions
16+
except ImportError:
17+
pass
18+
else:
19+
for model in utils.get_processes():
20+
if not revisions.is_registered(model):
21+
revisions.register(model)
22+
23+
24+
@contextmanager
25+
def with_reversion(task):
26+
try:
27+
import reversion
28+
except ImportError:
29+
yield
30+
else:
31+
with reversion.create_revision():
32+
yield
33+
reversion.set_comment(task.node_name)
34+
35+
36+
try:
37+
import reversion
38+
from reversion.views import RevisionMixin as _RevisionMixin
39+
40+
class RevisionMixin(_RevisionMixin):
41+
def dispatch(self, request, *args, **kwargs):
42+
if self.revision_request_creates_revision(request):
43+
reversion.set_comment(self.node_name)
44+
return super().dispatch(request, *args, **kwargs)
45+
46+
except ImportError:
47+
class RevisionMixin:
48+
pass
49+
50+
try:
51+
from reversion.admin import VersionAdmin
52+
except ImportError:
53+
from django.contrib.admin import ModelAdmin as VersionAdmin

galahad/models.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from django.utils.translation import ugettext_lazy as t
1212
from django.views.generic.edit import BaseCreateView
1313

14-
from galahad import views, celery, tasks
14+
from galahad import views, celery, tasks, utils
1515
from .conf import settings
1616

1717
logger = logging.getLogger(__name__)
@@ -282,8 +282,7 @@ def process_subclasses():
282282

283283
apps.check_models_ready()
284284
query = models.Q()
285-
for model in apps.get_models():
286-
if issubclass(model, Process) and model is not Process:
285+
for model in utils.get_processes():
287286
opts = model._meta
288287
query |= models.Q(app_label=opts.app_label, model=opts.model_name)
289288
return query
@@ -421,6 +420,8 @@ def node(self):
421420
def finish(self, user=None):
422421
self.completed = timezone.now()
423422
self.status = self.SUCCEEDED
423+
if user and not user.is_authenticated:
424+
user = None
424425
self.completed_by_user = user
425426
if self.pk:
426427
self.save(update_fields=[

galahad/utils.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import types
2+
3+
4+
def get_processes() -> types.GeneratorType:
5+
"""Return all processes"""
6+
from django.apps import apps
7+
from .models import Process
8+
9+
apps.check_models_ready()
10+
for model in apps.get_models():
11+
if issubclass(model, Process) and model is not Process:
12+
yield model

galahad/views.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from django.views import generic
77

88
from . import models
9+
from .contrib.reversion import RevisionMixin
910

1011

1112
class ProcessTemplateNameViewMixin:
@@ -27,7 +28,7 @@ def get_template_names(self):
2728
return names
2829

2930

30-
class TaskViewMixin(ProcessTemplateNameViewMixin):
31+
class TaskViewMixin(ProcessTemplateNameViewMixin, RevisionMixin):
3132
node_name = None
3233

3334
def __init__(self, **kwargs):
@@ -56,10 +57,7 @@ def post(self, request, *args, **kwargs):
5657
response = super().post(request, *args, **kwargs)
5758
task = self.get_task()
5859
task.process = self.object
59-
if request.user.is_authenticated:
60-
task.finish(request.user)
61-
else:
62-
task.finish()
60+
task.finish(request.user)
6361
task.start_next_tasks()
6462
return response
6563

@@ -68,7 +66,7 @@ class ProcessDetailView(ProcessTemplateNameViewMixin, generic.DetailView):
6866
pass
6967

7068

71-
class ManualOverrideView(PermissionRequiredMixin, ProcessTemplateNameViewMixin, generic.UpdateView):
69+
class ManualOverrideView(PermissionRequiredMixin, RevisionMixin, ProcessTemplateNameViewMixin, generic.UpdateView):
7270
permission_required = 'override'
7371
node_name = 'manual_override'
7472
fields = '__all__'

0 commit comments

Comments
 (0)