Add scheduling for reports and scripts

This commit is contained in:
kkthxbye-code 2022-09-18 15:06:28 +02:00
parent 41d653738a
commit 824b4e0923
7 changed files with 63 additions and 18 deletions

View 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",
)

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

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

View File

@ -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">

View File

@ -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 %}