commit
260366fb01
@ -6,7 +6,6 @@ out/
|
||||
data/uploads/*
|
||||
!data/uploads/sample.jpg
|
||||
data/db/*
|
||||
!data/db/slideshow.json.dist
|
||||
/plugins/user/*
|
||||
!/plugins/user/.gitkeep
|
||||
*.lock
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -6,7 +6,6 @@ out/
|
||||
data/uploads/*
|
||||
!data/uploads/sample.jpg
|
||||
data/db/*
|
||||
!data/db/slideshow.json.dist
|
||||
/plugins/user/*
|
||||
!/plugins/user/.gitkeep
|
||||
*.lock
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
FROM python:3.9.17-alpine3.17
|
||||
|
||||
RUN apk add --no-cache --virtual .build-deps gcc musl-dev sqlite-dev
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN pip install -r requirements.txt
|
||||
RUN pip install -r requirements.txt && apk del .build-deps gcc musl-dev sqlite-dev
|
||||
|
||||
ENTRYPOINT ["python", "/app/obscreen.py"]
|
||||
|
||||
@ -15,7 +15,7 @@ Use a RaspberryPi (Lite OS) to show a full-screen slideshow (Kiosk-mode)
|
||||
- Clear GUI
|
||||
- Fleet view to manage many devices easily
|
||||
- Very few dependencies
|
||||
- JSON database files
|
||||
- SQLite database
|
||||
- Plugin system
|
||||
- No stupid pricing plan
|
||||
- No cloud
|
||||
|
||||
@ -1,37 +0,0 @@
|
||||
{
|
||||
"version": 2,
|
||||
"keys": [
|
||||
"duration",
|
||||
"enabled",
|
||||
"location",
|
||||
"name",
|
||||
"position",
|
||||
"type",
|
||||
"cron_schedule",
|
||||
"cron_schedule_end",
|
||||
"created_by",
|
||||
"updated_by",
|
||||
"created_at",
|
||||
"updated_at"
|
||||
],
|
||||
"data": {
|
||||
"0": {
|
||||
"location": "data/uploads/sample.jpg",
|
||||
"duration": 10,
|
||||
"type": "picture",
|
||||
"enabled": true,
|
||||
"name": "Picture Sample",
|
||||
"position": 0,
|
||||
"cron_schedule": null
|
||||
},
|
||||
"1": {
|
||||
"location": "https://unix.org",
|
||||
"duration": 20,
|
||||
"type": "url",
|
||||
"enabled": true,
|
||||
"name": "URL Sample",
|
||||
"position": 1,
|
||||
"cron_schedule": null
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -9,7 +9,7 @@ jQuery(document).ready(function ($) {
|
||||
if (confirm(l.js_sysinfo_restart_confirmation)) {
|
||||
$('body').html(l.js_sysinfo_restart_loading).css({margin:200});
|
||||
$.ajax({
|
||||
url: '/sysinfo/restart',
|
||||
url: '/sysinfo/restart?secret_key='+secret_key,
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
data: '',
|
||||
method: 'POST',
|
||||
|
||||
@ -53,10 +53,10 @@ cd ~ && git clone https://github.com/jr-k/obscreen.git && cd obscreen
|
||||
# Install application dependencies
|
||||
python3 -m venv venv
|
||||
source ./venv/bin/activate
|
||||
pip install -r requirements.txt
|
||||
|
||||
# Add some sample data
|
||||
cp data/db/slideshow.json.dist data/db/slideshow.json
|
||||
# 🚨For MacOS users, requirements installation may cause an error but it's ok if only for pysqlite3 package
|
||||
# you'll need to install brew and execute command `brew install sqlite3`
|
||||
pip install -r requirements.txt
|
||||
|
||||
# Customize server default values
|
||||
cp .env.dist .env
|
||||
|
||||
@ -72,9 +72,6 @@ python3 -m venv venv
|
||||
source ./venv/bin/activate
|
||||
pip install -r requirements.txt
|
||||
|
||||
# Add some sample data
|
||||
cp data/db/slideshow.json.dist data/db/slideshow.json
|
||||
|
||||
# Customize server default values
|
||||
cp .env.dist .env
|
||||
```
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
flask==2.3.3
|
||||
pysondb-v2==2.1.0
|
||||
python-dotenv
|
||||
cron-descriptor
|
||||
waitress
|
||||
flask-login
|
||||
pysqlite3
|
||||
|
||||
@ -18,8 +18,9 @@ class Application:
|
||||
self._model_store = ModelStore()
|
||||
self._template_renderer = TemplateRenderer(project_dir=project_dir, model_store=self._model_store, render_hook=self.render_hook)
|
||||
self._web_server = WebServer(project_dir=project_dir, model_store=self._model_store, template_renderer=self._template_renderer)
|
||||
self._plugin_store = PluginStore(project_dir=project_dir, model_store=self._model_store, template_renderer=self._template_renderer, web_server=self._web_server)
|
||||
|
||||
logging.info("[Obscreen] Starting...")
|
||||
self._plugin_store = PluginStore(project_dir=project_dir, model_store=self._model_store, template_renderer=self._template_renderer, web_server=self._web_server)
|
||||
signal.signal(signal.SIGINT, self.signal_handler)
|
||||
|
||||
def start(self) -> None:
|
||||
@ -27,6 +28,7 @@ class Application:
|
||||
|
||||
def signal_handler(self, signal, frame) -> None:
|
||||
logging.info("Shutting down...")
|
||||
self._model_store.database().close()
|
||||
self._stop_event.set()
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
@ -24,6 +24,9 @@ class AuthController(ObController):
|
||||
if current_user.is_authenticated:
|
||||
return redirect(url_for('slideshow_slide_list'))
|
||||
|
||||
if not self._model_store.variable().map().get('auth_enabled').as_bool():
|
||||
return redirect(url_for('slideshow_slide_list'))
|
||||
|
||||
if len(request.form):
|
||||
user = self._model_store.user().get_one_by_username(request.form['username'], enabled=True)
|
||||
if user:
|
||||
@ -42,6 +45,13 @@ class AuthController(ObController):
|
||||
|
||||
def logout(self):
|
||||
logout_user()
|
||||
|
||||
if request.args.get('restart'):
|
||||
return redirect(url_for(
|
||||
'sysinfo_restart',
|
||||
secret_key=self._model_store.config().map().get('secret_key')
|
||||
))
|
||||
|
||||
return redirect(url_for('login'))
|
||||
|
||||
def auth_user_list(self):
|
||||
|
||||
@ -24,7 +24,7 @@ class SettingsController(ObController):
|
||||
forward = self._post_update(request.form['id'])
|
||||
return forward if forward is not None else redirect(url_for('settings_variable_list'))
|
||||
|
||||
def _post_update(self, id: str):
|
||||
def _post_update(self, id: int):
|
||||
variable = self._model_store.variable().get(id)
|
||||
|
||||
if variable.refresh_player:
|
||||
@ -39,7 +39,10 @@ class SettingsController(ObController):
|
||||
if variable.name == 'auth_enabled':
|
||||
self.reload_web_server()
|
||||
if variable.as_bool():
|
||||
return redirect(url_for('logout'))
|
||||
return redirect(url_for(
|
||||
'logout',
|
||||
restart=1
|
||||
))
|
||||
|
||||
if variable.name == 'lang':
|
||||
self._model_store.lang().set_lang(variable.value)
|
||||
|
||||
@ -2,21 +2,23 @@ import os
|
||||
import sys
|
||||
import platform
|
||||
import subprocess
|
||||
import threading
|
||||
import time
|
||||
|
||||
from flask import Flask, render_template, jsonify
|
||||
from flask import Flask, render_template, jsonify, request, url_for, redirect
|
||||
from src.manager.VariableManager import VariableManager
|
||||
from src.manager.ConfigManager import ConfigManager
|
||||
from src.service.ModelStore import ModelStore
|
||||
|
||||
from src.interface.ObController import ObController
|
||||
from src.utils import get_ip_address
|
||||
from src.utils import get_ip_address, am_i_in_docker
|
||||
|
||||
|
||||
class SysinfoController(ObController):
|
||||
|
||||
def register(self):
|
||||
self._app.add_url_rule('/sysinfo', 'sysinfo_attribute_list', self._auth(self.sysinfo), methods=['GET'])
|
||||
self._app.add_url_rule('/sysinfo/restart', 'sysinfo_restart', self._auth(self.sysinfo_restart), methods=['POST'])
|
||||
self._app.add_url_rule('/sysinfo/restart', 'sysinfo_restart', self.sysinfo_restart, methods=['GET', 'POST'])
|
||||
self._app.add_url_rule('/sysinfo/restart/needed', 'sysinfo_restart_needed', self._auth(self.sysinfo_restart_needed), methods=['GET'])
|
||||
|
||||
def sysinfo(self):
|
||||
@ -29,20 +31,13 @@ class SysinfoController(ObController):
|
||||
)
|
||||
|
||||
def sysinfo_restart(self):
|
||||
if platform.system().lower() == 'darwin':
|
||||
if self._model_store.config().map().get('debug'):
|
||||
python = sys.executable
|
||||
os.execl(python, python, *sys.argv)
|
||||
else:
|
||||
try:
|
||||
subprocess.run(["sudo", "systemctl", "restart", 'obscreen'], check=True, timeout=10, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
pass
|
||||
except subprocess.TimeoutExpired:
|
||||
pass
|
||||
except subprocess.CalledProcessError:
|
||||
pass
|
||||
secret = self._model_store.config().map().get('secret_key')
|
||||
challenge = request.args.get('secret_key')
|
||||
thread = threading.Thread(target=self.restart, args=(secret, challenge))
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
|
||||
return jsonify({'status': 'ok'})
|
||||
return redirect(url_for('manage'))
|
||||
|
||||
def sysinfo_restart_needed(self):
|
||||
var_last_slide_update = self._model_store.variable().get_one_by_name('last_slide_update')
|
||||
@ -53,3 +48,24 @@ class SysinfoController(ObController):
|
||||
|
||||
return jsonify({'status': True})
|
||||
|
||||
def restart(self, secret: str, challenge: str) -> None:
|
||||
time.sleep(1)
|
||||
|
||||
if secret != challenge:
|
||||
return jsonify({'status': 'error'})
|
||||
|
||||
if platform.system().lower() == 'darwin':
|
||||
if self._model_store.config().map().get('debug'):
|
||||
python = sys.executable
|
||||
os.execl(python, python, *sys.argv)
|
||||
elif am_i_in_docker:
|
||||
python = sys.executable
|
||||
os.execl(python, python, *sys.argv)
|
||||
else:
|
||||
try:
|
||||
subprocess.run(["sudo", "systemctl", "restart", 'obscreen-manager'], check=True, timeout=10, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
pass
|
||||
except subprocess.TimeoutExpired:
|
||||
pass
|
||||
except subprocess.CalledProcessError:
|
||||
pass
|
||||
|
||||
@ -1,33 +1,144 @@
|
||||
import os
|
||||
import json
|
||||
import sys
|
||||
|
||||
from pysondb import PysonDB
|
||||
from typing import Optional
|
||||
import sqlite3
|
||||
import logging
|
||||
|
||||
from sqlite3 import Cursor
|
||||
from src.utils import wrap_if, is_wrapped_by
|
||||
from typing import Optional, Dict
|
||||
|
||||
class DatabaseManager:
|
||||
|
||||
DB_DIR = 'data/db'
|
||||
DB_FILE: str = "data/db/obscreen.db"
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
self._conn = None
|
||||
self._enabled = True
|
||||
self.init()
|
||||
|
||||
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
|
||||
def init(self):
|
||||
logging.info('Using DB engine {}'.format(self.__class__.__name__))
|
||||
self._open()
|
||||
|
||||
def _open(self, flush: bool = False) -> None:
|
||||
if flush and os.path.isfile(self.DB_FILE):
|
||||
os.unlink(self.DB_FILE)
|
||||
|
||||
self._conn = sqlite3.connect(self.DB_FILE, check_same_thread=False)
|
||||
self._conn.row_factory = sqlite3.Row
|
||||
|
||||
def open(self, table_name: str, table_model: list):
|
||||
self.execute_write_query('''CREATE TABLE IF NOT EXISTS {} (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
{}
|
||||
)'''.format(table_name, ", ".join(table_model)))
|
||||
|
||||
return self
|
||||
|
||||
def close(self) -> None:
|
||||
self._conn.close()
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
self.close()
|
||||
|
||||
def get_connection(self):
|
||||
return self._conn
|
||||
|
||||
def execute_write_query(self, query, params=()) -> None:
|
||||
logging.debug(query)
|
||||
cur = None
|
||||
sanitized_params = []
|
||||
|
||||
for param in params:
|
||||
if isinstance(param, bool):
|
||||
sanitized_params.append(int(param))
|
||||
elif isinstance(param, dict) or isinstance(param, list):
|
||||
sanitized_params.append(json.dumps(param))
|
||||
else:
|
||||
sanitized_params.append(param)
|
||||
|
||||
@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
|
||||
with self._conn:
|
||||
cur = self._conn.cursor()
|
||||
cur.execute(query, tuple(sanitized_params))
|
||||
except sqlite3.Error as e:
|
||||
logging.error("SQL query execution error while writing '{}': {}".format(query, e))
|
||||
self._conn.rollback()
|
||||
finally:
|
||||
if cur is not None:
|
||||
cur.close()
|
||||
|
||||
def execute_read_query(self, query, params=()) -> list:
|
||||
logging.debug(query)
|
||||
cur = None
|
||||
|
||||
try:
|
||||
with self._conn:
|
||||
cur = self._conn.cursor()
|
||||
cur.execute(query, params)
|
||||
rows = cur.fetchall()
|
||||
result = [dict(row) for row in rows]
|
||||
except sqlite3.Error as e:
|
||||
logging.error("SQL query execution error while reading '{}': {}".format(query, e))
|
||||
result = []
|
||||
finally:
|
||||
if cur is not None:
|
||||
cur.close()
|
||||
|
||||
return result
|
||||
|
||||
def get_all(self, table_name: str, sort: Optional[str] = None) -> list:
|
||||
return self.execute_read_query(
|
||||
query="select * from {} {}".format(table_name, "ORDER BY {} ASC".format(sort) if sort else "")
|
||||
)
|
||||
|
||||
def get_by_query(self, table_name: str, query: str = "1=1", sort: Optional[str] = None) -> list:
|
||||
return self.execute_read_query(
|
||||
query="select * from {} where {} {}".format(
|
||||
table_name,
|
||||
query,
|
||||
"ORDER BY {} ASC".format(sort) if sort else ""
|
||||
)
|
||||
)
|
||||
|
||||
def get_one_by_query(self, table_name: str, query: str = "1=1", sort: Optional[str] = None) -> list:
|
||||
query = "select * from {} where {} {}".format(table_name, query, "ORDER BY {} ASC".format(sort) if sort else "")
|
||||
lines = self.execute_read_query(query=query)
|
||||
count = len(lines)
|
||||
|
||||
if count > 1:
|
||||
raise Error("More than one line returned by query '{}'".format(query))
|
||||
|
||||
return lines[0] if count == 1 else None
|
||||
|
||||
def update_by_query(self, table_name: str, query: str = "1=1", values: dict = {}) -> list:
|
||||
return self.execute_write_query(
|
||||
query="UPDATE {} SET {} where {}".format(
|
||||
table_name,
|
||||
" , ".join(["{} = ?".format(k, v) for k, v in values.items()]),
|
||||
query
|
||||
),
|
||||
params=tuple(v for v in values.values())
|
||||
)
|
||||
|
||||
def update_by_id(self, table_name: str, id: int, values: dict = {}) -> list:
|
||||
return self.update_by_query(table_name, "id = {}".format(id), values)
|
||||
|
||||
def get_by_id(self, table_name: str, id: int) -> Optional[Dict]:
|
||||
return self.get_one_by_query(table_name, "id = {}".format(id))
|
||||
|
||||
def add(self, table_name: str, values: dict) -> None:
|
||||
self.execute_write_query(
|
||||
query="INSERT INTO {} ({}) VALUES ({})".format(
|
||||
table_name,
|
||||
", ".join(["{}".format(key) for key in values.keys()]),
|
||||
", ".join(["?" for _ in values.keys()]),
|
||||
),
|
||||
params=tuple(v for v in values.values())
|
||||
)
|
||||
|
||||
def delete_by_id(self, table_name: str, id: int) -> None:
|
||||
self.execute_write_query("DELETE FROM {} WHERE id = ?".format(table_name), params=(id,))
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
from pysondb.errors import IdDoesNotExistError
|
||||
from typing import Dict, Optional, List, Tuple, Union
|
||||
|
||||
from src.model.entity.Screen import Screen
|
||||
@ -12,71 +11,59 @@ class ScreenManager(ModelManager):
|
||||
|
||||
TABLE_NAME = "fleet"
|
||||
TABLE_MODEL = [
|
||||
"name",
|
||||
"enabled",
|
||||
"position",
|
||||
"host",
|
||||
"port"
|
||||
"name CHAR(255)",
|
||||
"enabled INTEGER",
|
||||
"position INTEGER",
|
||||
"host CHAR(255)",
|
||||
"port INTEGER"
|
||||
]
|
||||
|
||||
def __init__(self, lang_manager: LangManager, database_manager: DatabaseManager, user_manager: UserManager):
|
||||
super().__init__(lang_manager, database_manager, user_manager)
|
||||
self._db = database_manager.open(self.TABLE_NAME, self.TABLE_MODEL)
|
||||
|
||||
def hydrate_object(self, raw_screen: dict, id: Optional[str] = None) -> Screen:
|
||||
def hydrate_object(self, raw_screen: dict, id: Optional[int] = None) -> Screen:
|
||||
if id:
|
||||
raw_screen['id'] = id
|
||||
|
||||
return Screen(**raw_screen)
|
||||
|
||||
def hydrate_dict(self, raw_screens: dict) -> List[Screen]:
|
||||
return [self.hydrate_object(raw_screen, raw_id) for raw_id, raw_screen in raw_screens.items()]
|
||||
|
||||
def hydrate_list(self, raw_screens: list) -> List[Screen]:
|
||||
return [self.hydrate_object(raw_screen) for raw_screen in raw_screens]
|
||||
|
||||
def get(self, id: str) -> Optional[Screen]:
|
||||
try:
|
||||
return self.hydrate_object(self._db.get_by_id(id), id)
|
||||
except IdDoesNotExistError:
|
||||
return None
|
||||
def get(self, id: int) -> Optional[Screen]:
|
||||
object = self._db.get_by_id(self.TABLE_NAME, id)
|
||||
return self.hydrate_object(object, id) if object else None
|
||||
|
||||
def get_by(self, query) -> List[Screen]:
|
||||
return self.hydrate_dict(self._db.get_by_query(query=query))
|
||||
def get_by(self, query, sort: Optional[str] = None) -> List[Screen]:
|
||||
return self.hydrate_list(self._db.get_by_query(self.TABLE_NAME, query=query, sort=sort))
|
||||
|
||||
def get_one_by(self, query) -> Optional[Screen]:
|
||||
screens = self.hydrate_dict(self._db.get_by_query(query=query))
|
||||
if len(screens) == 1:
|
||||
return screens[0]
|
||||
elif len(screens) > 1:
|
||||
raise Error("More than one result for query")
|
||||
object = self._db.get_one_by_query(self.TABLE_NAME, query=query)
|
||||
|
||||
if not object:
|
||||
return None
|
||||
|
||||
return self.hydrate_object(object)
|
||||
|
||||
def get_all(self, sort: bool = False) -> List[Screen]:
|
||||
raw_screens = self._db.get_all()
|
||||
|
||||
if isinstance(raw_screens, dict):
|
||||
if sort:
|
||||
return sorted(self.hydrate_dict(raw_screens), key=lambda x: x.position)
|
||||
return self.hydrate_dict(raw_screens)
|
||||
|
||||
return self.hydrate_list(sorted(raw_screens, key=lambda x: x['position']) if sort else raw_screens)
|
||||
return self.hydrate_list(self._db.get_all(self.TABLE_NAME, "position" if sort else None))
|
||||
|
||||
def get_enabled_screens(self) -> List[Screen]:
|
||||
return [screen for screen in self.get_all(sort=True) if screen.enabled]
|
||||
return self.get_by(query="enabled = 1", sort="position")
|
||||
|
||||
def get_disabled_screens(self) -> List[Screen]:
|
||||
return [screen for screen in self.get_all(sort=True) if not screen.enabled]
|
||||
return self.get_by(query="enabled = 0", sort="position")
|
||||
|
||||
def update_enabled(self, id: str, enabled: bool) -> None:
|
||||
self._db.update_by_id(id, {"enabled": enabled, "position": 999})
|
||||
def update_enabled(self, id: int, enabled: bool) -> None:
|
||||
self._db.update_by_id(self.TABLE_NAME, id, {"enabled": enabled, "position": 999})
|
||||
|
||||
def update_positions(self, positions: list) -> None:
|
||||
for screen_id, screen_position in positions.items():
|
||||
self._db.update_by_id(screen_id, {"position": screen_position})
|
||||
self._db.update_by_id(self.TABLE_NAME, screen_id, {"position": screen_position})
|
||||
|
||||
def update_form(self, id: str, name: str, host: str, port: int) -> None:
|
||||
self._db.update_by_id(id, {"name": name, "host": host, "port": port})
|
||||
def update_form(self, id: int, name: str, host: str, port: int) -> None:
|
||||
self._db.update_by_id(self.TABLE_NAME, id, {"name": name, "host": host, "port": port})
|
||||
|
||||
def add_form(self, screen: Union[Screen, Dict]) -> None:
|
||||
form = screen
|
||||
@ -85,10 +72,10 @@ class ScreenManager(ModelManager):
|
||||
form = screen.to_dict()
|
||||
del form['id']
|
||||
|
||||
self._db.add(form)
|
||||
self._db.add(self.TABLE_NAME, form)
|
||||
|
||||
def delete(self, id: str) -> None:
|
||||
self._db.delete_by_id(id)
|
||||
def delete(self, id: int) -> None:
|
||||
self._db.delete_by_id(self.TABLE_NAME, id)
|
||||
|
||||
def to_dict(self, screens: List[Screen]) -> List[Dict]:
|
||||
return [screen.to_dict() for screen in screens]
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import os
|
||||
|
||||
from typing import Dict, Optional, List, Tuple, Union
|
||||
from pysondb.errors import IdDoesNotExistError
|
||||
|
||||
from src.model.entity.Slide import Slide
|
||||
from src.model.enum.SlideType import SlideType
|
||||
@ -16,80 +15,68 @@ class SlideManager(ModelManager):
|
||||
|
||||
TABLE_NAME = "slideshow"
|
||||
TABLE_MODEL = [
|
||||
"name",
|
||||
"type",
|
||||
"enabled",
|
||||
"duration",
|
||||
"position",
|
||||
"location",
|
||||
"cron_schedule",
|
||||
"cron_schedule_end",
|
||||
"created_by",
|
||||
"updated_by",
|
||||
"created_at",
|
||||
"updated_at"
|
||||
"name CHAR(255)",
|
||||
"type CHAR(30)",
|
||||
"enabled INTEGER",
|
||||
"duration INTEGER",
|
||||
"position INTEGER",
|
||||
"location TEXT",
|
||||
"cron_schedule CHAR(255)",
|
||||
"cron_schedule_end CHAR(255)",
|
||||
"created_by CHAR(255)",
|
||||
"updated_by CHAR(255)",
|
||||
"created_at INTEGER",
|
||||
"updated_at INTEGER"
|
||||
]
|
||||
|
||||
def __init__(self, lang_manager: LangManager, database_manager: DatabaseManager, user_manager: UserManager):
|
||||
super().__init__(lang_manager, database_manager, user_manager)
|
||||
self._db = database_manager.open(self.TABLE_NAME, self.TABLE_MODEL)
|
||||
|
||||
def hydrate_object(self, raw_slide: dict, id: str = None) -> Slide:
|
||||
def hydrate_object(self, raw_slide: dict, id: int = None) -> Slide:
|
||||
if id:
|
||||
raw_slide['id'] = id
|
||||
|
||||
[raw_slide, user_tracker_edits] = self.user_manager.initialize_user_trackers(raw_slide)
|
||||
|
||||
if len(user_tracker_edits) > 0:
|
||||
self._db.update_by_id(raw_slide['id'], user_tracker_edits)
|
||||
self._db.update_by_id(self.TABLE_NAME, raw_slide['id'], user_tracker_edits)
|
||||
|
||||
return Slide(**raw_slide)
|
||||
|
||||
def hydrate_dict(self, raw_slides: dict) -> List[Slide]:
|
||||
return [self.hydrate_object(raw_slide, raw_id) for raw_id, raw_slide in raw_slides.items()]
|
||||
|
||||
def hydrate_list(self, raw_slides: list) -> List[Slide]:
|
||||
return [self.hydrate_object(raw_slide) for raw_slide in raw_slides]
|
||||
|
||||
def get(self, id: str) -> Optional[Slide]:
|
||||
try:
|
||||
return self.hydrate_object(self._db.get_by_id(id), id)
|
||||
except IdDoesNotExistError:
|
||||
return None
|
||||
def get(self, id: int) -> Optional[Slide]:
|
||||
object = self._db.get_by_id(self.TABLE_NAME, id)
|
||||
return self.hydrate_object(object, id) if object else None
|
||||
|
||||
def get_by(self, query) -> List[Slide]:
|
||||
return self.hydrate_dict(self._db.get_by_query(query=query))
|
||||
def get_by(self, query, sort: Optional[str] = None) -> List[Slide]:
|
||||
return self.hydrate_list(self._db.get_by_query(self.TABLE_NAME, query=query, sort=sort))
|
||||
|
||||
def get_one_by(self, query) -> Optional[Slide]:
|
||||
slides = self.hydrate_dict(self._db.get_by_query(query=query))
|
||||
if len(slides) == 1:
|
||||
return slides[0]
|
||||
elif len(slides) > 1:
|
||||
raise Error("More than one result for query")
|
||||
object = self._db.get_one_by_query(self.TABLE_NAME, query=query)
|
||||
|
||||
if not object:
|
||||
return None
|
||||
|
||||
return self.hydrate_object(object)
|
||||
|
||||
def get_all(self, sort: bool = False) -> List[Slide]:
|
||||
raw_slides = self._db.get_all()
|
||||
return self.hydrate_list(self._db.get_all(self.TABLE_NAME, sort="position" if sort else None))
|
||||
|
||||
if isinstance(raw_slides, dict):
|
||||
if sort:
|
||||
return sorted(self.hydrate_dict(raw_slides), key=lambda x: x.position)
|
||||
return self.hydrate_dict(raw_slides)
|
||||
|
||||
return self.hydrate_list(sorted(raw_slides, key=lambda x: x['position']) if sort else raw_slides)
|
||||
|
||||
def forget_user(self, user_id: str):
|
||||
slides = self.hydrate_dict(self._db.get_by_query(query=lambda s: s['created_by'] == user_id or s['updated_by'] == user_id))
|
||||
def forget_user(self, user_id: int):
|
||||
slides = self.get_by("created_by = '{}' or updated_by = '{}'".format(user_id, user_id))
|
||||
edits_slides = self.user_manager.forget_user(slides, user_id)
|
||||
|
||||
for slide_id, edits in edits_slides.items():
|
||||
self._db.update_by_id(slide_id, edits)
|
||||
self._db.update_by_id(self.TABLE_NAME, slide_id, edits)
|
||||
|
||||
def get_enabled_slides(self) -> List[Slide]:
|
||||
return [slide for slide in self.get_all(sort=True) if slide.enabled]
|
||||
return self.get_by(query="enabled = 1", sort="position")
|
||||
|
||||
def get_disabled_slides(self) -> List[Slide]:
|
||||
return [slide for slide in self.get_all(sort=True) if not slide.enabled]
|
||||
return self.get_by(query="enabled = 0", sort="position")
|
||||
|
||||
def pre_add(self, slide: Dict) -> Dict:
|
||||
self.user_manager.track_user_on_create(slide)
|
||||
@ -115,15 +102,15 @@ class SlideManager(ModelManager):
|
||||
def post_delete(self, slide_id: str) -> str:
|
||||
return slide_id
|
||||
|
||||
def update_enabled(self, id: str, enabled: bool) -> None:
|
||||
self._db.update_by_id(id, self.pre_update({"enabled": enabled, "position": 999}))
|
||||
def update_enabled(self, id: int, enabled: bool) -> None:
|
||||
self._db.update_by_id(self.TABLE_NAME, id, self.pre_update({"enabled": enabled, "position": 999}))
|
||||
self.post_update(id)
|
||||
|
||||
def update_positions(self, positions: list) -> None:
|
||||
for slide_id, slide_position in positions.items():
|
||||
self._db.update_by_id(slide_id, {"position": slide_position})
|
||||
self._db.update_by_id(self.TABLE_NAME, slide_id, {"position": slide_position})
|
||||
|
||||
def update_form(self, id: str, name: str, duration: int, cron_schedule: Optional[str] = '', cron_schedule_end: Optional[str] = '', location: Optional[str] = None) -> None:
|
||||
def update_form(self, id: int, name: str, duration: int, cron_schedule: Optional[str] = '', cron_schedule_end: Optional[str] = '', location: Optional[str] = None) -> None:
|
||||
slide = self.get(id)
|
||||
|
||||
if not slide:
|
||||
@ -142,7 +129,7 @@ class SlideManager(ModelManager):
|
||||
if slide.type == SlideType.YOUTUBE:
|
||||
form['location'] = get_yt_video_id(form['location'])
|
||||
|
||||
self._db.update_by_id(id, self.pre_update(form))
|
||||
self._db.update_by_id(self.TABLE_NAME, id, self.pre_update(form))
|
||||
self.post_update(id)
|
||||
|
||||
def add_form(self, slide: Union[Slide, Dict]) -> None:
|
||||
@ -155,10 +142,10 @@ class SlideManager(ModelManager):
|
||||
if form['type'] == SlideType.YOUTUBE.value:
|
||||
form['location'] = get_yt_video_id(form['location'])
|
||||
|
||||
self._db.add(self.pre_add(form))
|
||||
self._db.add(self.TABLE_NAME, self.pre_add(form))
|
||||
self.post_add(slide.id)
|
||||
|
||||
def delete(self, id: str) -> None:
|
||||
def delete(self, id: int) -> None:
|
||||
slide = self.get(id)
|
||||
|
||||
if slide:
|
||||
@ -169,7 +156,7 @@ class SlideManager(ModelManager):
|
||||
pass
|
||||
|
||||
self.pre_delete(id)
|
||||
self._db.delete_by_id(id)
|
||||
self._db.delete_by_id(self.TABLE_NAME, id)
|
||||
self.post_delete(id)
|
||||
|
||||
def to_dict(self, slides: List[Slide]) -> List[Dict]:
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import hashlib
|
||||
import time
|
||||
from pysondb.errors import IdDoesNotExistError
|
||||
from typing import Dict, Optional, List, Tuple, Union
|
||||
from flask_login import current_user
|
||||
|
||||
@ -14,9 +13,9 @@ class UserManager:
|
||||
|
||||
TABLE_NAME = "user"
|
||||
TABLE_MODEL = [
|
||||
"username",
|
||||
"password",
|
||||
"enabled"
|
||||
"username CHAR(255)",
|
||||
"password CHAR(255)",
|
||||
"enabled INTEGER"
|
||||
]
|
||||
|
||||
def __init__(self, lang_manager: LangManager, database_manager: DatabaseManager, on_user_delete):
|
||||
@ -44,37 +43,32 @@ class UserManager:
|
||||
|
||||
return user_map
|
||||
|
||||
def hydrate_object(self, raw_user: dict, id: Optional[str] = None) -> User:
|
||||
def hydrate_object(self, raw_user: dict, id: Optional[int] = None) -> User:
|
||||
if id:
|
||||
raw_user['id'] = id
|
||||
|
||||
return User(**raw_user)
|
||||
|
||||
def hydrate_dict(self, raw_users: dict) -> List[User]:
|
||||
return [self.hydrate_object(raw_user, raw_id) for raw_id, raw_user in raw_users.items()]
|
||||
|
||||
def hydrate_list(self, raw_users: list) -> List[User]:
|
||||
return [self.hydrate_object(raw_user) for raw_user in raw_users]
|
||||
|
||||
def get(self, id: str) -> Optional[User]:
|
||||
try:
|
||||
return self.hydrate_object(self._db.get_by_id(id), id)
|
||||
except IdDoesNotExistError:
|
||||
return None
|
||||
def get(self, id: int) -> Optional[User]:
|
||||
object = self._db.get_by_id(self.TABLE_NAME, id)
|
||||
return self.hydrate_object(object, id) if object else None
|
||||
|
||||
def get_by(self, query) -> List[User]:
|
||||
return self.hydrate_dict(self._db.get_by_query(query=query))
|
||||
def get_by(self, query, sort: Optional[str] = None) -> List[User]:
|
||||
return self.hydrate_list(self._db.get_by_query(self.TABLE_NAME, query=query, sort=sort))
|
||||
|
||||
def get_one_by(self, query) -> Optional[User]:
|
||||
users = self.hydrate_dict(self._db.get_by_query(query=query))
|
||||
if len(users) == 1:
|
||||
return users[0]
|
||||
elif len(users) > 1:
|
||||
raise Error("More than one result for query")
|
||||
object = self._db.get_one_by_query(self.TABLE_NAME, query=query)
|
||||
|
||||
if not object:
|
||||
return None
|
||||
|
||||
return self.hydrate_object(object)
|
||||
|
||||
def get_one_by_username(self, username: str, enabled: bool = None) -> Optional[User]:
|
||||
return self.get_one_by(query=lambda v: v['username'] == username and (enabled is None or v['enabled'] == enabled))
|
||||
return self.get_one_by("username = '{}' and (enabled is null or enabled = {})".format(username, int(enabled)))
|
||||
|
||||
def count_all_enabled(self):
|
||||
return len(self.get_enabled_users())
|
||||
@ -85,12 +79,16 @@ class UserManager:
|
||||
def track_user_updated(self, id_or_entity: Optional[str]) -> User:
|
||||
return self.track_user_action(id_or_entity, 'updated_by')
|
||||
|
||||
def track_user_action(self, id_or_entity: Optional[str], attribute: Optional[str] = 'created_by') -> User:
|
||||
if not isinstance(id_or_entity, str):
|
||||
def track_user_action(self, id_or_entity: Optional[int], attribute: Optional[str] = 'created_by') -> User:
|
||||
if not isinstance(id_or_entity, int):
|
||||
id_or_entity = getattr(id_or_entity, attribute)
|
||||
id_or_entity = str(id_or_entity)
|
||||
|
||||
user_map = self.map()
|
||||
try:
|
||||
id_or_entity = int(id_or_entity)
|
||||
except ValueError:
|
||||
return User(username=id_or_entity, enabled=False)
|
||||
|
||||
user_map = self.prepare_map()
|
||||
|
||||
if id_or_entity in user_map:
|
||||
return user_map[id_or_entity]
|
||||
@ -101,20 +99,13 @@ class UserManager:
|
||||
return User(username=self._lang_manager.translate('anonymous'), enabled=False)
|
||||
|
||||
def get_all(self, sort: bool = False) -> List[User]:
|
||||
raw_users = self._db.get_all()
|
||||
|
||||
if isinstance(raw_users, dict):
|
||||
if sort:
|
||||
return sorted(self.hydrate_dict(raw_users), key=lambda x: x.username)
|
||||
return self.hydrate_dict(raw_users)
|
||||
|
||||
return self.hydrate_list(sorted(raw_users, key=lambda x: x['username']) if sort else raw_users)
|
||||
return self.hydrate_list(self._db.get_all(self.TABLE_NAME, "username" if sort else None))
|
||||
|
||||
def get_enabled_users(self) -> List[User]:
|
||||
return [user for user in self.get_all(sort=True) if user.enabled]
|
||||
return self.get_by(query="enabled = 1", sort="username")
|
||||
|
||||
def get_disabled_users(self) -> List[User]:
|
||||
return [user for user in self.get_all(sort=True) if not user.enabled]
|
||||
return self.get_by(query="enabled = 0", sort="username")
|
||||
|
||||
def pre_add(self, user: Dict) -> Dict:
|
||||
return user
|
||||
@ -122,33 +113,33 @@ class UserManager:
|
||||
def pre_update(self, user: Dict) -> Dict:
|
||||
return user
|
||||
|
||||
def pre_delete(self, user_id: str) -> str:
|
||||
def pre_delete(self, user_id: int) -> int:
|
||||
self._on_user_delete(user_id)
|
||||
return user_id
|
||||
|
||||
def post_add(self, user_id: str) -> str:
|
||||
def post_add(self, user_id: int) -> int:
|
||||
self.reload()
|
||||
return user_id
|
||||
|
||||
def post_update(self, user_id: str) -> str:
|
||||
def post_update(self, user_id: int) -> int:
|
||||
self.reload()
|
||||
return user_id
|
||||
|
||||
def post_delete(self, user_id: str) -> str:
|
||||
def post_delete(self, user_id: int) -> int:
|
||||
self.reload()
|
||||
return user_id
|
||||
|
||||
def update_enabled(self, id: str, enabled: bool) -> None:
|
||||
self._db.update_by_id(id, self.pre_update({"enabled": enabled}))
|
||||
def update_enabled(self, id: int, enabled: bool) -> None:
|
||||
self._db.update_by_id(self.TABLE_NAME, id, self.pre_update({"enabled": enabled}))
|
||||
self.post_update(id)
|
||||
|
||||
def update_form(self, id: str, username: str, password: Optional[str]) -> None:
|
||||
def update_form(self, id: int, username: str, password: Optional[str]) -> None:
|
||||
form = {"username": username}
|
||||
|
||||
if password is not None and password:
|
||||
form['password'] = self.encode_password(password)
|
||||
|
||||
self._db.update_by_id(id, self.pre_update(form))
|
||||
self._db.update_by_id(self.TABLE_NAME, id, self.pre_update(form))
|
||||
self.post_update(id)
|
||||
|
||||
def add_form(self, user: Union[User, Dict]) -> None:
|
||||
@ -160,12 +151,12 @@ class UserManager:
|
||||
|
||||
form['password'] = self.encode_password(form['password'])
|
||||
|
||||
self._db.add(self.pre_add(form))
|
||||
self._db.add(self.TABLE_NAME, self.pre_add(form))
|
||||
self.post_add(user.id)
|
||||
|
||||
def delete(self, id: str) -> None:
|
||||
def delete(self, id: int) -> None:
|
||||
self.pre_delete(id)
|
||||
self._db.delete_by_id(id)
|
||||
self._db.delete_by_id(self.TABLE_NAME, id)
|
||||
self.post_delete(id)
|
||||
|
||||
def to_dict(self, users: List[User]) -> List[Dict]:
|
||||
@ -215,18 +206,18 @@ class UserManager:
|
||||
|
||||
return None
|
||||
|
||||
def forget_user(self, objects: List, user_id: str) -> Dict:
|
||||
user_map = self.map()
|
||||
user_id = str(user_id)
|
||||
def forget_user(self, objects: List, user_id: int) -> Dict:
|
||||
user_map = self.prepare_map()
|
||||
user_id = int(user_id)
|
||||
edits = {}
|
||||
|
||||
for object in objects:
|
||||
edits = {object.id: {}}
|
||||
edits[object.id] = {}
|
||||
|
||||
if str(object.created_by) == user_id and user_id in user_map:
|
||||
if int(object.created_by) == user_id and user_id in user_map:
|
||||
edits[object.id]['created_by'] = user_map[user_id].username
|
||||
|
||||
if str(object.updated_by) == user_id and user_id in user_map:
|
||||
if int(object.updated_by) == user_id and user_id in user_map:
|
||||
edits[object.id]['updated_by'] = user_map[user_id].username
|
||||
|
||||
return edits
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import json
|
||||
import time
|
||||
from typing import Dict, Optional, List, Tuple, Union
|
||||
from pysondb.errors import IdDoesNotExistError
|
||||
|
||||
from src.manager.DatabaseManager import DatabaseManager
|
||||
from src.manager.LangManager import LangManager
|
||||
@ -24,17 +24,17 @@ class VariableManager(ModelManager):
|
||||
|
||||
TABLE_NAME = "settings"
|
||||
TABLE_MODEL = [
|
||||
"description",
|
||||
"description_edition",
|
||||
"editable",
|
||||
"name",
|
||||
"section",
|
||||
"plugin",
|
||||
"selectables",
|
||||
"type",
|
||||
"unit",
|
||||
"refresh_player",
|
||||
"value"
|
||||
"description TEXT",
|
||||
"description_edition TEXT",
|
||||
"editable INTEGER",
|
||||
"name CHAR(255)",
|
||||
"section CHAR(255)",
|
||||
"plugin CHAR(255)",
|
||||
"selectables TEXT",
|
||||
"type CHAR(255)",
|
||||
"unit CHAR(255)",
|
||||
"refresh_player INTEGER",
|
||||
"value TEXT"
|
||||
]
|
||||
|
||||
def __init__(self, lang_manager: LangManager, database_manager: DatabaseManager, user_manager: UserManager):
|
||||
@ -75,25 +75,25 @@ class VariableManager(ModelManager):
|
||||
same_selectables_label = get_keys(default_var, 'selectables', 'label') == get_keys(variable, 'selectables', 'label')
|
||||
|
||||
if variable.description != default_var['description']:
|
||||
self._db.update_by_id(variable.id, {"description": default_var['description']})
|
||||
self._db.update_by_id(self.TABLE_NAME, variable.id, {"description": default_var['description']})
|
||||
|
||||
if variable.description_edition != default_var['description_edition']:
|
||||
self._db.update_by_id(variable.id, {"description_edition": default_var['description_edition']})
|
||||
self._db.update_by_id(self.TABLE_NAME, variable.id, {"description_edition": default_var['description_edition']})
|
||||
|
||||
if variable.unit != default_var['unit']:
|
||||
self._db.update_by_id(variable.id, {"unit": default_var['unit']})
|
||||
self._db.update_by_id(self.TABLE_NAME, variable.id, {"unit": default_var['unit']})
|
||||
|
||||
if variable.section != default_var['section']:
|
||||
self._db.update_by_id(variable.id, {"section": default_var['section']})
|
||||
self._db.update_by_id(self.TABLE_NAME, variable.id, {"section": default_var['section']})
|
||||
|
||||
if variable.refresh_player != default_var['refresh_player']:
|
||||
self._db.update_by_id(variable.id, {"refresh_player": default_var['refresh_player']})
|
||||
self._db.update_by_id(self.TABLE_NAME, variable.id, {"refresh_player": default_var['refresh_player']})
|
||||
|
||||
if not same_selectables_keys or not same_selectables_label:
|
||||
self._db.update_by_id(variable.id, {"selectables": default_var['selectables']})
|
||||
self._db.update_by_id(self.TABLE_NAME, variable.id, {"selectables": default_var['selectables']})
|
||||
|
||||
if variable.name == 'last_restart':
|
||||
self._db.update_by_id(variable.id, {"value": time.time()})
|
||||
self._db.update_by_id(self.TABLE_NAME, variable.id, {"value": time.time()})
|
||||
|
||||
return variable
|
||||
|
||||
@ -144,75 +144,60 @@ class VariableManager(ModelManager):
|
||||
|
||||
return var_map
|
||||
|
||||
def hydrate_object(self, raw_variable: dict, id: Optional[str] = None) -> Variable:
|
||||
def hydrate_object(self, raw_variable: dict, id: Optional[int] = None) -> Variable:
|
||||
if id:
|
||||
raw_variable['id'] = id
|
||||
|
||||
if 'selectables' in raw_variable and raw_variable['selectables']:
|
||||
raw_variable['selectables'] = [Selectable(**selectable) for selectable in raw_variable['selectables']]
|
||||
raw_variable['selectables'] = [Selectable(**selectable) for selectable in json.loads(raw_variable['selectables'])]
|
||||
|
||||
return Variable(**raw_variable)
|
||||
|
||||
def hydrate_dict(self, raw_variables: dict) -> List[Variable]:
|
||||
return [self.hydrate_object(raw_variable, raw_id) for raw_id, raw_variable in raw_variables.items()]
|
||||
|
||||
def hydrate_list(self, raw_variables: list) -> List[Variable]:
|
||||
return [self.hydrate_object(raw_variable) for raw_variable in raw_variables]
|
||||
|
||||
def get(self, id: str) -> Optional[Variable]:
|
||||
try:
|
||||
return self.hydrate_object(self._db.get_by_id(id), id)
|
||||
except IdDoesNotExistError:
|
||||
return None
|
||||
def get(self, id: int) -> Optional[Variable]:
|
||||
object = self._db.get_by_id(self.TABLE_NAME, id)
|
||||
return self.hydrate_object(object, id) if object else None
|
||||
|
||||
def get_by(self, query) -> List[Variable]:
|
||||
return self.hydrate_dict(self._db.get_by_query(query=query))
|
||||
def get_by(self, query, sort: Optional[str] = None) -> List[Variable]:
|
||||
return self.hydrate_list(self._db.get_by_query(self.TABLE_NAME, query=query, sort=sort))
|
||||
|
||||
def get_by_prefix(self, prefix: str) -> List[Variable]:
|
||||
return self.hydrate_dict(self._db.get_by_query(query=lambda v: v['name'].startswith(prefix)))
|
||||
return self.get_by(query="name like '{}%'".format(prefix))
|
||||
|
||||
def get_by_plugin(self, plugin: str) -> List[Variable]:
|
||||
return self.hydrate_dict(self._db.get_by_query(query=lambda v: v['plugin'] == plugin))
|
||||
return self.get_by(query="plugin = '{}'".format(plugin))
|
||||
|
||||
def get_one_by_name(self, name: str) -> Optional[Variable]:
|
||||
return self.get_one_by(query=lambda v: v['name'] == name)
|
||||
return self.get_one_by("name = '{}'".format(name))
|
||||
|
||||
def get_one_by(self, query) -> Optional[Variable]:
|
||||
object = self._db.get_by_query(query=query)
|
||||
variables = self.hydrate_dict(object)
|
||||
if len(variables) == 1:
|
||||
return variables[0]
|
||||
elif len(variables) > 1:
|
||||
raise Error("More than one result for query")
|
||||
object = self._db.get_one_by_query(self.TABLE_NAME, query=query)
|
||||
|
||||
if not object:
|
||||
return None
|
||||
|
||||
return self.hydrate_object(object)
|
||||
|
||||
def get_all(self) -> List[Variable]:
|
||||
raw_variables = self._db.get_all()
|
||||
|
||||
if isinstance(raw_variables, dict):
|
||||
return self.hydrate_dict(raw_variables)
|
||||
|
||||
return self.hydrate_list(raw_variables)
|
||||
return self.hydrate_list(self._db.get_all(self.TABLE_NAME))
|
||||
|
||||
def get_editable_variables(self, plugin: bool = True, sort: Optional[str] = None) -> List[Variable]:
|
||||
query = lambda v: (not plugin and not isinstance(v['plugin'], str)) or (plugin and isinstance(v['plugin'], str))
|
||||
variables = [variable for variable in self.get_by(query=query) if variable.editable]
|
||||
if sort is not None and sort:
|
||||
return sorted(variables, key=lambda x: getattr(x, sort))
|
||||
return variables
|
||||
query = "plugin is null and editable = 1" if not plugin else "plugin is not null and length(plugin) > 0 and editable = 1"
|
||||
return self.get_by(query=query, sort=sort)
|
||||
|
||||
def get_readonly_variables(self) -> List[Variable]:
|
||||
return [variable for variable in self.get_all() if not variable.editable]
|
||||
return self.get_by(query="editable = 0", sort="name")
|
||||
|
||||
def update_form(self, id: str, value: Union[int, bool, str]) -> None:
|
||||
var_dict = self._db.update_by_id(id, {"value": value})
|
||||
var = self.hydrate_object(var_dict, id)
|
||||
def update_form(self, id: int, value: Union[int, bool, str]) -> None:
|
||||
self._db.update_by_id(self.TABLE_NAME, id, {"value": value})
|
||||
var = self.get_one_by("id = {}".format(id))
|
||||
self._var_map[var.name] = var
|
||||
|
||||
def update_by_name(self, name: str, value) -> Optional[Variable]:
|
||||
[var_id] = self._db.update_by_query(query=lambda v: v['name'] == name, new_data={"value": value})
|
||||
var_dict = self._db.get_by_id(var_id)
|
||||
var = self.hydrate_object(var_dict, id)
|
||||
self._db.update_by_query(self.TABLE_NAME, query="name = '{}'".format(name), values={"value": value})
|
||||
var = self.get_one_by_name(name)
|
||||
self._var_map[name] = var
|
||||
|
||||
def add_form(self, variable: Union[Variable, Dict]) -> None:
|
||||
@ -222,10 +207,10 @@ class VariableManager(ModelManager):
|
||||
form = variable.to_dict()
|
||||
del form['id']
|
||||
|
||||
self._db.add(form)
|
||||
self._db.add(self.TABLE_NAME, form)
|
||||
|
||||
def delete(self, id: str) -> None:
|
||||
self._db.delete_by_id(id)
|
||||
def delete(self, id: int) -> None:
|
||||
self._db.delete_by_id(self.TABLE_NAME, id)
|
||||
|
||||
def to_dict(self, variables: List[Variable]) -> List[Dict]:
|
||||
return [variable.to_dict() for variable in variables]
|
||||
|
||||
@ -5,7 +5,7 @@ from typing import Optional, Union
|
||||
|
||||
class Screen:
|
||||
|
||||
def __init__(self, host: str = '', port: int = 5000, enabled: bool = False, name: str = 'Untitled', position: int = 999, id: Optional[str] = None):
|
||||
def __init__(self, host: str = '', port: int = 5000, enabled: bool = False, name: str = 'Untitled', position: int = 999, id: Optional[int] = None):
|
||||
self._id = id if id else None
|
||||
self._host = host
|
||||
self._port = port
|
||||
@ -14,7 +14,7 @@ class Screen:
|
||||
self._position = position
|
||||
|
||||
@property
|
||||
def id(self) -> Union[int, str]:
|
||||
def id(self) -> Optional[int]:
|
||||
return self._id
|
||||
|
||||
@property
|
||||
@ -35,11 +35,11 @@ class Screen:
|
||||
|
||||
@property
|
||||
def enabled(self) -> bool:
|
||||
return self._enabled
|
||||
return bool(self._enabled)
|
||||
|
||||
@enabled.setter
|
||||
def enabled(self, value: bool):
|
||||
self._enabled = value
|
||||
self._enabled = bool(value)
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
|
||||
@ -8,7 +8,7 @@ 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: Optional[str] = None, cron_schedule_end: Optional[str] = None, created_by: Optional[str] = None, updated_by: Optional[str] = None, created_at: Optional[int] = None, updated_at: Optional[int] = None):
|
||||
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[int] = None, cron_schedule: Optional[str] = None, cron_schedule_end: Optional[str] = None, created_by: Optional[str] = None, updated_by: Optional[str] = None, created_at: Optional[int] = None, updated_at: Optional[int] = None):
|
||||
self._id = id if id else None
|
||||
self._location = location
|
||||
self._duration = duration
|
||||
@ -24,7 +24,7 @@ class Slide:
|
||||
self._updated_at = int(updated_at if updated_at else time.time())
|
||||
|
||||
@property
|
||||
def id(self) -> Optional[str]:
|
||||
def id(self) -> Optional[int]:
|
||||
return self._id
|
||||
|
||||
@property
|
||||
@ -101,11 +101,11 @@ class Slide:
|
||||
|
||||
@property
|
||||
def enabled(self) -> bool:
|
||||
return self._enabled
|
||||
return bool(self._enabled)
|
||||
|
||||
@enabled.setter
|
||||
def enabled(self, value: bool):
|
||||
self._enabled = value
|
||||
self._enabled = bool(value)
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
|
||||
@ -5,14 +5,14 @@ from typing import Optional, Union
|
||||
|
||||
class User:
|
||||
|
||||
def __init__(self, username: str = '', password: str = '', enabled: bool = True, id: Optional[str] = None):
|
||||
def __init__(self, username: str = '', password: str = '', enabled: bool = True, id: Optional[int] = None):
|
||||
self._id = id if id else None
|
||||
self._username = username
|
||||
self._password = password
|
||||
self._enabled = enabled
|
||||
|
||||
@property
|
||||
def id(self) -> Union[int, str]:
|
||||
def id(self) -> Optional[int]:
|
||||
return self._id
|
||||
|
||||
@property
|
||||
@ -33,11 +33,11 @@ class User:
|
||||
|
||||
@property
|
||||
def enabled(self) -> bool:
|
||||
return self._enabled
|
||||
return bool(self._enabled)
|
||||
|
||||
@enabled.setter
|
||||
def enabled(self, value: bool):
|
||||
self._enabled = value
|
||||
self._enabled = bool(value)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"User(" \
|
||||
|
||||
@ -11,7 +11,7 @@ from src.utils import str_to_enum
|
||||
class Variable:
|
||||
|
||||
def __init__(self, name: str = '', section: str = '', description: str = '', description_edition: str = '', type: Union[VariableType, str] = VariableType.STRING,
|
||||
value: Union[int, bool, str] = '', editable: bool = True, id: Optional[str] = None,
|
||||
value: Union[int, bool, str] = '', editable: bool = True, id: Optional[int] = None,
|
||||
plugin: Optional[str] = None, selectables: Optional[List[Selectable]] = None, unit: Optional[VariableUnit] = None,
|
||||
refresh_player: bool = False):
|
||||
self._id = id if id else None
|
||||
@ -32,7 +32,7 @@ class Variable:
|
||||
self._unit = None
|
||||
|
||||
@property
|
||||
def id(self) -> Union[int, str]:
|
||||
def id(self) -> Optional[int]:
|
||||
return self._id
|
||||
|
||||
@property
|
||||
@ -96,19 +96,19 @@ class Variable:
|
||||
|
||||
@property
|
||||
def editable(self) -> bool:
|
||||
return self._editable
|
||||
return bool(self._editable)
|
||||
|
||||
@editable.setter
|
||||
def editable(self, value: bool):
|
||||
self._editable = value
|
||||
self._editable = bool(value)
|
||||
|
||||
@property
|
||||
def refresh_player(self) -> bool:
|
||||
return self._refresh_player
|
||||
return bool(self._refresh_player)
|
||||
|
||||
@refresh_player.setter
|
||||
def refresh_player(self, value: bool):
|
||||
self._refresh_player = value
|
||||
self._refresh_player = bool(value)
|
||||
|
||||
@property
|
||||
def value(self) -> Union[int, bool, str]:
|
||||
@ -171,10 +171,10 @@ class Variable:
|
||||
return str(self._value)
|
||||
|
||||
def as_int(self) -> int:
|
||||
return int(self._value)
|
||||
return int(float(self._value))
|
||||
|
||||
def as_ctime(self) -> int:
|
||||
return time.ctime(self._value)
|
||||
return time.ctime(int(float(self._value)))
|
||||
|
||||
def display(self) -> Union[int, bool, str]:
|
||||
value = self.eval()
|
||||
|
||||
@ -53,5 +53,5 @@ class ModelStore:
|
||||
def user(self) -> UserManager:
|
||||
return self._user_manager
|
||||
|
||||
def on_user_delete(self, user_id: str):
|
||||
def on_user_delete(self, user_id: int) -> None:
|
||||
self._slide_manager.forget_user(user_id)
|
||||
|
||||
@ -156,6 +156,9 @@ class PluginStore:
|
||||
|
||||
def is_plugin_enabled(self, plugin: ObPlugin) -> bool:
|
||||
var = self._model_store.variable().get_one_by_name(plugin.get_plugin_variable_name(self.DEFAULT_PLUGIN_ENABLED_VARIABLE))
|
||||
if var.as_bool:
|
||||
logging.info("[Plugin] {} enabled".format(plugin.use_title()))
|
||||
|
||||
return var.as_bool() if var else False
|
||||
|
||||
|
||||
|
||||
@ -25,6 +25,7 @@ class TemplateRenderer:
|
||||
def get_view_globals(self) -> dict:
|
||||
globals = dict(
|
||||
STATIC_PREFIX="/{}/{}/".format(WebDirConstant.FOLDER_STATIC, WebDirConstant.FOLDER_STATIC_WEB_ASSETS),
|
||||
SECRET_KEY=self._model_store.config().map().get('secret_key'),
|
||||
FLEET_ENABLED=self._model_store.variable().map().get('fleet_enabled').as_bool(),
|
||||
AUTH_ENABLED=self._model_store.variable().map().get('auth_enabled').as_bool(),
|
||||
track_created=self._model_store.user().track_user_created,
|
||||
|
||||
14
src/utils.py
14
src/utils.py
@ -13,6 +13,20 @@ from cron_descriptor.Exception import FormatException, WrongArgumentException, M
|
||||
CAMEL_CASE_TO_SNAKE_CASE_PATTERN = re.compile(r'(?<!^)(?=[A-Z])')
|
||||
|
||||
|
||||
def is_wrapped_by(s: str, head: str = '', tail: str = '') -> bool:
|
||||
return s[0] == head and s[-1] == tail if len(s) > 0 else None
|
||||
|
||||
|
||||
def wrap_if(s: str, condition: bool = True, quote_type: str = "'") -> str:
|
||||
if not condition or is_wrapped_by(s, quote_type, quote_type):
|
||||
return s
|
||||
return "{}{}{}".format(
|
||||
quote_type,
|
||||
s,
|
||||
quote_type
|
||||
)
|
||||
|
||||
|
||||
def am_i_in_docker():
|
||||
docker_env = os.path.exists('/.dockerenv')
|
||||
docker_cgroup = False
|
||||
|
||||
@ -1 +1 @@
|
||||
1.14
|
||||
1.15
|
||||
|
||||
@ -113,6 +113,7 @@
|
||||
{% endblock %}
|
||||
</div>
|
||||
<script>
|
||||
var secret_key = '{{ SECRET_KEY }}';
|
||||
var l = {
|
||||
'js_slideshow_slide_delete_confirmation': '{{ l.slideshow_slide_delete_confirmation }}',
|
||||
'js_fleet_screen_delete_confirmation': '{{ l.js_fleet_screen_delete_confirmation }}',
|
||||
|
||||
Loading…
Reference in New Issue
Block a user