mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-24 09:28:38 -06:00
Add scheduling for reports and scripts
This commit is contained in:
parent
41d653738a
commit
824b4e0923
16
netbox/extras/forms/reports.py
Normal file
16
netbox/extras/forms/reports.py
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
from django import forms
|
||||||
|
|
||||||
|
from utilities.forms import BootstrapMixin, DateTimePicker
|
||||||
|
|
||||||
|
__all__ = (
|
||||||
|
'ReportForm',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ReportForm(BootstrapMixin, forms.Form):
|
||||||
|
schedule_at = forms.DateTimeField(
|
||||||
|
required=False,
|
||||||
|
widget=DateTimePicker(),
|
||||||
|
label="Schedule at",
|
||||||
|
help_text="Schedule execution of report to a set time",
|
||||||
|
)
|
@ -1,6 +1,6 @@
|
|||||||
from django import forms
|
from django import forms
|
||||||
|
|
||||||
from utilities.forms import BootstrapMixin
|
from utilities.forms import BootstrapMixin, DateTimePicker
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'ScriptForm',
|
'ScriptForm',
|
||||||
@ -14,17 +14,25 @@ class ScriptForm(BootstrapMixin, forms.Form):
|
|||||||
label="Commit changes",
|
label="Commit changes",
|
||||||
help_text="Commit changes to the database (uncheck for a dry-run)"
|
help_text="Commit changes to the database (uncheck for a dry-run)"
|
||||||
)
|
)
|
||||||
|
_schedule_at = forms.DateTimeField(
|
||||||
|
required=False,
|
||||||
|
widget=DateTimePicker(),
|
||||||
|
label="Schedule at",
|
||||||
|
help_text="Schedule execution of script to a set time",
|
||||||
|
)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
# Move _commit to the end of the form
|
# Move _commit and _schedule_at to the end of the form
|
||||||
|
schedule_at = self.fields.pop('_schedule_at')
|
||||||
commit = self.fields.pop('_commit')
|
commit = self.fields.pop('_commit')
|
||||||
|
self.fields['_schedule_at'] = schedule_at
|
||||||
self.fields['_commit'] = commit
|
self.fields['_commit'] = commit
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def requires_input(self):
|
def requires_input(self):
|
||||||
"""
|
"""
|
||||||
A boolean indicating whether the form requires user input (ignore the _commit field).
|
A boolean indicating whether the form requires user input (ignore the _commit and _schedule_at fields).
|
||||||
"""
|
"""
|
||||||
return bool(len(self.fields) > 1)
|
return bool(len(self.fields) > 2)
|
||||||
|
@ -550,7 +550,11 @@ class JobResult(models.Model):
|
|||||||
)
|
)
|
||||||
|
|
||||||
queue = django_rq.get_queue("default")
|
queue = django_rq.get_queue("default")
|
||||||
queue.enqueue(func, job_id=str(job_result.job_id), job_result=job_result, **kwargs)
|
|
||||||
|
if schedule_at := kwargs.pop("schedule_at", None):
|
||||||
|
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)
|
||||||
|
|
||||||
return job_result
|
return job_result
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ from utilities.utils import copy_safe_request, count_related, get_viewname, norm
|
|||||||
from utilities.views import ContentTypePermissionRequiredMixin
|
from utilities.views import ContentTypePermissionRequiredMixin
|
||||||
from . import filtersets, forms, tables
|
from . import filtersets, forms, tables
|
||||||
from .choices import JobResultStatusChoices
|
from .choices import JobResultStatusChoices
|
||||||
|
from .forms.reports import ReportForm
|
||||||
from .models import *
|
from .models import *
|
||||||
from .reports import get_report, get_reports, run_report
|
from .reports import get_report, get_reports, run_report
|
||||||
from .scripts import get_scripts, run_script
|
from .scripts import get_scripts, run_script
|
||||||
@ -562,7 +563,7 @@ class ReportView(ContentTypePermissionRequiredMixin, View):
|
|||||||
|
|
||||||
return render(request, 'extras/report.html', {
|
return render(request, 'extras/report.html', {
|
||||||
'report': report,
|
'report': report,
|
||||||
'run_form': ConfirmationForm(),
|
'form': ReportForm(),
|
||||||
})
|
})
|
||||||
|
|
||||||
def post(self, request, module, name):
|
def post(self, request, module, name):
|
||||||
@ -575,6 +576,12 @@ class ReportView(ContentTypePermissionRequiredMixin, View):
|
|||||||
if report is None:
|
if report is None:
|
||||||
raise Http404
|
raise Http404
|
||||||
|
|
||||||
|
schedule_at = None
|
||||||
|
form = ReportForm(request.POST)
|
||||||
|
|
||||||
|
if form.is_valid():
|
||||||
|
schedule_at = form.cleaned_data.get("schedule_at")
|
||||||
|
|
||||||
# Allow execution only if RQ worker process is running
|
# Allow execution only if RQ worker process is running
|
||||||
if not Worker.count(get_connection('default')):
|
if not Worker.count(get_connection('default')):
|
||||||
messages.error(request, "Unable to run report: RQ worker process not running.")
|
messages.error(request, "Unable to run report: RQ worker process not running.")
|
||||||
@ -589,7 +596,8 @@ class ReportView(ContentTypePermissionRequiredMixin, View):
|
|||||||
report.full_name,
|
report.full_name,
|
||||||
report_content_type,
|
report_content_type,
|
||||||
request.user,
|
request.user,
|
||||||
job_timeout=report.job_timeout
|
job_timeout=report.job_timeout,
|
||||||
|
schedule_at=schedule_at,
|
||||||
)
|
)
|
||||||
|
|
||||||
return redirect('extras:report_result', job_result_pk=job_result.pk)
|
return redirect('extras:report_result', job_result_pk=job_result.pk)
|
||||||
@ -707,6 +715,7 @@ class ScriptView(ContentTypePermissionRequiredMixin, GetScriptMixin, View):
|
|||||||
|
|
||||||
elif form.is_valid():
|
elif form.is_valid():
|
||||||
commit = form.cleaned_data.pop('_commit')
|
commit = form.cleaned_data.pop('_commit')
|
||||||
|
schedule_at = form.cleaned_data.pop("_schedule_at")
|
||||||
|
|
||||||
script_content_type = ContentType.objects.get(app_label='extras', model='script')
|
script_content_type = ContentType.objects.get(app_label='extras', model='script')
|
||||||
|
|
||||||
@ -719,6 +728,7 @@ class ScriptView(ContentTypePermissionRequiredMixin, GetScriptMixin, View):
|
|||||||
request=copy_safe_request(request),
|
request=copy_safe_request(request),
|
||||||
commit=commit,
|
commit=commit,
|
||||||
job_timeout=script.job_timeout,
|
job_timeout=script.job_timeout,
|
||||||
|
schedule_at=schedule_at,
|
||||||
)
|
)
|
||||||
|
|
||||||
return redirect('extras:script_result', job_result_pk=job_result.pk)
|
return redirect('extras:script_result', job_result_pk=job_result.pk)
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
"clipboard": "^2.0.8",
|
"clipboard": "^2.0.8",
|
||||||
"color2k": "^1.2.4",
|
"color2k": "^1.2.4",
|
||||||
"dayjs": "^1.10.4",
|
"dayjs": "^1.10.4",
|
||||||
"flatpickr": "4.6.3",
|
"flatpickr": "4.6.13",
|
||||||
"htmx.org": "^1.6.1",
|
"htmx.org": "^1.6.1",
|
||||||
"just-debounce-it": "^1.4.0",
|
"just-debounce-it": "^1.4.0",
|
||||||
"masonry-layout": "^4.2.2",
|
"masonry-layout": "^4.2.2",
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
{% extends 'generic/object.html' %}
|
{% extends 'generic/object.html' %}
|
||||||
{% load helpers %}
|
{% load helpers %}
|
||||||
|
{% load form_helpers %}
|
||||||
|
|
||||||
{% block title %}{{ report.name }}{% endblock %}
|
{% block title %}{{ report.name }}{% endblock %}
|
||||||
|
|
||||||
@ -33,18 +34,24 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
<div role="tabpanel" class="tab-pane active" id="report">
|
<div role="tabpanel" class="tab-pane active" id="report">
|
||||||
{% if perms.extras.run_report %}
|
{% if perms.extras.run_report %}
|
||||||
<div class="float-end noprint">
|
<div class="row">
|
||||||
<form action="{% url 'extras:report' module=report.module name=report.class_name %}" method="post">
|
<div class="col">
|
||||||
|
<form action="{% url 'extras:report' module=report.module name=report.class_name %}" method="post" class="form-object-edit">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<button type="submit" name="_run" class="btn btn-primary">
|
{% render_form form %}
|
||||||
{% if report.result %}
|
<div class="float-end">
|
||||||
<i class="mdi mdi-replay"></i> Run Again
|
<button type="submit" name="_run" class="btn btn-primary">
|
||||||
{% else %}
|
{% if report.result %}
|
||||||
<i class="mdi mdi-play"></i> Run Report
|
<i class="mdi mdi-replay"></i> Run Again
|
||||||
{% endif %}
|
{% else %}
|
||||||
</button>
|
<i class="mdi mdi-play"></i> Run Report
|
||||||
|
{% endif %}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col col-md-12">
|
<div class="col col-md-12">
|
||||||
|
@ -43,7 +43,7 @@
|
|||||||
You do not have permission to run scripts.
|
You do not have permission to run scripts.
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<form action="" method="post" enctype="multipart/form-data" class="form form-horizontal">
|
<form action="" method="post" enctype="multipart/form-data" class="form form-object-edit">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<div class="field-group my-4">
|
<div class="field-group my-4">
|
||||||
{% if form.requires_input %}
|
{% if form.requires_input %}
|
||||||
|
Loading…
Reference in New Issue
Block a user