diff --git a/lang/en.json b/lang/en.json index 3c9857f..92a49f3 100644 --- a/lang/en.json +++ b/lang/en.json @@ -65,6 +65,7 @@ "settings_variable_desc_lang": "Server language", "settings_variable_desc_fleet_enabled": "Enable fleet screen management view", "settings_variable_desc_external_url": "External url (i.e: https://screen-01.company.com or http://10.10.3.100)", + "settings_variable_desc_slide_upload_limit": "Slide upload limit (in bytes, 32*1024*1024 for 32MB)", "settings_variable_desc_slide_animation_enabled": "Enable animation effect between slides", "settings_variable_desc_slide_animation_entrance_effect": "Slide animation entrance effect", diff --git a/lang/fr.json b/lang/fr.json index 1cc575c..554a902 100644 --- a/lang/fr.json +++ b/lang/fr.json @@ -65,6 +65,7 @@ "settings_variable_desc_lang": "Langage de l'application", "settings_variable_desc_fleet_enabled": "Activer la gestion de flotte des écrans", "settings_variable_desc_external_url": "URL externe (i.e: https://screen-01.company.com or http://10.10.3.100)", + "settings_variable_desc_slide_upload_limit": "Limite d'upload du fichier d'une slide (en octets, 32*1024*1024 pour 32Mo)", "settings_variable_desc_slide_animation_enabled": "Activer les effets d'animation entre les slides", "settings_variable_desc_slide_animation_entrance_effect": "Effet d'animation d'arrivée de la slide", diff --git a/src/interface/ObPlugin.py b/src/interface/ObPlugin.py index 188fa1a..1ff951f 100644 --- a/src/interface/ObPlugin.py +++ b/src/interface/ObPlugin.py @@ -5,6 +5,7 @@ from jinja2 import Environment, FileSystemLoader, select_autoescape from typing import Optional, List, Dict, Union from src.model.entity.Variable import Variable from src.model.enum.VariableType import VariableType +from src.model.enum.VariableUnit import VariableUnit from src.model.enum.HookType import HookType from src.model.hook.HookRegistration import HookRegistration from src.model.hook.StaticHookRegistration import StaticHookRegistration @@ -55,13 +56,14 @@ class ObPlugin(abc.ABC): def get_plugin_variable_name(self, name: str) -> str: return "{}_{}".format(self.get_plugin_variable_prefix(), name) - def add_variable(self, name: str, value='', type: VariableType = VariableType.STRING, editable: bool = True, description: str = '', selectables: Optional[Dict[str, str]] = None) -> Variable: + def add_variable(self, name: str, value='', type: VariableType = VariableType.STRING, editable: bool = True, description: str = '', selectables: Optional[Dict[str, str]] = None, unit: Optional[VariableUnit] = None) -> Variable: return self._model_store.variable().set_variable( name=self.get_plugin_variable_name(name), value=value, type=type, editable=editable, description=description, + unit=unit, selectables=selectables if isinstance(selectables, dict) else None, plugin=self.use_id(), ) diff --git a/src/manager/DatabaseManager.py b/src/manager/DatabaseManager.py new file mode 100644 index 0000000..8ff6dcc --- /dev/null +++ b/src/manager/DatabaseManager.py @@ -0,0 +1,33 @@ +import json +import sys + +from pysondb import PysonDB +from typing import Optional + + +class DatabaseManager: + + DB_DIR = 'data/db' + + def __init__(self): + pass + + def open(self, table_name: str, table_model: list) -> PysonDB: + db_file = "{}/{}.json".format(self.DB_DIR, table_name) + db = PysonDB(db_file) + db = self._update_model(db_file, table_model) + return db + + @staticmethod + def _update_model(db_file: str, table_model: list) -> Optional[PysonDB]: + try: + with open(db_file, 'r') as file: + db_model = file.read() + db_model = json.loads(db_model) + db_model['keys'] = table_model + with open(db_file, 'w') as file: + file.write(json.dumps(db_model, indent=4)) + return PysonDB(db_file) + except FileNotFoundError: + logging.error("Database file {} not found".format(db_file)) + return None diff --git a/src/manager/ScreenManager.py b/src/manager/ScreenManager.py index 584a465..d019d67 100644 --- a/src/manager/ScreenManager.py +++ b/src/manager/ScreenManager.py @@ -1,15 +1,24 @@ -from typing import Dict, Optional, List, Tuple, Union -from src.model.entity.Screen import Screen -from pysondb import PysonDB from pysondb.errors import IdDoesNotExistError +from typing import Dict, Optional, List, Tuple, Union + +from src.model.entity.Screen import Screen +from src.manager.DatabaseManager import DatabaseManager class ScreenManager: - DB_FILE = "data/db/fleet.json" + TABLE_NAME = "fleet" + TABLE_MODEL = [ + "name", + "enabled", + "position", + "host", + "port" + ] - def __init__(self): - self._db = PysonDB(self.DB_FILE) + def __init__(self, database_manager: DatabaseManager): + self._database_manager = database_manager + self._db = database_manager.open(self.TABLE_NAME, self.TABLE_MODEL) @staticmethod def hydrate_object(raw_screen: dict, id: Optional[str] = None) -> Screen: diff --git a/src/manager/SlideManager.py b/src/manager/SlideManager.py index 31fa397..3dee264 100644 --- a/src/manager/SlideManager.py +++ b/src/manager/SlideManager.py @@ -1,18 +1,29 @@ import os from typing import Dict, Optional, List, Tuple, Union +from pysondb.errors import IdDoesNotExistError + from src.model.entity.Slide import Slide from src.utils import str_to_enum, get_optional_string -from pysondb import PysonDB -from pysondb.errors import IdDoesNotExistError +from src.manager.DatabaseManager import DatabaseManager class SlideManager: - DB_FILE = "data/db/slideshow.json" + TABLE_NAME = "slideshow" + TABLE_MODEL = [ + "name", + "type", + "enabled", + "duration", + "position", + "location", + "cron_schedule" + ] - def __init__(self): - self._db = PysonDB(self.DB_FILE) + def __init__(self, database_manager: DatabaseManager): + self._database_manager = database_manager + self._db = database_manager.open(self.TABLE_NAME, self.TABLE_MODEL) @staticmethod def hydrate_object(raw_slide: dict, id: str = None) -> Slide: diff --git a/src/manager/VariableManager.py b/src/manager/VariableManager.py index 5a86f9d..de45a7d 100644 --- a/src/manager/VariableManager.py +++ b/src/manager/VariableManager.py @@ -1,27 +1,41 @@ +import time from typing import Dict, Optional, List, Tuple, Union +from pysondb.errors import IdDoesNotExistError + +from src.manager.DatabaseManager import DatabaseManager from src.model.entity.Variable import Variable from src.model.entity.Selectable import Selectable from src.model.enum.VariableType import VariableType +from src.model.enum.VariableUnit import VariableUnit from src.model.enum.AnimationEntranceEffect import AnimationEntranceEffect from src.model.enum.AnimationExitEffect import AnimationExitEffect from src.model.enum.AnimationSpeed import AnimationSpeed -from pysondb import PysonDB -from pysondb.errors import IdDoesNotExistError -from src.utils import get_keys -import time +from src.utils import get_keys, enum_to_str SELECTABLE_BOOLEAN = {"1": "✅", "0": "❌"} + class VariableManager: - DB_FILE = "data/db/settings.json" + TABLE_NAME = "settings" + TABLE_MODEL = [ + "description", + "editable", + "name", + "plugin", + "selectables", + "type", + "unit", + "value" + ] - def __init__(self): - self._db = PysonDB(self.DB_FILE) + def __init__(self, database_manager: DatabaseManager): + self._database_manager = database_manager + self._db = database_manager.open(self.TABLE_NAME, self.TABLE_MODEL) self._var_map = {} self.reload() - def set_variable(self, name: str, value, type: VariableType, editable: bool, description: str, plugin: Optional[None] = None, selectables: Optional[Dict[str, str]] = None) -> Variable: + def set_variable(self, name: str, value, type: VariableType, editable: bool, description: str, plugin: Optional[None] = None, selectables: Optional[Dict[str, str]] = None, unit: Optional[VariableUnit] = None) -> Variable: if isinstance(value, bool) and value: value = '1' elif isinstance(value, bool) and not value: @@ -37,6 +51,7 @@ class VariableManager: "editable": editable, "description": description, "plugin": plugin, + "unit": unit.value if unit else None, "selectables": ([{"key": key, "label": label} for key, label in selectables.items()]) if isinstance(selectables, dict) else None } variable = self.get_one_by_name(default_var['name']) @@ -50,6 +65,9 @@ class VariableManager: if variable.description != default_var['description']: self._db.update_by_id(variable.id, {"description": default_var['description']}) + if variable.unit != default_var['unit']: + self._db.update_by_id(variable.id, {"unit": default_var['unit']}) + if not same_selectables: self._db.update_by_id(variable.id, {"selectables": default_var['selectables']}) @@ -63,6 +81,7 @@ class VariableManager: {"name": "lang", "value": "en", "type": VariableType.SELECT_SINGLE, "editable": True, "description": lang_map['settings_variable_desc_lang'] if lang_map else "", "selectables": {"en": "English", "fr": "French"}}, {"name": "fleet_enabled", "value": False, "type": VariableType.BOOL, "editable": True, "description": lang_map['settings_variable_desc_fleet_enabled'] if lang_map else ""}, {"name": "external_url", "value": "", "type": VariableType.STRING, "editable": True, "description": lang_map['settings_variable_desc_external_url'] if lang_map else ""}, + {"name": "slide_upload_limit", "value": 32 * 1024 * 1024, "unit": VariableUnit.BYTE, "type": VariableType.INT, "editable": True, "description": lang_map['settings_variable_desc_slide_upload_limit'] if lang_map else ""}, {"name": "slide_animation_enabled", "value": False, "type": VariableType.BOOL, "editable": True, "description": lang_map['settings_variable_desc_slide_animation_enabled'] if lang_map else ""}, {"name": "slide_animation_entrance_effect", "value": AnimationEntranceEffect.FADE_IN.value, "type": VariableType.SELECT_SINGLE, "editable": True, "description": lang_map['settings_variable_desc_slide_animation_entrance_effect'] if lang_map else "", "selectables": AnimationEntranceEffect.get_values()}, {"name": "slide_animation_exit_effect", "value": AnimationExitEffect.NONE.value, "type": VariableType.SELECT_SINGLE, "editable": True, "description": lang_map['settings_variable_desc_slide_animation_exit_effect'] if lang_map else "", "selectables": AnimationExitEffect.get_values()}, diff --git a/src/model/entity/Variable.py b/src/model/entity/Variable.py index 32f7de5..56ea443 100644 --- a/src/model/entity/Variable.py +++ b/src/model/entity/Variable.py @@ -3,6 +3,7 @@ import time from typing import Optional, Union, Dict, List from src.model.enum.VariableType import VariableType +from src.model.enum.VariableUnit import VariableUnit from src.model.entity.Selectable import Selectable from src.utils import str_to_enum @@ -11,10 +12,11 @@ class Variable: def __init__(self, name: str = '', description: str = '', type: Union[VariableType, str] = VariableType.STRING, value: Union[int, bool, str] = '', editable: bool = True, id: Optional[str] = None, - plugin: Optional[str] = None, selectables: Optional[List[Selectable]] = None): + plugin: Optional[str] = None, selectables: Optional[List[Selectable]] = None, unit: Optional[VariableUnit] = None): self._id = id if id else None self._name = name self._type = str_to_enum(type, VariableType) if isinstance(type, str) else type + self._unit = str_to_enum(unit, VariableUnit) if isinstance(unit, str) else unit self._description = description self._value = value self._editable = editable @@ -52,6 +54,14 @@ class Variable: def type(self, value: VariableType): self._type = value + @property + def unit(self) -> VariableUnit: + return self._unit + + @unit.setter + def unit(self, value: VariableUnit): + self._unit = value + @property def description(self) -> str: return self._description @@ -90,6 +100,7 @@ class Variable: f"name='{self.name}',\n" \ f"value='{self.value}',\n" \ f"type='{self.type}',\n" \ + f"unit='{self.unit}',\n" \ f"description='{self.description}',\n" \ f"editable='{self.editable}',\n" \ f"plugin='{self.plugin}',\n" \ @@ -105,6 +116,7 @@ class Variable: "name": self.name, "value": self.value, "type": self.type.value, + "unit": self.unit.value if self.unit else None, "description": self.description, "editable": self.editable, "plugin": self.plugin, @@ -130,7 +142,14 @@ class Variable: if isinstance(self._selectables, list): for selectable in self.selectables: if selectable.key == value: - return str(selectable.label) + value = str(selectable.label) + break + + if self.unit == VariableUnit.BYTE: + value = "{} {}".format( + value / 1024 / 1024, + "MB" + ) return value diff --git a/src/model/enum/VariableUnit.py b/src/model/enum/VariableUnit.py new file mode 100644 index 0000000..f20c1d6 --- /dev/null +++ b/src/model/enum/VariableUnit.py @@ -0,0 +1,6 @@ +from enum import Enum + + +class VariableUnit(Enum): + + BYTE = 'byte' diff --git a/src/service/ModelStore.py b/src/service/ModelStore.py index a749c72..5f5d6ac 100644 --- a/src/service/ModelStore.py +++ b/src/service/ModelStore.py @@ -2,6 +2,7 @@ from src.manager.SlideManager import SlideManager from src.manager.ScreenManager import ScreenManager from src.manager.VariableManager import VariableManager from src.manager.LangManager import LangManager +from src.manager.DatabaseManager import DatabaseManager from src.manager.ConfigManager import ConfigManager from src.manager.LoggingManager import LoggingManager @@ -9,11 +10,12 @@ from src.manager.LoggingManager import LoggingManager class ModelStore: def __init__(self): - self._variable_manager = VariableManager() + self._database_manager = DatabaseManager() + self._variable_manager = VariableManager(database_manager=self._database_manager) self._config_manager = ConfigManager(variable_manager=self._variable_manager) self._logging_manager = LoggingManager(config_manager=self._config_manager) - self._screen_manager = ScreenManager() - self._slide_manager = SlideManager() + self._screen_manager = ScreenManager(database_manager=self._database_manager) + self._slide_manager = SlideManager(database_manager=self._database_manager) self._lang_manager = LangManager(lang=self.variable().map().get('lang').as_string()) self._variable_manager.reload(lang_map=self._lang_manager.map()) @@ -26,6 +28,9 @@ class ModelStore: def variable(self) -> VariableManager: return self._variable_manager + def database(self) -> DatabaseManager: + return self._database_manager + def slide(self) -> SlideManager: return self._slide_manager diff --git a/src/service/WebServer.py b/src/service/WebServer.py index 1dce679..be77560 100644 --- a/src/service/WebServer.py +++ b/src/service/WebServer.py @@ -15,8 +15,6 @@ from src.constant.WebDirConstant import WebDirConstant class WebServer: - MAX_UPLOADS = 16 * 1024 * 1024 - def __init__(self, project_dir: str, model_store: ModelStore, template_renderer: TemplateRenderer): self._project_dir = project_dir self._model_store = model_store @@ -54,7 +52,7 @@ class WebServer: ) self._app.config['UPLOAD_FOLDER'] = "{}/{}".format(WebDirConstant.FOLDER_STATIC, WebDirConstant.FOLDER_STATIC_WEB_UPLOADS) - self._app.config['MAX_CONTENT_LENGTH'] = self.MAX_UPLOADS + self._app.config['MAX_CONTENT_LENGTH'] = self._model_store.variable().map().get('slide_upload_limit').as_int() if self._debug: self._app.config['TEMPLATES_AUTO_RELOAD'] = True diff --git a/src/utils.py b/src/utils.py index 90af616..0d6467b 100644 --- a/src/utils.py +++ b/src/utils.py @@ -60,6 +60,14 @@ def get_keys(dict_or_object, key_list_name: str, key_attr_name: str = 'key') -> return None +def enum_to_str(enum: Optional[Enum]) -> Optional[str]: + if enum: + print(enum) + return str(enum.value) + + return None + + def str_to_enum(str_val: str, enum_class) -> Enum: for enum_item in enum_class: if enum_item.value == str_val: