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
pull/2262/head^2
Ozzie Isaacs 2 years ago
parent d83c731030
commit 6e8445fed5

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

@ -24,13 +24,12 @@ import os
import re import re
import base64 import base64
import json import json
import time
import operator import operator
from datetime import datetime, timedelta from datetime import datetime, timedelta, time
from functools import wraps from functools import wraps
from babel import Locale 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 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_login import login_required, current_user, logout_user, confirm_login
from flask_babel import gettext as _ from flask_babel import gettext as _
@ -58,7 +57,8 @@ feature_support = {
'goodreads': bool(services.goodreads_support), 'goodreads': bool(services.goodreads_support),
'kobo': bool(services.kobo), 'kobo': bool(services.kobo),
'updater': constants.UPDATER_AVAILABLE, 'updater': constants.UPDATER_AVAILABLE,
'gmail': bool(services.gmail) 'gmail': bool(services.gmail),
'scheduler': schedule.use_APScheduler
} }
try: try:
@ -184,6 +184,7 @@ def update_thumbnails():
@login_required @login_required
@admin_required @admin_required
def admin(): def admin():
locale = get_locale()
version = updater_thread.get_current_version_info() version = updater_thread.get_current_version_info()
if version is False: if version is False:
commit = _(u'Unknown') commit = _(u'Unknown')
@ -198,15 +199,19 @@ def admin():
form_date -= timedelta(hours=int(commit[20:22]), minutes=int(commit[23:])) form_date -= timedelta(hours=int(commit[20:22]), minutes=int(commit[23:]))
elif commit[19] == '-': elif commit[19] == '-':
form_date += timedelta(hours=int(commit[20:22]), minutes=int(commit[23:])) 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: else:
commit = version['version'] commit = version['version']
all_user = ub.session.query(ub.User).all() all_user = ub.session.query(ub.User).all()
email_settings = config.get_mail_settings() 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, 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") title=_(u"Admin page"), page="admin")
@ -1660,36 +1665,57 @@ def update_mailsettings():
@admin_required @admin_required
def edit_scheduledtasks(): def edit_scheduledtasks():
content = config.get_scheduled_task_settings() 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"]) @admi.route("/admin/scheduledtasks", methods=["POST"])
@login_required @login_required
@admin_required @admin_required
def update_scheduledtasks(): def update_scheduledtasks():
error = False
to_save = request.form.to_dict() to_save = request.form.to_dict()
_config_int(to_save, "schedule_start_time") if "0" <= to_save.get("schedule_start_time") <= "23":
_config_int(to_save, "schedule_end_time") _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_book_covers")
_config_checkbox(to_save, "schedule_generate_series_covers") _config_checkbox(to_save, "schedule_generate_series_covers")
_config_checkbox(to_save, "schedule_reconnect")
try: if not error:
config.save() try:
flash(_(u"Scheduled tasks settings updated"), category="success") config.save()
flash(_(u"Scheduled tasks settings updated"), category="success")
# Cancel any running tasks # Cancel any running tasks
schedule.end_scheduled_tasks() schedule.end_scheduled_tasks()
# Re-register tasks with new settings # Re-register tasks with new settings
schedule.register_scheduled_tasks(cli.reconnect_enable) schedule.register_scheduled_tasks(config.schedule_reconnect)
except IntegrityError as ex: except IntegrityError:
ub.session.rollback() ub.session.rollback()
log.error("An unknown error occurred while saving scheduled tasks settings") log.error("An unknown error occurred while saving scheduled tasks settings")
flash(_(u"An unknown error occurred. Please try again later."), category="error") flash(_(u"An unknown error occurred. Please try again later."), category="error")
except OperationalError: except OperationalError:
ub.session.rollback() ub.session.rollback()
log.error("Settings DB is not Writeable") log.error("Settings DB is not Writeable")
flash(_("Settings DB is not Writeable"), category="error") flash(_("Settings DB is not Writeable"), category="error")
return edit_scheduledtasks() return edit_scheduledtasks()

@ -142,9 +142,10 @@ class _Settings(_Base):
config_allow_reverse_proxy_header_login = Column(Boolean, default=False) config_allow_reverse_proxy_header_login = Column(Boolean, default=False)
schedule_start_time = Column(Integer, default=4) 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_book_covers = Column(Boolean, default=False)
schedule_generate_series_covers = Column(Boolean, default=False) schedule_generate_series_covers = Column(Boolean, default=False)
schedule_reconnect = Column(Boolean, default=False)
def __repr__(self): def __repr__(self):
return self.__class__.__name__ return self.__class__.__name__

