Skip to content

Commit 4a89e97

Browse files
committed
4.6.0
- full admin site implementation
1 parent 0f3501f commit 4a89e97

File tree

7 files changed

+181
-8
lines changed

7 files changed

+181
-8
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@ tests/coverage_html/
1616
tests/.coverage
1717
build/
1818
tests/report/
19+
.idea/

.idea/.gitignore

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# Django Process
33

44
Its a reusable app for execute scrits in workflows with dependecies
5+
NEW VERSION 4.6 it allows you to use the complete app from the admin site with the graphics and custom actions
56

67
## Table of Contents
78
* [usage](#usage)

process/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
__title__ = 'django-process'
22
__description__ = 'An app for create process workflows and schedule tasks on django'
33
__url__ = 'https://github.com/Jesrat/django-process'
4-
__version__ = '4.5.7'
4+
__version__ = '4.6.0'
55
__author__ = 'Josue Gomez'
66
__author_email__ = 'jgomez@jesrat.com'
77
__license__ = 'Apache 2.0'

process/admin.py

Lines changed: 92 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,113 @@
1-
from django.contrib import admin
2-
from .models import Process, Task, TaskDependence, Job, JobTask
1+
from .exceptions import ProcessException
2+
from .forms import JobForm, JobTaskForm, TaskForm
3+
from .models import Job, JobTask, Process, Task, TaskDependence
4+
from django.contrib import admin, messages
5+
from django.utils.translation import ngettext
6+
from django.utils.translation import gettext_lazy as _
37

48

59
# Register your models here.
610
@admin.register(Process)
711
class ProcessAdmin(admin.ModelAdmin):
8-
list_display = ('__str__', 'is_active', 'minute', 'hour', 'day_of_month', 'month', 'day_of_week')
12+
actions = ['run_on_demand']
13+
list_display = ('__str__', 'description', 'is_active', 'recurrence')
14+
list_filter = ('is_active',)
15+
search_fields = ('name', 'description')
16+
17+
# noinspection PyMethodMayBeStatic
18+
def recurrence(self, process):
19+
return '|'.join([process.minute, process.hour, process.day_of_month, process.month, process.day_of_week])
20+
recurrence.short_description = 'recurrence'
21+
22+
def run_on_demand(self, request, queryset):
23+
for process in queryset:
24+
__, __ = Job.create(process)
25+
self.message_user(request, ngettext(
26+
'%d process was successfully started.',
27+
'%d processes were successfully started.',
28+
len(queryset),
29+
) % len(queryset), messages.SUCCESS)
30+
run_on_demand.short_description = _('run on demand')
931

1032

1133
@admin.register(Task)
1234
class TaskAdmin(admin.ModelAdmin):
35+
form = TaskForm
1336
list_display = ('__str__', 'is_active', 'process')
1437

15-
16-
@admin.register(TaskDependence)
17-
class TaskDependenceAdmin(admin.ModelAdmin):
18-
list_display = ('__str__', 'task', 'parent')
38+
# noinspection PyMethodMayBeStatic,PyUnusedLocal
39+
def get_readonly_fields(self, request, obj=None):
40+
if obj:
41+
return self.readonly_fields + ('process',)
42+
return self.readonly_fields
1943

2044

2145
@admin.register(Job)
2246
class JobAdmin(admin.ModelAdmin):
47+
form = JobForm
48+
actions = ['cancel']
2349
list_display = ('__str__', 'dt_start', 'dt_end', 'observations')
50+
list_filter = ('status',)
51+
change_form_template = 'process/admin_change_job.html'
52+
53+
# noinspection PyMethodMayBeStatic,PyUnusedLocal
54+
def get_readonly_fields(self, request, obj=None):
55+
if obj and obj.status not in Job.cancelable:
56+
return 'process', 'status', 'dt_start', 'dt_end', 'observations'
57+
return 'process', 'dt_start', 'dt_end', 'observations'
58+
59+
def get_queryst(self, request):
60+
qs = Job.objects.all().prefetch_related('process')
61+
# TODO: this should be handled by some parameter to the ChangeList.
62+
ordering = self.get_ordering(request)
63+
if ordering:
64+
qs = qs.order_by(*ordering)
65+
return qs
66+
67+
# noinspection PyMethodMayBeStatic, PyUnusedLocal
68+
def has_add_permission(self, request):
69+
return False
70+
71+
def cancel(self, request, queryset):
72+
# check cancelable
73+
not_cancelable = [j for j in queryset if j.status not in Job.cancelable]
74+
if not_cancelable:
75+
self.message_user(
76+
request,
77+
ngettext(
78+
'following job is not in cancelable status: %(job)s',
79+
'following jobs are not in cancelable status: %(job)s',
80+
len(not_cancelable)
81+
) % {'job': ', '.join([str(job) for job in not_cancelable])},
82+
messages.ERROR
83+
)
84+
return
85+
# attempt to cancel
86+
try:
87+
for job in queryset:
88+
job.cancel()
89+
except ProcessException as e:
90+
self.message_user(_('there was an exception when cancelling jobs %s') % e, messages.ERROR)
91+
return
92+
# success
93+
self.message_user(request, ngettext(
94+
'%d job was successfully canceled.',
95+
'%d jobs were successfully canceled.',
96+
len(queryset),
97+
) % len(queryset), messages.SUCCESS)
98+
cancel.short_description = _('cancel')
2499

25100

26101
@admin.register(JobTask)
27102
class JobTaskAdmin(admin.ModelAdmin):
103+
form = JobTaskForm
28104
list_display = ('__str__', 'dt_start', 'dt_end', 'observations')
105+
106+
def get_readonly_fields(self, request, obj=None):
107+
if obj:
108+
return self.readonly_fields + ('job', 'task', 'dt_start', 'dt_end', 'observations')
109+
return self.readonly_fields
110+
111+
# noinspection PyMethodMayBeStatic, PyUnusedLocal
112+
def has_add_permission(self, request):
113+
return False

process/forms.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
from .models import Job, JobTask, Task, TaskDependence
2+
from django import forms
3+
from django.db.models import Q
4+
5+
6+
class JobForm(forms.ModelForm):
7+
8+
def __init__(self, *args, **kwargs):
9+
super().__init__(*args, **kwargs)
10+
status = self.instance.status
11+
choices = (('cancelled', 'cancelled'), (status, status)) if status in Job.cancelable else []
12+
if self.fields.get('status'):
13+
self.fields['status'].choices = choices
14+
15+
class Meta:
16+
model = Job
17+
fields = '__all__'
18+
19+
# noinspection PyUnusedLocal,PyAttributeOutsideInit
20+
def save(self, **kwargs):
21+
self.save_m2m = self._save_m2m
22+
if self.instance.status == Job.cancelled:
23+
self.instance.refresh_from_db()
24+
if self.instance.status in Job.cancelable:
25+
self.instance.cancel()
26+
return self.instance
27+
28+
29+
class TaskForm(forms.ModelForm):
30+
parents = forms.ModelMultipleChoiceField(queryset=Task.objects.filter(pk=0), required=False)
31+
32+
def __init__(self, *args, **kwargs):
33+
super().__init__(*args, **kwargs)
34+
if self.instance.pk:
35+
self.fields['parents'].queryset = self.instance.process.tasks.filter(~Q(pk=self.instance.pk))
36+
self.fields['parents'].initial = [d.parent for d in self.instance.parents.all()]
37+
38+
class Meta:
39+
model = Task
40+
fields = '__all__'
41+
42+
def _save_m2m(self):
43+
print(f'cleaned_data {self.cleaned_data}')
44+
current_queryset = self.instance.parents.all()
45+
for current in current_queryset:
46+
if current.parent not in self.cleaned_data['parents']:
47+
current.delete()
48+
for parent in self.cleaned_data['parents']:
49+
if parent not in [c.parent for c in current_queryset]:
50+
relation = TaskDependence(parent=parent, task=self.instance)
51+
relation.save()
52+
53+
54+
class JobTaskForm(forms.ModelForm):
55+
56+
def __init__(self, *args, **kwargs):
57+
super().__init__(*args, **kwargs)
58+
if self.instance.pk:
59+
choices = [(stat, stat) for name, opt_list, stat in JobTask.management if self.instance.status in opt_list]
60+
choices.append((self.instance.status, self.instance.status))
61+
self.fields['status'].choices = choices
62+
63+
class Meta:
64+
model = JobTask
65+
fields = '__all__'
66+
67+
def save(self, **kwargs):
68+
if self.instance.status == JobTask.reopened:
69+
self.instance.reopen(main=True)
70+
return self.instance
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{% extends "admin/change_form.html" %}
2+
{% load process_diagram i18n admin_urls static admin_modify %}
3+
{% block extrahead %}
4+
{{ block.super }}
5+
{% include "process/dj-process.html" %}
6+
<style>
7+
#container {
8+
margin: 0;
9+
}
10+
</style>
11+
{% endblock %}
12+
{% block submit_buttons_bottom %}
13+
{{ original|diagram }}
14+
{{ block.super }}
15+
{% endblock %}

0 commit comments

Comments
 (0)