0
0
mirror of https://gitflic.ru/project/maks1ms/ocab.git synced 2025-04-16 14:33:46 +03:00
This commit is contained in:
Maxim Slipenko 2024-07-21 20:01:50 +03:00
parent d52864a231
commit 34c365178b
4 changed files with 102 additions and 70 deletions

14
poetry.lock generated
View File

@ -2053,18 +2053,18 @@ files = [
[[package]] [[package]]
name = "setuptools" name = "setuptools"
version = "71.0.1" version = "71.0.4"
description = "Easily download, build, install, upgrade, and uninstall Python packages" description = "Easily download, build, install, upgrade, and uninstall Python packages"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
{file = "setuptools-71.0.1-py3-none-any.whl", hash = "sha256:1eb8ef012efae7f6acbc53ec0abde4bc6746c43087fd215ee09e1df48998711f"}, {file = "setuptools-71.0.4-py3-none-any.whl", hash = "sha256:ed2feca703be3bdbd94e6bb17365d91c6935c6b2a8d0bb09b66a2c435ba0b1a5"},
{file = "setuptools-71.0.1.tar.gz", hash = "sha256:c51d7fd29843aa18dad362d4b4ecd917022131425438251f4e3d766c964dd1ad"}, {file = "setuptools-71.0.4.tar.gz", hash = "sha256:48297e5d393a62b7cb2a10b8f76c63a73af933bd809c9e0d0d6352a1a0135dd8"},
] ]
[package.extras] [package.extras]
core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.text (>=3.7)", "more-itertools (>=8.8)", "ordered-set (>=3.1.1)", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.text (>=3.7)", "more-itertools (>=8.8)", "ordered-set (>=3.1.1)", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"]
doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (<7.4)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.10.0)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (<0.4)", "pytest-ruff (>=0.2.1)", "pytest-ruff (>=0.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.10.0)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (<0.4)", "pytest-ruff (>=0.2.1)", "pytest-ruff (>=0.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
[[package]] [[package]]
@ -2230,13 +2230,13 @@ zstd = ["zstandard (>=0.18.0)"]
[[package]] [[package]]
name = "uvicorn" name = "uvicorn"
version = "0.30.1" version = "0.30.3"
description = "The lightning-fast ASGI server." description = "The lightning-fast ASGI server."
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
{file = "uvicorn-0.30.1-py3-none-any.whl", hash = "sha256:cd17daa7f3b9d7a24de3617820e634d0933b69eed8e33a516071174427238c81"}, {file = "uvicorn-0.30.3-py3-none-any.whl", hash = "sha256:94a3608da0e530cea8f69683aa4126364ac18e3826b6630d1a65f4638aade503"},
{file = "uvicorn-0.30.1.tar.gz", hash = "sha256:d46cd8e0fd80240baffbcd9ec1012a712938754afcf81bce56c024c1656aece8"}, {file = "uvicorn-0.30.3.tar.gz", hash = "sha256:0d114d0831ff1adbf231d358cbf42f17333413042552a624ea6a9b4c33dcfd81"},
] ]
[package.dependencies] [package.dependencies]

View File