@ -19,7 +19,7 @@
import datetime import datetime
from . import config, constants 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.database import TaskReconnectDatabase
from .tasks.thumbnail import TaskGenerateCoverThumbnails, TaskGenerateSeriesThumbnails, TaskClearCoverThumbnailCache from .tasks.thumbnail import TaskGenerateCoverThumbnails, TaskGenerateSeriesThumbnails, TaskClearCoverThumbnailCache
from .services.worker import WorkerThread from .services.worker import WorkerThread
@ -27,7 +27,7 @@ from .services.worker import WorkerThread
def get_scheduled_tasks(reconnect=True): def get_scheduled_tasks(reconnect=True):
tasks = list() tasks = list()
# config.schedule_reconnect or
# Reconnect Calibre database (metadata.db) # Reconnect Calibre database (metadata.db)
if reconnect: if reconnect:
tasks.append([lambda: TaskReconnectDatabase(), 'reconnect', False]) tasks.append([lambda: TaskReconnectDatabase(), 'reconnect', False])
@ -59,15 +59,14 @@ def register_scheduled_tasks(reconnect=True):
scheduler.remove_all_jobs() scheduler.remove_all_jobs()
start = config.schedule_start_time start = config.schedule_start_time
end = config.schedule_end_time duration = config.schedule_duration
# Register scheduled tasks # Register scheduled tasks
if start != end: scheduler.schedule_tasks(tasks=get_scheduled_tasks(), trigger='cron', hour=start)
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
scheduler.schedule(func=end_scheduled_tasks, trigger='cron', name="end scheduled task", hour=end)
# Kick-off tasks, if they should currently be running # 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)) scheduler.schedule_tasks_immediately(tasks=get_scheduled_tasks(reconnect))
@ -76,14 +75,17 @@ def register_startup_tasks():
if scheduler: if scheduler:
start = config.schedule_start_time start = config.schedule_start_time
end = config.schedule_end_time duration = config.schedule_duration
# Run scheduled tasks immediately for development and testing # Run scheduled tasks immediately for development and testing
# Ignore tasks that should currently be running, as these will be added when registering scheduled tasks # 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)) scheduler.schedule_tasks_immediately(tasks=get_scheduled_tasks(False))
def should_task_be_running(start, end): def should_task_be_running(start, duration):
now = datetime.datetime.now().hour now = datetime.datetime.now()
return (start < end and start <= now < end) or (end < start and (now < end or start <= 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 ))

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

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

@ -11,16 +11,16 @@
<div class="form-group"> <div class="form-group">
<label for="schedule_start_time">{{_('Time at which tasks start to run')}}</label> <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"> <select name="schedule_start_time" id="schedule_start_time" class="form-control">
{% for n in range(24) %} {% for n in starttime %}
<option value="{{n}}" {% if config.schedule_start_time == n %}selected{% endif %}>{{n}}{{_(':00')}}</option> <option value="{{n[0]}}" {% if config.schedule_start_time == n[0] %}selected{% endif %}>{{n[1]}}</option>
{% endfor %} {% endfor %}
</select> </select>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="schedule_end_time">{{_('Time at which tasks stop running')}}</label> <label for="schedule_duration">{{_('Maximum tasks duration')}}</label>
<select name="schedule_end_time" id="schedule_end_time" class="form-control"> <select name="schedule_duration" id="schedule_duration" class="form-control">
{% for n in range(24) %} {% for n in duration %}
<option value="{{n}}" {% if config.schedule_end_time == n %}selected{% endif %}>{{n}}{{_(':00')}}</option> <option value="{{n[0]}}" {% if config.schedule_duration == n[0] %}selected{% endif %}>{{n[1]}}</option>
{% endfor %} {% endfor %}
</select> </select>
</div> </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 %}> <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> <label for="schedule_generate_series_covers">{{_('Generate Series Cover Thumbnails')}}</label>
</div--> </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> <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> <a href="{{ url_for('admin.admin') }}" id="email_back" class="btn btn-default">{{_('Cancel')}}</a>
</form> </form>

Loading…
Cancel
Save