Merge pull request #1026 from tcely/patch-6

Add a start link to scheduled tasks
This commit is contained in:
meeb authored and GitHub committed 2025-05-09 00:43:20 +10:00
commit 73972c8b0c
5 files changed
+152 -7

No files matched your search

+9
View File
@@ -44,10 +44,19 @@ class ResetTasksForm(forms.Form):
pass pass
class ScheduleTaskForm(forms.Form):
when = forms.DateTimeField(
label=_('When the task should run'),
required=True,
)
class ConfirmDeleteMediaServerForm(forms.Form): class ConfirmDeleteMediaServerForm(forms.Form):
pass pass
_media_server_type_label = 'Jellyfin' _media_server_type_label = 'Jellyfin'
class JellyfinMediaServerForm(forms.Form): class JellyfinMediaServerForm(forms.Form):
@@ -0,0 +1,34 @@
{% extends 'base.html' %}
{% block headtitle %}Schedule task{% endblock %}
{% block content %}
<div class="row no-margin-bottom">
<div class="col s12">
<h1>Schedule task</h1>
<p>
If you don't want to wait for the existing schedule to be triggered,
you can use this to change when the task will be scheduled to run.
It is not guaranteed to run at any exact time, because when a task
requests to run and when a slot to execute it, in the appropriate
queue and with the priority level assigned, is dependent on how long
other tasks are taking to complete the assigned work.
</p>
<p>
This will change the time that the task is requesting to be run
to the current time, plus some number of seconds.
</p>
</div>
</div>
<div class="row">
<form method="post" action="{% url 'sync:run-task' pk=task.pk %}" class="col s12 simpleform">
{% csrf_token %}
{% include 'simpleform.html' with form=form %}
<div class="row no-margin-bottom padding-top">
<div class="col s12">
<button class="btn" type="submit" name="action">Really schedule task <i class="far fa-clock"></i></button>
</div>
</div>
</form>
</div>
{% endblock %}
+16 -5
View File
@@ -43,11 +43,16 @@
</p> </p>
<div class="collection"> <div class="collection">
{% for task in errors %} {% for task in errors %}
<a href="{% url task.url pk=task.instance.pk %}" class="collection-item error-text"> <div class="collection-item error-text">
<a href="{% url task.url pk=task.instance.pk %}" class="error-text">
<i class="fas fa-exclamation-triangle"></i> <strong>{{ task }}</strong>, attempted {{ task.attempts }} time{{ task.attempts|pluralize }}<br> <i class="fas fa-exclamation-triangle"></i> <strong>{{ task }}</strong>, attempted {{ task.attempts }} time{{ task.attempts|pluralize }}<br>
Error: &quot;{{ task.error_message }}&quot;<br> Error: &quot;{{ task.error_message }}&quot;<br>
<i class="fas fa-history"></i> Task will be retried at <strong>{{ task.run_at|date:'Y-m-d H:i:s' }}</strong>
</a> </a>
<i class="fas fa-history"></i> Task will be retried at <strong>{{ task.run_at|date:'Y-m-d H:i:s' }}</strong>
<a href="{% url 'sync:run-task' pk=task.pk %}" class="error-text">
<i class="fas fa-undo"></i>
</a>
</div>
{% empty %} {% empty %}
<span class="collection-item no-items"><i class="fas fa-info-circle"></i> There are no tasks with errors on this page.</span> <span class="collection-item no-items"><i class="fas fa-info-circle"></i> There are no tasks with errors on this page.</span>
{% endfor %} {% endfor %}
@@ -66,11 +71,17 @@
</p> </p>
<div class="collection"> <div class="collection">
{% for task in scheduled %} {% for task in scheduled %}
<a href="{% url task.url pk=task.instance.pk %}" class="collection-item"> <div class="collection-item">
<i class="fas fa-stopwatch"></i> <strong>{{ task }}</strong><br> <a href="{% url task.url pk=task.instance.pk %}">
<i class="fas fa-hourglass-start"></i> <strong>{{ task }}</strong><br>
{% if task.instance.index_schedule and task.repeat > 0 %}Scheduled to run {{ task.instance.get_index_schedule_display|lower }}.<br>{% endif %} {% if task.instance.index_schedule and task.repeat > 0 %}Scheduled to run {{ task.instance.get_index_schedule_display|lower }}.<br>{% endif %}
<i class="fas fa-redo"></i> Task will run {% if task.run_now %}<strong>immediately</strong>{% else %}at <strong>{{ task.run_at|date:'Y-m-d H:i:s' }}</strong>{% endif %} <i class="far fa-clock"></i> Task will run {% if task.run_now %}<strong>immediately</strong>{% else %}at <strong>{{ task.run_at|date:'Y-m-d H:i:s' }}</strong>
</a> </a>
<a href="{% url 'sync:run-task' pk=task.pk %}">
<i class="far fa-play-circle"></i>
{% endif %}
</a>
</div>
{% empty %} {% empty %}
<span class="collection-item no-items"><i class="fas fa-info-circle"></i> There are no scheduled tasks on this page.</span> <span class="collection-item no-items"><i class="fas fa-info-circle"></i> There are no scheduled tasks on this page.</span>
{% endfor %} {% endfor %}
+13 -1
View File
@@ -3,7 +3,7 @@ from .views import (DashboardView, SourcesView, ValidateSourceView, AddSourceVie
SourceView, UpdateSourceView, DeleteSourceView, MediaView, SourceView, UpdateSourceView, DeleteSourceView, MediaView,
MediaThumbView, MediaItemView, MediaRedownloadView, MediaSkipView, MediaThumbView, MediaItemView, MediaRedownloadView, MediaSkipView,
MediaEnableView, MediaContent, TasksView, CompletedTasksView, ResetTasks, MediaEnableView, MediaContent, TasksView, CompletedTasksView, ResetTasks,
MediaServersView, AddMediaServerView, MediaServerView, TaskScheduleView, MediaServersView, AddMediaServerView, MediaServerView,
DeleteMediaServerView, UpdateMediaServerView) DeleteMediaServerView, UpdateMediaServerView)
@@ -122,6 +122,18 @@ urlpatterns = [
name='tasks', name='tasks',
), ),
path(
'task/<int:pk>/schedule/now',
TaskScheduleView.as_view(),
name='run-task',
),
path(
'task/<int:pk>/schedule/<int:timestamp>',
TaskScheduleView.as_view(),
name='schedule-task',
),
path( path(
'tasks-completed', 'tasks-completed',
CompletedTasksView.as_view(), CompletedTasksView.as_view(),
+80 -1
View File
@@ -20,11 +20,12 @@ from django.utils.text import slugify
from django.utils._os import safe_join from django.utils._os import safe_join
from django.utils import timezone from django.utils import timezone
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from common.timestamp import timestamp_to_datetime
from common.utils import append_uri_params from common.utils import append_uri_params
from background_task.models import Task, CompletedTask from background_task.models import Task, CompletedTask
from .models import Source, Media, MediaServer from .models import Source, Media, MediaServer
from .forms import (ValidateSourceForm, ConfirmDeleteSourceForm, RedownloadMediaForm, from .forms import (ValidateSourceForm, ConfirmDeleteSourceForm, RedownloadMediaForm,
SkipMediaForm, EnableMediaForm, ResetTasksForm, SkipMediaForm, EnableMediaForm, ResetTasksForm, ScheduleTaskForm,
ConfirmDeleteMediaServerForm) ConfirmDeleteMediaServerForm)
from .utils import validate_url, delete_file, multi_key_sort, mkdir_p from .utils import validate_url, delete_file, multi_key_sort, mkdir_p
from .tasks import (map_task_to_instance, get_error_message, from .tasks import (map_task_to_instance, get_error_message,
@@ -1005,6 +1006,84 @@ class ResetTasks(FormView):
return append_uri_params(url, {'message': 'reset'}) return append_uri_params(url, {'message': 'reset'})
class TaskScheduleView(FormView, SingleObjectMixin):
'''
Confirm that the task should be re-scheduled.
'''
template_name = 'sync/task-schedule.html'
form_class = ScheduleTaskForm
model = Task
errors = dict(
invalid_when=_('The type ({}) was incorrect.'),
)
def __init__(self, *args, **kwargs):
self.object = None
self.timestamp = None
self.when = None
super().__init__(*args, **kwargs)
def dispatch(self, request, *args, **kwargs):
self.object = self.get_object()
self.timestamp = kwargs.get('timestamp', '')
try:
self.timestamp = int(self.timestamp, 10)
except (TypeError, ValueError):
self.timestamp = None
else:
try:
self.when = timestamp_to_datetime(self.timestamp)
except AssertionError:
self.when = None
if self.when is None:
self.when = timezone.now()
return super().dispatch(request, *args, **kwargs)
def get_initial(self):
initial = super().get_initial()
initial['when'] = self.when
return initial
def get_context_data(self, *args, **kwargs):
data = super().get_context_data(*args, **kwargs)
data['when'] = self.when
return data
def get_success_url(self):
return append_uri_params(
reverse_lazy('sync:tasks'),
dict(
message='scheduled',
pk=str(self.object.pk),
),
)
def form_valid(self, form):
max_attempts = getattr(settings, 'MAX_ATTEMPTS', 15)
now = timezone.now()
when = form.cleaned_data.get('when')
if not isinstance(when, now.__class__):
form.add_error(
'when',
ValidationError(
errors['invalid_when'].format(
type(when),
),
),
)
if form.errors:
return super().form_invalid(form)
self.object.attempts = max_attempts // 2
self.object.run_at = when
self.object.save()
return super().form_valid(form)
class MediaServersView(ListView): class MediaServersView(ListView):
''' '''
List of media servers which have been added. List of media servers which have been added.