@ -1,3 +1,4 @@
import time
from typing import Any, Dict, List, Optional, Union from typing import Any, Dict, List, Optional, Union
import yaml import yaml
@ -6,7 +7,7 @@ import yaml
try: try:
import dash_bootstrap_components as dbc import dash_bootstrap_components as dbc
from dash_extensions.enrich import Input, Output, dcc, html from dash_extensions.enrich import ALL, Input, Output, State, dcc, html
DASH_AVAILABLE = True DASH_AVAILABLE = True
except ImportError: except ImportError:
@ -102,9 +103,10 @@ class ConfigManager:
self.set_nested_setting(self._config, key, value) self.set_nested_setting(self._config, key, value)
self.save_config() self.save_config()
def get_settings_layout(self): def get_settings_layout(self, prefix):
from dash_extensions.enrich import DashBlueprint from dash_extensions.enrich import DashBlueprint
self._prefix = prefix
bp = DashBlueprint() bp = DashBlueprint()
def create_layout(): def create_layout():
@ -138,22 +140,28 @@ class ConfigManager:
if not meta.get("is_private"): if not meta.get("is_private"):
row = [] row = []
label_text = meta.get("pretty_name", key) label_text = meta.get("pretty_name", key)
if not label_text:
label_text = key
if meta.get("type") != "checkbox": if meta.get("type") != "checkbox":
row.append(dbc.Label(label_text)) row.append(dbc.Label(label_text))
component_id = { component_id = {
"type": "setting", "type": "setting",
"key": "-".join(current_key_list), "key": "::".join(current_key_list),
} }
if meta.get("type") == "string": if meta.get("type") == "string":
component = dbc.Input( component = dbc.Input(
id=component_id, type="text", value=value id=component_id,
type="text",
value=value,
) )
elif meta.get("type") == "number": elif meta.get("type") == "number":
component = dbc.Input( component = dbc.Input(
id=component_id, type="number", value=value id=component_id,
type="number",
value=value,
) )
elif meta.get("type") == "checkbox": elif meta.get("type") == "checkbox":
component = dbc.Col( component = dbc.Col(
@ -161,11 +169,7 @@ class ConfigManager:
dbc.Checkbox( dbc.Checkbox(
id=component_id, id=component_id,
value=value, value=value,
label=dbc.Label( label=label_text,
label_text,
style={"margin-right": "10px"},
check=True,
),
) )
] ]
) )
@ -175,7 +179,9 @@ class ConfigManager:
for opt in meta.get("options", []) for opt in meta.get("options", [])
] ]
component = dcc.Dropdown( component = dcc.Dropdown(
id=component_id, options=options, value=value id=component_id,
options=options,
value=value,
) )
else: else:
continue continue
@ -195,17 +201,15 @@ class ConfigManager:
[ [
html.H1("Настройки"), html.H1("Настройки"),
dbc.Form(settings_components), dbc.Form(settings_components),
html.Div(id="save-confirmation"),
dbc.Button( dbc.Button(
"Сохранить", "Сохранить",
id="save-settings", id="save-settings",
color="primary", color="primary",
className="mt-3", className="mt-3 w-100",
n_clicks=0, n_clicks=0,
), ),
html.Div(id="settings-update-trigger", style={"display": "none"}), html.Div(id="settings-update-trigger", style={"display": "none"}),
html.Span(
id="save-confirmation", style={"verticalAlign": "middle"}
),
dcc.Store(id="settings-store"), dcc.Store(id="settings-store"),
], ],
style={ style={
@ -221,39 +225,76 @@ class ConfigManager:
return bp return bp
def setup_callbacks(self, app): def setup_callbacks(self, app):
# ws = WebSocket(app, url="/ws")
@app.callback( @app.callback(
Output("save-confirmation", "children"), Output("save-confirmation", "children", allow_duplicate=True),
# Output("settings-store", "data"), Output("settings-store", "data"),
Input("save-settings", "n_clicks"), Input("save-settings", "n_clicks"),
# State({"type": "setting", "key": ALL}, "value"), State({"type": "setting", "key": ALL}, "value"),
# State({"type": "setting", "key": ALL}, "id"), State({"type": "setting", "key": ALL}, "id"),
running=[(Output("save-settings", "disabled"), True, False)], prevent_initial_call=True,
allow_duplicate=True,
# https://github.com/emilhe/dash-extensions/issues/344
# running=[
# (
# Output({
# id: "save-settings",
# 'n_clicks': MATCH
# }, "disabled"), True, False
# )
# ]
) )
def save_settings(n_clicks, values, ids): def save_settings(n_clicks, values, keys):
time.sleep(3)
if n_clicks > 0: if n_clicks > 0:
# updated_settings = {} updated_settings = {}
# print(ids)
# for value, id_dict in zip(values, ids):
# key = id_dict["key"]
# self.update_setting(key.split("-"), value)
# updated_settings[key] = value
return "Настройки сохранены!" # , json.dumps(updated_settings) for value, id_dict in zip(values, keys):
return "" # , None key: str = id_dict["key"]
if self._prefix:
key = key.removeprefix(f"{self._prefix}-")
# @app.callback( self.update_setting(key.split("::"), value)
# Output({"type": "setting", "key": ALL}, "value"), updated_settings[key] = value
# Input("settings-store", "data"),
# ) import datetime
# def update_settings_from_store(data): import locale
# if data:
# updated_settings = json.loads(data) locale.setlocale(locale.LC_TIME, "ru_RU.UTF-8")
# print( now = datetime.datetime.now()
# [current_value for key, current_value in updated_settings.items()]
# ) date_str = now.strftime("%H:%M:%S")
# return [
# current_value for key, current_value in updated_settings.items() return (
# ] dbc.Alert(
# raise dash.exceptions.PreventUpdate() f"Настройки сохранены в {date_str}",
color="success",
duration=10000,
),
date_str,
)
app.clientside_callback(
"""
function(n_clicks) {
const buttonSelector = '#%s-save-settings';
if (n_clicks > 0) {
document.querySelector(buttonSelector).disabled = true;
}
}
"""
% (self._prefix),
Input("save-settings", "n_clicks"),
)
app.clientside_callback(
"""
function(data) {
const buttonSelector = '#%s-save-settings';
if (data) {
document.querySelector(buttonSelector).disabled = false;
}
}
"""
% (self._prefix),
Input("settings-store", "data"),
)

