Work on job scheduling:
* Added JobResult form filtersets * Change housekeeping cleanup delete from `_raw_delete` to `delete` to make sure scheduled tasks are cancelled * Change default sort of JobResult table to -created * Added `delete` override to `JobResult` to remove scheduled tasks from RQ when a JobResult is deleted * Updated js/css dist files. Will need to be redone when develop is merged to feature.
This commit is contained in:
parent
53c8a48244
commit
679a9e839b
|
|
@ -141,7 +141,7 @@ class LogLevelChoices(ChoiceSet):
|
|||
class JobResultStatusChoices(ChoiceSet):
|
||||
|
||||
STATUS_PENDING = 'pending'
|
||||
STATUS_SCHEDULED = 'pending'
|
||||
STATUS_SCHEDULED = 'scheduled'
|
||||
STATUS_RUNNING = 'running'
|
||||
STATUS_COMPLETED = 'completed'
|
||||
STATUS_ERRORED = 'errored'
|
||||
|
|
@ -149,7 +149,7 @@ class JobResultStatusChoices(ChoiceSet):
|
|||
|
||||
CHOICES = (
|
||||
(STATUS_PENDING, 'Pending'),
|
||||
(STATUS_SCHEDULED, 'Pending'),
|
||||
(STATUS_SCHEDULED, 'Scheduled'),
|
||||
(STATUS_RUNNING, 'Running'),
|
||||
(STATUS_COMPLETED, 'Completed'),
|
||||
(STATUS_ERRORED, 'Errored'),
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ class CustomFieldFilterSet(BaseFilterSet):
|
|||
Q(description__icontains=value)
|
||||
)
|
||||
|
||||
|
||||
class CustomLinkFilterSet(BaseFilterSet):
|
||||
q = django_filters.CharFilter(
|
||||
method='search',
|
||||
|
|
@ -434,8 +435,8 @@ class JobResultFilterSet(BaseFilterSet):
|
|||
method='search',
|
||||
label='Search',
|
||||
)
|
||||
created = django_filters.DateTimeFilter()
|
||||
completed = django_filters.DateTimeFilter()
|
||||
created = django_filters.DateTimeFromToRangeFilter()
|
||||
completed = django_filters.DateTimeFromToRangeFilter()
|
||||
status = django_filters.MultipleChoiceFilter(
|
||||
choices=JobResultStatusChoices,
|
||||
null_value=None
|
||||
|
|
|
|||
|
|
@ -69,7 +69,43 @@ class CustomFieldFilterForm(FilterForm):
|
|||
class JobResultFilterForm(FilterForm):
|
||||
fieldsets = (
|
||||
(None, ('q',)),
|
||||
#('Attributes', ('type', 'content_type_id', 'group_name', 'weight', 'required', 'ui_visibility')),
|
||||
('Attributes', ('obj_type', 'status')),
|
||||
('Creation', ('created_before', 'created_after', 'completed_before', 'completed_after', 'user')),
|
||||
)
|
||||
|
||||
obj_type = ContentTypeChoiceField(
|
||||
label=_('Object Type'),
|
||||
queryset=ContentType.objects.all(),
|
||||
limit_choices_to=FeatureQuery('job_results'), # TODO: This doesn't actually work
|
||||
required=False,
|
||||
)
|
||||
status = MultipleChoiceField(
|
||||
choices=JobResultStatusChoices,
|
||||
required=False
|
||||
)
|
||||
created_after = forms.DateTimeField(
|
||||
required=False,
|
||||
widget=DateTimePicker()
|
||||
)
|
||||
created_before = forms.DateTimeField(
|
||||
required=False,
|
||||
widget=DateTimePicker()
|
||||
)
|
||||
completed_after = forms.DateTimeField(
|
||||
required=False,
|
||||
widget=DateTimePicker()
|
||||
)
|
||||
completed_before = forms.DateTimeField(
|
||||
required=False,
|
||||
widget=DateTimePicker()
|
||||
)
|
||||
user = DynamicModelMultipleChoiceField(
|
||||
queryset=User.objects.all(),
|
||||
required=False,
|
||||
label=_('User'),
|
||||
widget=APISelectMultiple(
|
||||
api_url='/api/users/users/',
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -13,4 +13,4 @@ class ReportForm(BootstrapMixin, forms.Form):
|
|||
widget=DateTimePicker(),
|
||||
label="Schedule at",
|
||||
help_text="Schedule execution of report to a set time",
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ class Command(BaseCommand):
|
|||
ending=""
|
||||
)
|
||||
self.stdout.flush()
|
||||
JobResult.objects.filter(created__lt=cutoff)._raw_delete(using=DEFAULT_DB_ALIAS)
|
||||
JobResult.objects.filter(created__lt=cutoff).delete(using=DEFAULT_DB_ALIAS)
|
||||
if options['verbosity']:
|
||||
self.stdout.write("Done.", self.style.SUCCESS)
|
||||
elif options['verbosity']:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
# Generated by Django 4.1.1 on 2022-10-09 18:37
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('extras', '0078_unique_constraints'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='jobresult',
|
||||
options={'ordering': ['-created']},
|
||||
),
|
||||
]
|
||||
|
|
@ -528,13 +528,22 @@ class JobResult(models.Model):
|
|||
objects = RestrictedQuerySet.as_manager()
|
||||
|
||||
class Meta:
|
||||
ordering = ['obj_type', 'name', '-created']
|
||||
ordering = ['-created']
|
||||
|
||||
def __str__(self):
|
||||
return str(self.job_id)
|
||||
|
||||
def delete(self, *args, **kwargs):
|
||||
queue = django_rq.get_queue("default")
|
||||
job = queue.fetch_job(str(self.job_id))
|
||||
|
||||
if job:
|
||||
job.cancel()
|
||||
|
||||
return super().delete(*args, **kwargs)
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('extras:jobresult', args=[self.pk])
|
||||
return reverse(f'extras:{self.obj_type.name}_result', args=[self.pk])
|
||||
|
||||
@property
|
||||
def duration(self):
|
||||
|
|
@ -579,7 +588,7 @@ class JobResult(models.Model):
|
|||
if schedule_at := kwargs.pop("schedule_at", None):
|
||||
job_result.status = JobResultStatusChoices.STATUS_SCHEDULED
|
||||
job_result.save()
|
||||
|
||||
|
||||
queue.enqueue_at(schedule_at, func, job_id=str(job_result.job_id), job_result=job_result, **kwargs)
|
||||
else:
|
||||
queue.enqueue(func, job_id=str(job_result.job_id), job_result=job_result, **kwargs)
|
||||
|
|
|
|||
|
|
@ -56,9 +56,9 @@ class JobResultTable(NetBoxTable):
|
|||
class Meta(NetBoxTable.Meta):
|
||||
model = JobResult
|
||||
fields = (
|
||||
'pk', 'id', 'name', 'obj_type', 'created', 'completed', 'user', 'status', 'job_id',
|
||||
'pk', 'id', 'name', 'obj_type', 'job_id', 'created', 'completed', 'user', 'status',
|
||||
)
|
||||
default_columns = ('pk', 'id', 'name', 'obj_type', 'created', 'completed', 'user', 'status', 'job_id')
|
||||
default_columns = ('pk', 'id', 'name', 'obj_type', 'status', 'created', 'completed', 'user',)
|
||||
|
||||
|
||||
#
|
||||
|
|
|
|||
|
|
@ -76,7 +76,6 @@ urlpatterns = [
|
|||
|
||||
# Job results
|
||||
path('job-results/', views.JobResultListView.as_view(), name='jobresult_list'),
|
||||
path('job-results/<int:pk>/', views.JobResultView.as_view(), name='jobresult'),
|
||||
path('job-results/delete/', views.JobResultBulkDeleteView.as_view(), name='jobresult_bulk_delete'),
|
||||
path('job-results/<int:pk>/delete/', views.JobResultDeleteView.as_view(), name='jobresult_delete'),
|
||||
|
||||
|
|
|
|||
|
|
@ -815,6 +815,7 @@ class JobResultListView(generic.ObjectListView):
|
|||
table = tables.JobResultTable
|
||||
actions = ('delete', 'bulk_delete', )
|
||||
|
||||
|
||||
class JobResultDeleteView(generic.ObjectDeleteView):
|
||||
queryset = JobResult.objects.all()
|
||||
|
||||
|
|
@ -822,4 +823,4 @@ class JobResultDeleteView(generic.ObjectDeleteView):
|
|||
class JobResultBulkDeleteView(generic.BulkDeleteView):
|
||||
queryset = JobResult.objects.all()
|
||||
filterset = filtersets.JobResultFilterSet
|
||||
table = tables.JobResultTable
|
||||
table = tables.JobResultTable
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1,60 +0,0 @@
|
|||
{% extends 'generic/object.html' %}
|
||||
{% load helpers %}
|
||||
{% load plugins %}
|
||||
|
||||
{% block title %}{{ object.name }} ({{object.job_id}}){% endblock %}
|
||||
|
||||
{# JobResult does not support add/edit controls #}
|
||||
{% block controls %}{% endblock %}
|
||||
{% block subtitle %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col col-md-6">
|
||||
<div class="card">
|
||||
<h5 class="card-header">
|
||||
Tag
|
||||
</h5>
|
||||
<div class="card-body">
|
||||
<table class="table table-hover panel-body attr-table">
|
||||
<tr>
|
||||
<th scope="row">Name</th>
|
||||
<td>
|
||||
{{ object.name }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Created</th>
|
||||
<td>
|
||||
{{ object.created|annotated_date }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Completed</th>
|
||||
<td>
|
||||
{{ object.completed|annotated_date }}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col col-md-6">
|
||||
<div class="card">
|
||||
<h5 class="card-header">
|
||||
TODO
|
||||
</h5>
|
||||
<div class="card-body">
|
||||
<table class="table table-hover panel-body attr-table">
|
||||
TODO
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col col-md-12">
|
||||
{% plugin_full_width_page object %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
Loading…
Reference in New Issue