Changed schedule start- and end-time to schedule start and duration

Localized display of schedule start-time and duration
Removed displaying scheduling settings if "APScheduler" is missing
Input check for start-time and duration
This commit is contained in:
Ozzie Isaacs 2022-04-25 17:00:07 +02:00
parent d83c731030
commit 6e8445fed5
7 changed files with 90 additions and 50 deletions

2
cps.py
View File

@ -77,7 +77,7 @@ def main():
app.register_blueprint(oauth)
# Register scheduled tasks
register_scheduled_tasks()
register_scheduled_tasks() # ToDo only reconnect if reconnect is enabled
register_startup_tasks()
success = web_server.start()

View File

@ -24,13 +24,12 @@ import os
import re
import base64
import json
import time
import operator
from datetime import datetime, timedelta
from datetime import datetime, timedelta, time
from functools import wraps
from babel import Locale
from babel.dates import format_datetime
from babel.dates import format_datetime, format_time, format_timedelta
from flask import Blueprint, flash, redirect, url_for, abort, request, make_response, send_from_directory, g, Response
from flask_login import login_required, current_user, logout_user, confirm_login
from flask_babel import gettext as _
@ -58,7 +57,8 @@ feature_support = {
'goodreads': bool(services.goodreads_support),
'kobo': bool(services.kobo),
'updater': constants.UPDATER_AVAILABLE,
'gmail': bool(services.gmail)
'gmail': bool(services.gmail),
'scheduler': schedule.use_APScheduler
}
try:
@ -184,6 +184,7 @@ def update_thumbnails():
@login_required
@admin_required
def admin():
locale = get_locale()
version = updater_thread.get_current_version_info()
if version is False:
commit = _(u'Unknown')
@ -198,15 +199,19 @@ def admin():
form_date -= timedelta(hours=int(commit[20:22]), minutes=int(commit[23:]))
elif commit[19] == '-':
form_date += timedelta(hours=int(commit[20:22]), minutes=int(commit[23:]))
commit = format_datetime(form_date - tz, format='short', locale=get_locale())
commit = format_datetime(form_date - tz, format='short', locale=locale)
else:
commit = version['version']
all_user = ub.session.query(ub.User).all()
email_settings = config.get_mail_settings()
kobo_support = feature_support['kobo'] and config.config_kobo_sync
schedule_time = format_time(time(hour=config.schedule_start_time), format="short", locale=locale)
t = timedelta(hours=config.schedule_duration // 60, minutes=config.schedule_duration % 60)
schedule_duration = format_timedelta(t, format="short", threshold=.99, locale=locale)
return render_title_template("admin.html", allUser=all_user, email=email_settings, config=config, commit=commit,
feature_support=feature_support, kobo_support=kobo_support,
feature_support=feature_support, schedule_time=schedule_time,
schedule_duration=schedule_duration,
title=_(u"Admin page"), page="admin")
@ -1660,36 +1665,57 @@ def update_mailsettings():
@admin_required
def edit_scheduledtasks():
content = config.get_scheduled_task_settings()
return render_title_template("schedule_edit.html", config=content, title=_(u"Edit Scheduled Tasks Settings"))
time_field = list()
duration_field = list()
locale = get_locale()
for n in range(24):
time_field.append((n , format_time(time(hour=n), format="short", locale=locale)))
for n in range(5, 65, 5):
t = timedelta(hours=n // 60, minutes=n % 60)
duration_field.append((n, format_timedelta(t, format="short", threshold=.99, locale=locale)))
return render_title_template("schedule_edit.html", config=content, starttime=time_field, duration=duration_field, title=_(u"Edit Scheduled Tasks Settings"))
@admi.route("/admin/scheduledtasks", methods=["POST"])
@login_required
@admin_required
def update_scheduledtasks():
error = False
to_save = request.form.to_dict()
_config_int(to_save, "schedule_start_time")
_config_int(to_save, "schedule_end_time")
if "0" <= to_save.get("schedule_start_time") <= "23":
_config_int(to_save, "schedule_start_time")
else:
flash(_(u"Invalid start time for task specified"), category="error")
error = True
if "0" < to_save.get("schedule_duration") <= "60":
_config_int(to_save, "schedule_duration")
else:
flash(_(u"Invalid duration for task specified"), category="error")
error = True
_config_checkbox(to_save, "schedule_generate_book_covers")
_config_checkbox(to_save, "schedule_generate_series_covers")
_config_checkbox(to_save, "schedule_reconnect")
try:
config.save()
flash(_(u"Scheduled tasks settings updated"), category="success")
if not error:
try:
config.save()
flash(_(u"Scheduled tasks settings updated"), category="success")
# Cancel any running tasks
schedule.end_scheduled_tasks()
# Cancel any running tasks
schedule.end_scheduled_tasks()
# Re-register tasks with new settings
schedule.register_scheduled_tasks(cli.reconnect_enable)
except IntegrityError as ex:
ub.session.rollback()
log.error("An unknown error occurred while saving scheduled tasks settings")
flash(_(u"An unknown error occurred. Please try again later."), category="error")
except OperationalError:
ub.session.rollback()
log.error("Settings DB is not Writeable")
flash(_("Settings DB is not Writeable"), category="error")
# Re-register tasks with new settings
schedule.register_scheduled_tasks(config.schedule_reconnect)
except IntegrityError:
ub.session.rollback()
log.error("An unknown error occurred while saving scheduled tasks settings")
flash(_(u"An unknown error occurred. Please try again later."), category="error")
except OperationalError:
ub.session.rollback()
log.error("Settings DB is not Writeable")
flash(_("Settings DB is not Writeable"), category="error")
return edit_scheduledtasks()

View File

@ -142,9 +142,10 @@ class _Settings(_Base):
config_allow_reverse_proxy_header_login = Column(Boolean, default=False)
schedule_start_time = Column(Integer, default=4)
schedule_end_time = Column(Integer, default=6)
schedule_duration = Column(Integer, default=10)
schedule_generate_book_covers = Column(Boolean, default=False)
schedule_generate_series_covers = Column(Boolean, default=False)
schedule_reconnect = Column(Boolean, default=False)
def __repr__(self):
return self.__class__.__name__

View File

@ -19,7 +19,7 @@
import datetime
from . import config, constants
from .services.background_scheduler import BackgroundScheduler
from .services.background_scheduler import BackgroundScheduler, use_APScheduler
from .tasks.database import TaskReconnectDatabase
from .tasks.thumbnail import TaskGenerateCoverThumbnails, TaskGenerateSeriesThumbnails, TaskClearCoverThumbnailCache
from .services.worker import WorkerThread
@ -27,7 +27,7 @@ from .services.worker import WorkerThread
def get_scheduled_tasks(reconnect=True):
tasks = list()
# config.schedule_reconnect or
# Reconnect Calibre database (metadata.db)
if reconnect:
tasks.append([lambda: TaskReconnectDatabase(), 'reconnect', False])
@ -59,15 +59,14 @@ def register_scheduled_tasks(reconnect=True):
scheduler.remove_all_jobs()
start = config.schedule_start_time
end = config.schedule_end_time
duration = config.schedule_duration
# Register scheduled tasks
if start != end:
scheduler.schedule_tasks(tasks=get_scheduled_tasks(), trigger='cron', hour=start)
scheduler.schedule(func=end_scheduled_tasks, trigger='cron', name="end scheduled task", hour=end)
scheduler.schedule_tasks(tasks=get_scheduled_tasks(), trigger='cron', hour=start)
scheduler.schedule(func=end_scheduled_tasks, trigger='cron', name="end scheduled task", hour=start) # toDo
# Kick-off tasks, if they should currently be running
if should_task_be_running(start, end):
if should_task_be_running(start, duration):
scheduler.schedule_tasks_immediately(tasks=get_scheduled_tasks(reconnect))
@ -76,14 +75,17 @@ def register_startup_tasks():
if scheduler:
start = config.schedule_start_time
end = config.schedule_end_time
duration = config.schedule_duration
# Run scheduled tasks immediately for development and testing
# Ignore tasks that should currently be running, as these will be added when registering scheduled tasks
if constants.APP_MODE in ['development', 'test'] and not should_task_be_running(start, end):
if constants.APP_MODE in ['development', 'test'] and not should_task_be_running(start, duration):
scheduler.schedule_tasks_immediately(tasks=get_scheduled_tasks(False))
def should_task_be_running(start, end):
now = datetime.datetime.now().hour
return (start < end and start <= now < end) or (end < start and (now < end or start <= now ))
def should_task_be_running(start, duration):
now = datetime.datetime.now()
start_time = datetime.datetime.now().replace(hour=start, minute=0, second=0, microsecond=0)
end_time = start_time + datetime.timedelta(hours=duration // 60, minutes=duration % 60)
return start_time < now < end_time
# return (start < end and start <= now < end) or (end < start and (now < end or start <= now ))

View File

@ -31,6 +31,7 @@ class TaskReconnectDatabase(CalibreTask):
self.listen_address = config.get_config_ipaddress()
self.listen_port = config.config_port
def run(self, worker_thread):
address = self.listen_address if self.listen_address else 'localhost'
port = self.listen_port if self.listen_port else 8083

View File

@ -161,18 +161,18 @@
<a class="btn btn-default" id="view_config" href="{{url_for('admin.view_configuration')}}">{{_('Edit UI Configuration')}}</a>
</div>
</div>
{% if feature_support['scheduler'] %}
<div class="row">
<div class="col">
<h2>{{_('Scheduled Tasks')}}</h2>
<div class="col-xs-12 col-sm-12 scheduled_tasks_details">
<div class="row">
<div class="col-xs-6 col-sm-3">{{_('Time at which tasks start to run')}}</div>
<div class="col-xs-6 col-sm-3">{{config.schedule_start_time}}:00</div>
<div class="col-xs-6 col-sm-3">{{schedule_time}}</div>
</div>
<div class="row">
<div class="col-xs-6 col-sm-3">{{_('Time at which tasks stop running')}}</div>
<div class="col-xs-6 col-sm-3">{{config.schedule_end_time}}:00</div>
<div class="col-xs-6 col-sm-3">{{_('Maximum tasks duration')}}</div>
<div class="col-xs-6 col-sm-3">{{schedule_duration}}</div>
</div>
<div class="row">
<div class="col-xs-6 col-sm-3">{{_('Generate book cover thumbnails')}}</div>
@ -182,6 +182,11 @@
<div class="col-xs-6 col-sm-3">{{_('Generate series cover thumbnails')}}</div>
<div class="col-xs-6 col-sm-3">{{ display_bool_setting(config.schedule_generate_series_covers) }}</div>
</div-->
<div class="row">
<div class="col-xs-6 col-sm-3">{{_('Reconnect to Calibre Library')}}</div>
<div class="col-xs-6 col-sm-3">{{ display_bool_setting(config.schedule_reconnect) }}</div>
</div>
</div>
<a class="btn btn-default scheduledtasks" id="admin_edit_scheduled_tasks" href="{{url_for('admin.edit_scheduledtasks')}}">{{_('Edit Scheduled Tasks Settings')}}</a>
{% if config.schedule_generate_book_covers %}
@ -189,7 +194,7 @@
{% endif %}
</div>
</div>
{% endif %}
<div class="row form-group">
<h2>{{_('Administration')}}</h2>
<a class="btn btn-default" id="debug" href="{{url_for('admin.download_debug')}}">{{_('Download Debug Package')}}</a>

View File

@ -11,16 +11,16 @@
<div class="form-group">
<label for="schedule_start_time">{{_('Time at which tasks start to run')}}</label>
<select name="schedule_start_time" id="schedule_start_time" class="form-control">
{% for n in range(24) %}
<option value="{{n}}" {% if config.schedule_start_time == n %}selected{% endif %}>{{n}}{{_(':00')}}</option>
{% for n in starttime %}
<option value="{{n[0]}}" {% if config.schedule_start_time == n[0] %}selected{% endif %}>{{n[1]}}</option>
{% endfor %}
</select>
</div>
<div class="form-group">
<label for="schedule_end_time">{{_('Time at which tasks stop running')}}</label>
<select name="schedule_end_time" id="schedule_end_time" class="form-control">
{% for n in range(24) %}
<option value="{{n}}" {% if config.schedule_end_time == n %}selected{% endif %}>{{n}}{{_(':00')}}</option>
<label for="schedule_duration">{{_('Maximum tasks duration')}}</label>
<select name="schedule_duration" id="schedule_duration" class="form-control">
{% for n in duration %}
<option value="{{n[0]}}" {% if config.schedule_duration == n[0] %}selected{% endif %}>{{n[1]}}</option>
{% endfor %}
</select>
</div>
@ -32,6 +32,11 @@
<input type="checkbox" id="schedule_generate_series_covers" name="schedule_generate_series_covers" {% if config.schedule_generate_series_covers %}checked{% endif %}>
<label for="schedule_generate_series_covers">{{_('Generate Series Cover Thumbnails')}}</label>
</div-->
<div class="form-group">
<input type="checkbox" id="schedule_reconnect" name="schedule_reconnect" {% if config.schedule_generate_book_covers %}checked{% endif %}>
<label for="schedule_reconnect">{{_('Reconnect to Calibre Library')}}</label>
</div>
<button type="submit" name="submit" value="submit" class="btn btn-default">{{_('Save')}}</button>
<a href="{{ url_for('admin.admin') }}" id="email_back" class="btn btn-default">{{_('Cancel')}}</a>
</form>