cron schedule fully working on backend

This commit is contained in:
jrk 2024-05-02 21:57:57 +02:00
parent 7ed31eb9ab
commit 89c1811d83
17 changed files with 119 additions and 23 deletions

View File

@ -6,7 +6,8 @@
"location",
"name",
"position",
"type"
"type",
"cron_schedule"
],
"data": {
"0": {
@ -15,7 +16,8 @@
"type": "picture",
"enabled": true,
"name": "Picture Sample",
"position": 0
"position": 0,
"cron_schedule": null
},
"1": {
"location": "https://unix.org",
@ -23,7 +25,8 @@
"type": "url",
"enabled": true,
"name": "URL Sample",
"position": 1
"position": 1,
"cron_schedule": null
}
}
}

View File

@ -512,6 +512,14 @@ form .form-group textarea {
width: auto;
}
form .form-group input[type=checkbox] {
flex: 0;
}
form .form-group input[type=checkbox].trigger {
margin-right: 10px;
}
form .form-group span {
margin-left: 10px;
}

View File

@ -49,7 +49,7 @@ jQuery(document).ready(function ($) {
});
};
$(document).on('change', 'input[type=checkbox]', function () {
$(document).on('change', '.slide-item input[type=checkbox]', function () {
$.ajax({
url: '/slideshow/slide/toggle',
headers: {'Content-Type': 'application/json'},
@ -68,6 +68,16 @@ jQuery(document).ready(function ($) {
updateTable();
});
$(document).on('change', '.modal-slide input[type=checkbox]', function () {
const $target = $('#'+ $(this).attr('id').replace('-trigger', ''));
const hide = !$(this).is(':checked');
$target.toggleClass('hidden', hide);
if (hide) {
$target.val('');
}
});
$(document).on('change', '#slide-add-type', function () {
const value = $(this).val();
const inputType = $(this).find('option').filter(function (i, el) {
@ -95,11 +105,14 @@ jQuery(document).ready(function ($) {
$(document).on('click', '.slide-edit', function () {
const slide = JSON.parse($(this).parents('tr:eq(0)').attr('data-entity'));
showModal('modal-slide-edit');
const hasCron = slide.cron_schedule && slide.cron_schedule.length > 0;
$('.modal-slide-edit input:visible:eq(0)').focus().select();
$('#slide-edit-name').val(slide.name);
$('#slide-edit-type').val(slide.type);
$('#slide-edit-location').val(slide.location);
$('#slide-edit-duration').val(slide.duration);
$('#slide-edit-cron-schedule').val(slide.cron_schedule).toggleClass('hidden', !hasCron);
$('#slide-edit-cron-schedule-trigger').prop('checked', hasCron);
$('#slide-edit-id').val(slide.id);
});

BIN
docs/screenshot.png Normal file → Executable file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 255 KiB

After

Width:  |  Height:  |  Size: 267 KiB

View File

@ -8,7 +8,10 @@
"slideshow_slide_panel_th_duration": "Duration",
"slideshow_slide_panel_th_duration_unit": "sec",
"slideshow_slide_panel_th_enabled": "Enabled",
"slideshow_slide_panel_th_cron_scheduled": "Scheduled",
"slideshow_slide_panel_th_activity": "Activity",
"slideshow_slide_panel_td_cron_scheduled_loop": "Loop",
"slideshow_slide_panel_td_cron_scheduled_bad_cron": "Bad cron value",
"slideshow_slide_form_add_title": "Add Slide",
"slideshow_slide_form_add_submit": "Add",
"slideshow_slide_form_edit_title": "Edit Slide",
@ -22,6 +25,8 @@
"slideshow_slide_form_label_object": "Object",
"slideshow_slide_form_label_duration": "Duration",
"slideshow_slide_form_label_duration_unit": "seconds",
"slideshow_slide_form_label_cron_scheduled": "Scheduled",
"slideshow_slide_form_widget_cron_scheduled_placeholder": "Use crontab format: * * * * *",
"slideshow_slide_form_button_cancel": "Cancel",
"js_slideshow_slide_delete_confirmation": "Are you sure?",

View File

@ -8,7 +8,10 @@
"slideshow_slide_panel_th_duration": "Durée",
"slideshow_slide_panel_th_duration_unit": "sec",
"slideshow_slide_panel_th_enabled": "Activé",
"slideshow_slide_panel_th_cron_scheduled": "Programmation",
"slideshow_slide_panel_th_activity": "Options",
"slideshow_slide_panel_td_cron_scheduled_loop": "En boucle",
"slideshow_slide_panel_td_cron_scheduled_bad_cron": "Mauvaise valeur cron",
"slideshow_slide_form_add_title": "Ajouter d'une slide",
"slideshow_slide_form_add_submit": "Ajouter",
"slideshow_slide_form_edit_title": "Modification d'une slide",
@ -22,6 +25,8 @@
"slideshow_slide_form_label_object": "Objet",
"slideshow_slide_form_label_duration": "Durée",
"slideshow_slide_form_label_duration_unit": "secondes",
"slideshow_slide_form_label_cron_scheduled": "Programmer",
"slideshow_slide_form_widget_cron_scheduled_placeholder": "Utiliser le format crontab: * * * * *",
"slideshow_slide_form_button_cancel": "Annuler",
"js_slideshow_slide_delete_confirmation": "Êtes-vous sûr ?",

View File

@ -1,5 +1,5 @@
flask==2.3.3
pysondb-v2==2.1.0
python-dotenv
croniter
cron-descriptor

View File

@ -8,7 +8,7 @@ from src.service.ModelStore import ModelStore
from src.model.entity.Slide import Slide
from src.model.enum.SlideType import SlideType
from src.interface.ObController import ObController
from src.utils import str_to_enum
from src.utils import str_to_enum, get_optional_string
class SlideshowController(ObController):
@ -40,6 +40,7 @@ class SlideshowController(ObController):
name=request.form['name'],
type=str_to_enum(request.form['type'], SlideType),
duration=request.form['duration'],
cron_schedule=get_optional_string(request.form['cron_schedule']),
)
if slide.has_file():
@ -65,7 +66,7 @@ class SlideshowController(ObController):
return redirect(url_for('slideshow_slide_list'))
def slideshow_slide_edit(self):
self._model_store.slide().update_form(request.form['id'], request.form['name'], request.form['duration'])
self._model_store.slide().update_form(request.form['id'], request.form['name'], request.form['duration'], request.form['cron_schedule'])
self._post_update()
return redirect(url_for('slideshow_slide_list'))

View File

@ -8,7 +8,7 @@ class LangManager:
def __init__(self, lang: str):
self._map = {}
self._lang = lang
self._lang = lang.lower()
self.load()
def load(self, directory: str = "", prefix: str = ""):
@ -23,3 +23,6 @@ class LangManager:
def map(self) -> dict:
return self._map
def get_locale(self, local_with_country: bool = False) -> str:
return "{}_{}".format(self._lang, self._lang.upper()) if local_with_country else self._lang

View File

@ -2,7 +2,7 @@ import os
from typing import Dict, Optional, List, Tuple, Union
from src.model.entity.Slide import Slide
from src.utils import str_to_enum
from src.utils import str_to_enum, get_optional_string
from pysondb import PysonDB
from pysondb.errors import IdDoesNotExistError
@ -69,8 +69,12 @@ class SlideManager:
for slide_id, slide_position in positions.items():
self._db.update_by_id(slide_id, {"position": slide_position})
def update_form(self, id: str, name: str, duration: int) -> None:
self._db.update_by_id(id, {"name": name, "duration": duration})
def update_form(self, id: str, name: str, duration: int, cron_schedule: Optional[str] = '') -> None:
self._db.update_by_id(id, {
"name": name,
"duration": duration,
"cron_schedule": get_optional_string(cron_schedule)
})
def add_form(self, slide: Union[Slide, Dict]) -> None:
db_slide = slide

View File

@ -7,14 +7,14 @@ from src.utils import str_to_enum
class Slide:
def __init__(self, location: str = '', duration: int = 3, type: Union[SlideType, str] = SlideType.URL, enabled: bool = False, name: str = 'Untitled', position: int = 999, id: Optional[str] = None, cron_schedule: str = ''):
def __init__(self, location: str = '', duration: int = 3, type: Union[SlideType, str] = SlideType.URL, enabled: bool = False, name: str = 'Untitled', position: int = 999, id: Optional[str] = None, cron_schedule: Optional[str] = None):
self._id = id if id else None
self._location = location
self._duration = duration
self._type = str_to_enum(type, SlideType) if isinstance(type, str) else type
self._enabled = enabled
self._name = name
self._position = position,
self._position = position
self._cron_schedule = cron_schedule
@property
@ -38,11 +38,11 @@ class Slide:
self._type = value
@property
def cron_schedule(self) -> str:
def cron_schedule(self) -> Optional[str]:
return self._cron_schedule
@cron_schedule.setter
def cron_schedule(self, value: str):
def cron_schedule(self, value: Optional[str]):
self._cron_schedule = value
@property

View File

@ -1,6 +1,8 @@
import os
from flask import Flask, send_from_directory, Markup
from cron_descriptor import ExpressionDescriptor
from cron_descriptor.Exception import FormatException, WrongArgumentException, MissingFieldException
from typing import List
from jinja2 import Environment, FileSystemLoader, select_autoescape
from src.service.ModelStore import ModelStore
@ -18,6 +20,22 @@ class TemplateRenderer:
self._model_store = model_store
self._render_hook = render_hook
def cron_descriptor(self, expression: str, use_24hour_time_format=True) -> str:
try:
return str(
ExpressionDescriptor(
expression=expression,
use_24hour_time_format=use_24hour_time_format,
locale_code=self._model_store.lang().get_locale(local_with_country=True)
)
)
except FormatException:
return ''
except WrongArgumentException:
return ''
except MissingFieldException:
return ''
def get_view_globals(self) -> dict:
globals = dict(
STATIC_PREFIX="/{}/{}/".format(WebDirConstant.FOLDER_STATIC, WebDirConstant.FOLDER_STATIC_WEB_ASSETS),
@ -25,6 +43,7 @@ class TemplateRenderer:
VERSION=self._model_store.config().map().get('version'),
LANG=self._model_store.variable().map().get('lang').as_string(),
HOOK=self._render_hook,
cron_descriptor=self.cron_descriptor
)
for hook in HookType:

View File

@ -33,7 +33,6 @@ class WebServer:
def setup(self) -> None:
self._setup_flask_app()
self._setup_web_globals()
self._setup_web_extensions()
self._setup_web_errors()
self._setup_web_controllers()
@ -73,12 +72,8 @@ class WebServer:
def inject_global_vars() -> dict:
return self._template_renderer.get_view_globals()
def _setup_web_extensions(self) -> None:
@self._app.template_filter('ctime')
def time_ctime(s):
return time.ctime(s)
def _setup_web_errors(self) -> None:
@self._app.errorhandler(404)
def not_found(e):
return send_from_directory(self._get_template_folder(), 'core/error404.html'), 404

View File

@ -6,6 +6,17 @@ from typing import Optional, List
from enum import Enum
def get_optional_string(var: Optional[str]) -> Optional[str]:
if var is None:
return None
var = var.strip()
if var:
return var
return None
def get_keys(dict_or_object, key_list_name: str, key_attr_name: str = 'key') -> Optional[List]:
if dict_or_object is None:
return None

View File

@ -4,6 +4,7 @@
<th>{{ l.slideshow_slide_panel_th_name }}</th>
<th class="tac">{{ l.slideshow_slide_panel_th_duration }}</th>
<th class="tac">{{ l.slideshow_slide_panel_th_enabled }}</th>
<th class="">{{ l.slideshow_slide_panel_th_cron_scheduled }}</th>
<th class="tac">{{ l.slideshow_slide_panel_th_activity }}</th>
</tr>
</thead>
@ -42,6 +43,18 @@
<input type="checkbox" {% if slide.enabled %}checked="checked"{% endif %}><span></span>
</label>
</td>
<td class="">
{% if slide.cron_schedule %}
{% set cron_desc = cron_descriptor(slide.cron_schedule) %}
{% if cron_desc %}
⏳ {{ cron_desc }}
{% else %}
<span class="error">⚠️ {{ l.slideshow_slide_panel_td_cron_scheduled_bad_cron }}</span>
{% endif %}
{% else %}
🔄 {{ l.slideshow_slide_panel_td_cron_scheduled_loop }}
{% endif %}
</td>
<td class="actions tac">
<a href="javascript:void(0);" class="item-edit slide-edit">
<i class="fa fa-pencil"></i>

View File

@ -1,4 +1,4 @@
<div class="modal modal-slide-add">
<div class="modal modal-slide-add modal-slide">
<h2>
{{ l.slideshow_slide_form_add_title }}
</h2>
@ -38,6 +38,14 @@
</div>
</div>
<div class="form-group">
<label for="slide-add-cron-schedule">{{ l.slideshow_slide_form_label_cron_scheduled }}</label>
<div class="widget">
<input type="checkbox" id="slide-add-cron-schedule-trigger" class="trigger" />
<input type="text" name="cron_schedule" id="slide-add-cron-schedule" placeholder="{{ l.slideshow_slide_form_widget_cron_scheduled_placeholder }}" class="hidden" />
</div>
</div>
<div class="actions">
<button type="button" class="modal-close">
{{ l.slideshow_slide_form_button_cancel }}

View File

@ -1,4 +1,4 @@
<div class="modal modal-slide-edit hidden">
<div class="modal modal-slide-edit modal-slide hidden">
<h2>
{{ l.slideshow_slide_form_edit_submit }}
</h2>
@ -38,6 +38,14 @@
</div>
</div>
<div class="form-group">
<label for="slide-edit-cron-schedule">{{ l.slideshow_slide_form_label_cron_scheduled }}</label>
<div class="widget">
<input type="checkbox" id="slide-edit-cron-schedule-trigger" class="trigger" />
<input type="text" name="cron_schedule" id="slide-edit-cron-schedule" placeholder="{{ l.slideshow_slide_form_widget_cron_scheduled_placeholder }}" class="hidden" />
</div>
</div>
<div class="actions">
<button type="button" class="modal-close">
{{ l.slideshow_slide_form_button_cancel }}