cron schedule fully working on backend
This commit is contained in:
parent
7ed31eb9ab
commit
89c1811d83
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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
BIN
docs/screenshot.png
Normal file → Executable file
Binary file not shown.
|
Before Width: | Height: | Size: 255 KiB After Width: | Height: | Size: 267 KiB |
@ -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?",
|
||||
|
||||
|
||||
@ -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 ?",
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
flask==2.3.3
|
||||
pysondb-v2==2.1.0
|
||||
python-dotenv
|
||||
croniter
|
||||
cron-descriptor
|
||||
|
||||
|
||||
@ -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'))
|
||||
|
||||
|
||||
@ -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
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
11
src/utils.py
11
src/utils.py
@ -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
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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 }}
|
||||
|
||||
@ -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 }}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user