Просмотр исходного кода

Add scheduling for reports and scripts

kkthxbye-code 3 лет назад
Родитель
Сommit
824b4e0923

+ 16 - 0
netbox/extras/forms/reports.py

@@ -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",
+    )

+ 12 - 4
netbox/extras/forms/scripts.py

@@ -1,6 +1,6 @@
 from django import forms
 
-from utilities.forms import BootstrapMixin
+from utilities.forms import BootstrapMixin, DateTimePicker
 
 __all__ = (
     'ScriptForm',
@@ -14,17 +14,25 @@ class ScriptForm(BootstrapMixin, forms.Form):
         label="Commit changes",
         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):
         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')
+        self.fields['_schedule_at'] = schedule_at
         self.fields['_commit'] = commit
 
     @property
     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)

+ 5 - 1
netbox/extras/models/models.py

@@ -550,7 +550,11 @@ class JobResult(models.Model):
         )
 
         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
 

+ 12 - 2
netbox/extras/views.py

@@ -15,6 +15,7 @@ from utilities.utils import copy_safe_request, count_related, get_viewname, norm
 from utilities.views import ContentTypePermissionRequiredMixin
 from . import filtersets, forms, tables
 from .choices import JobResultStatusChoices
+from .forms.reports import ReportForm
 from .models import *
 from .reports import get_report, get_reports, run_report
 from .scripts import get_scripts, run_script
@@ -562,7 +563,7 @@ class ReportView(ContentTypePermissionRequiredMixin, View):
 
         return render(request, 'extras/report.html', {
             'report': report,
-            'run_form': ConfirmationForm(),
+            'form': ReportForm(),
         })
 
     def post(self, request, module, name):
@@ -575,6 +576,12 @@ class ReportView(ContentTypePermissionRequiredMixin, View):
         if report is None:
             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
         if not Worker.count(get_connection('default')):
             messages.error(request, "Unable to run report: RQ worker process not running.")
@@ -589,7 +596,8 @@ class ReportView(ContentTypePermissionRequiredMixin, View):
             report.full_name,
             report_content_type,
             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)
@@ -707,6 +715,7 @@ class ScriptView(ContentTypePermissionRequiredMixin, GetScriptMixin, View):
 
         elif form.is_valid():
             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')
 
@@ -719,6 +728,7 @@ class ScriptView(ContentTypePermissionRequiredMixin, GetScriptMixin, View):
                 request=copy_safe_request(request),
                 commit=commit,
                 job_timeout=script.job_timeout,
+                schedule_at=schedule_at,
             )
 
             return redirect('extras:script_result', job_result_pk=job_result.pk)

+ 1 - 1
netbox/project-static/package.json

@@ -28,7 +28,7 @@
     "clipboard": "^2.0.8",
     "color2k": "^1.2.4",
     "dayjs": "^1.10.4",
-    "flatpickr": "4.6.3",
+    "flatpickr": "4.6.13",
     "htmx.org": "^1.6.1",
     "just-debounce-it": "^1.4.0",
     "masonry-layout": "^4.2.2",

+ 16 - 9
netbox/templates/extras/report.html

@@ -1,5 +1,6 @@
 {% extends 'generic/object.html' %}
 {% load helpers %}
+{% load form_helpers %}
 
 {% block title %}{{ report.name }}{% endblock %}
 
@@ -33,18 +34,24 @@
 {% block content %}
   <div role="tabpanel" class="tab-pane active" id="report">
     {% if perms.extras.run_report %}
-      <div class="float-end noprint">
-          <form action="{% url 'extras:report' module=report.module name=report.class_name %}" method="post">
+    <div class="row">
+      <div class="col">
+          <form action="{% url 'extras:report' module=report.module name=report.class_name %}" method="post" class="form-object-edit">
               {% csrf_token %}
-              <button type="submit" name="_run" class="btn btn-primary">
-                  {% if report.result %}
-                      <i class="mdi mdi-replay"></i> Run Again
-                  {% else %}
-                      <i class="mdi mdi-play"></i> Run Report
-                  {% endif %}
-              </button>
+              {% render_form form %}
+              <div class="float-end">
+                <button type="submit" name="_run" class="btn btn-primary">
+                    {% if report.result %}
+                        <i class="mdi mdi-replay"></i> Run Again
+                    {% else %}
+                        <i class="mdi mdi-play"></i> Run Report
+                    {% endif %}
+                </button>
+              </div>
           </form>
+        </div>
       </div>
+
     {% endif %}
     <div class="row">
         <div class="col col-md-12">

+ 1 - 1
netbox/templates/extras/script.html

@@ -43,7 +43,7 @@
             You do not have permission to run scripts.
           </div>
         {% 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 %}
           <div class="field-group my-4">
             {% if form.requires_input %}