View File

@ -6,21 +6,11 @@ from .config import config
def register_settings_page(): def register_settings_page():
try: try:
register_page = get_module("standard.miniapp", "register_page") register_page = get_module("standard.miniapp", "register_page")
#
# def setup_callbacks_wrapper(config_manager):
# def setup(app):
# config_manager.setup_callbacks(app)
#
# return setup
#
# register_page(
# name="Настройки",
# path="/settings",
# layout=config.get_settings_layout(),
# setup_callbacks=setup_callbacks_wrapper(config),
# )
register_page( register_page(
name="Настройки", path="/settings", blueprint=config.get_settings_layout() name="Настройки",
path="/settings",
blueprint=config.get_settings_layout("settings"),
prefix="settings",
) )
pass pass

View File

@ -9,10 +9,11 @@ from flask import Flask
pages = OrderedDict() pages = OrderedDict()
def register_page(name, path, blueprint): def register_page(name, path, blueprint, prefix=""):
pages[path] = { pages[path] = {
"name": name, "name": name,
"blueprint": blueprint, "blueprint": blueprint,
"prefix": prefix,
} }
@ -53,7 +54,7 @@ def create_dash_app(requests_pathname_prefix: str = None) -> dash.Dash:
# Register pages # Register pages
for path, page in pages.items(): for path, page in pages.items():
# dash.register_page(page["name"], path=path, layout=page["layout"]) # dash.register_page(page["name"], path=path, layout=page["layout"])
page["blueprint"].register(app, path, prefix="a") page["blueprint"].register(app, path, prefix=page["prefix"])
# Create sidebar # Create sidebar
sidebar = dbc.Offcanvas( sidebar = dbc.Offcanvas(
@ -97,7 +98,7 @@ def create_dash_app(requests_pathname_prefix: str = None) -> dash.Dash:
app.layout = html.Div( app.layout = html.Div(
[ [
dcc.Location(id="url", refresh=False), dcc.Location(id="url", refresh=False),
dcc.Store(id="user-data", storage_type="session"), # dcc.Store(id="user-data", storage_type="session"),
dcc.Interval( dcc.Interval(
id="init-telegram-interval", id="init-telegram-interval",
interval=100, interval=100,
@ -154,7 +155,7 @@ def create_dash_app(requests_pathname_prefix: str = None) -> dash.Dash:
Input("open-offcanvas", "n_clicks"), Input("open-offcanvas", "n_clicks"),
) )
# Закрываем offcanvas при клике на ссылку в меню # # Закрываем offcanvas при клике на ссылку в меню
app.clientside_callback( app.clientside_callback(
""" """
function(n_clicks) { function(n_clicks) {