build a web module for the dashboard
- load all python module from local dir - initialize Blueprint and Babel
This commit is contained in:
@@ -5,8 +5,6 @@ import os
|
|||||||
import argparse
|
import argparse
|
||||||
from asyncio import StreamReader, StreamWriter
|
from asyncio import StreamReader, StreamWriter
|
||||||
from quart import Quart, Response
|
from quart import Quart, Response
|
||||||
from quart_babel import Babel
|
|
||||||
from quart_babel.locale import get_locale
|
|
||||||
from logging import config # noqa F401
|
from logging import config # noqa F401
|
||||||
from proxy import Proxy
|
from proxy import Proxy
|
||||||
from inverter_ifc import InverterIfc
|
from inverter_ifc import InverterIfc
|
||||||
@@ -17,8 +15,7 @@ from cnf.config import Config
|
|||||||
from cnf.config_read_env import ConfigReadEnv
|
from cnf.config_read_env import ConfigReadEnv
|
||||||
from cnf.config_read_toml import ConfigReadToml
|
from cnf.config_read_toml import ConfigReadToml
|
||||||
from cnf.config_read_json import ConfigReadJson
|
from cnf.config_read_json import ConfigReadJson
|
||||||
from web.routes import web_routes
|
from web import Web
|
||||||
from web.i18n import i18n_routes, my_get_locale, LANGUAGES
|
|
||||||
from modbus_tcp import ModbusTcp
|
from modbus_tcp import ModbusTcp
|
||||||
|
|
||||||
|
|
||||||
@@ -34,27 +31,11 @@ class ProxyState:
|
|||||||
ProxyState._is_up = value
|
ProxyState._is_up = value
|
||||||
|
|
||||||
|
|
||||||
def my_get_tz():
|
|
||||||
return 'CET'
|
|
||||||
|
|
||||||
|
|
||||||
app = Quart(__name__,
|
app = Quart(__name__,
|
||||||
template_folder='web/templates',
|
template_folder='web/templates',
|
||||||
static_folder='web/static')
|
static_folder='web/static')
|
||||||
babel = Babel(app,
|
Web(app, '../translations')
|
||||||
locale_selector=my_get_locale,
|
app.secret_key = 'super secret key' # fixme define a secret
|
||||||
timezone_selector=my_get_tz,
|
|
||||||
default_translation_directories='../translations')
|
|
||||||
app.register_blueprint(web_routes)
|
|
||||||
app.register_blueprint(i18n_routes)
|
|
||||||
app.secret_key = 'super secret key'
|
|
||||||
|
|
||||||
|
|
||||||
@app.context_processor
|
|
||||||
def utility_processor():
|
|
||||||
return dict(lang=get_locale(),
|
|
||||||
lang_str=LANGUAGES.get(str(get_locale()), "English"),
|
|
||||||
languages=LANGUAGES)
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/-/ready')
|
@app.route('/-/ready')
|
||||||
|
|||||||
25
app/src/utils/__init__.py
Normal file
25
app/src/utils/__init__.py
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import mimetypes
|
||||||
|
from importlib import import_module
|
||||||
|
from pathlib import Path
|
||||||
|
from collections.abc import Callable
|
||||||
|
|
||||||
|
|
||||||
|
class SourceFileLoader:
|
||||||
|
""" Represents a SouceFileLoader (__loader__)"""
|
||||||
|
name: str
|
||||||
|
get_resource_reader: Callable
|
||||||
|
|
||||||
|
|
||||||
|
def load_modules(loader: SourceFileLoader):
|
||||||
|
"""Load the entire modules from a SourceFileLoader (__loader__)"""
|
||||||
|
pkg = loader.name
|
||||||
|
for load in loader.get_resource_reader().contents():
|
||||||
|
|
||||||
|
if "python" not in str(mimetypes.guess_type(load)[0]):
|
||||||
|
continue
|
||||||
|
|
||||||
|
mod = Path(load).stem
|
||||||
|
if mod == "__init__":
|
||||||
|
continue
|
||||||
|
|
||||||
|
import_module(pkg + "." + mod, pkg)
|
||||||
28
app/src/web/__init__.py
Normal file
28
app/src/web/__init__.py
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
'''Quart blueprint for the proxy webserver with the dashboard
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
app = Quart(__name__, ...)
|
||||||
|
Web(app)
|
||||||
|
'''
|
||||||
|
from quart import Quart, Blueprint
|
||||||
|
from quart_babel import Babel
|
||||||
|
from utils import load_modules
|
||||||
|
|
||||||
|
web = Blueprint('web', __name__)
|
||||||
|
|
||||||
|
load_modules(__loader__)
|
||||||
|
|
||||||
|
|
||||||
|
class Web:
|
||||||
|
'''Helper Class to register the Blueprint at Quart and
|
||||||
|
initializing Babel'''
|
||||||
|
def __init__(self, app: Quart, translation_directories: str | list[str]):
|
||||||
|
app.register_blueprint(web)
|
||||||
|
|
||||||
|
from .i18n import get_locale, get_tz
|
||||||
|
global babel
|
||||||
|
babel = Babel(
|
||||||
|
app,
|
||||||
|
locale_selector=get_locale,
|
||||||
|
timezone_selector=get_tz,
|
||||||
|
default_translation_directories=translation_directories)
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
from quart import Blueprint
|
|
||||||
from quart import request, session, redirect
|
from quart import request, session, redirect
|
||||||
from quart_babel import _
|
from quart_babel import _
|
||||||
|
from quart_babel.locale import get_locale as babel_get_locale
|
||||||
|
|
||||||
|
from . import web
|
||||||
|
|
||||||
LANGUAGES = {
|
LANGUAGES = {
|
||||||
'en': _('English'),
|
'en': _('English'),
|
||||||
@@ -9,10 +10,8 @@ LANGUAGES = {
|
|||||||
'fr': _('French')
|
'fr': _('French')
|
||||||
}
|
}
|
||||||
|
|
||||||
i18n_routes = Blueprint('i18n_routes', __name__)
|
|
||||||
|
|
||||||
|
def get_locale():
|
||||||
def my_get_locale():
|
|
||||||
try:
|
try:
|
||||||
language = session['language']
|
language = session['language']
|
||||||
except KeyError:
|
except KeyError:
|
||||||
@@ -25,7 +24,18 @@ def my_get_locale():
|
|||||||
return request.accept_languages.best_match(LANGUAGES.keys())
|
return request.accept_languages.best_match(LANGUAGES.keys())
|
||||||
|
|
||||||
|
|
||||||
@i18n_routes.route('/language/<language>')
|
def get_tz():
|
||||||
|
return 'CET'
|
||||||
|
|
||||||
|
|
||||||
|
@web.context_processor
|
||||||
|
def utility_processor():
|
||||||
|
return dict(lang=babel_get_locale(),
|
||||||
|
lang_str=LANGUAGES.get(str(babel_get_locale()), "English"),
|
||||||
|
languages=LANGUAGES)
|
||||||
|
|
||||||
|
|
||||||
|
@web.route('/language/<language>')
|
||||||
def set_language(language=None):
|
def set_language(language=None):
|
||||||
if language in LANGUAGES:
|
if language in LANGUAGES:
|
||||||
session['language'] = language
|
session['language'] = language
|
||||||
|
|||||||
@@ -1,34 +1,32 @@
|
|||||||
from quart import Blueprint
|
|
||||||
from quart import render_template, url_for
|
from quart import render_template, url_for
|
||||||
from quart import send_from_directory
|
from quart import send_from_directory
|
||||||
from quart_babel import format_datetime
|
from quart_babel import format_datetime
|
||||||
from infos import Infos
|
from infos import Infos
|
||||||
from web.conn_table import get_table_data
|
from web.conn_table import get_table_data
|
||||||
|
from . import web
|
||||||
import os
|
import os
|
||||||
|
|
||||||
web_routes = Blueprint('web_routes', __name__)
|
|
||||||
|
|
||||||
|
|
||||||
async def get_icon(file: str, mime: str = 'image/png'):
|
async def get_icon(file: str, mime: str = 'image/png'):
|
||||||
return await send_from_directory(
|
return await send_from_directory(
|
||||||
os.path.join(web_routes.root_path, 'static/images'),
|
os.path.join(web.root_path, 'static/images'),
|
||||||
file,
|
file,
|
||||||
mimetype=mime)
|
mimetype=mime)
|
||||||
|
|
||||||
|
|
||||||
@web_routes.route('/')
|
@web.route('/')
|
||||||
async def index():
|
async def index():
|
||||||
return await render_template(
|
return await render_template(
|
||||||
'index.html.j2',
|
'index.html.j2',
|
||||||
fetch_url='.'+url_for('web_routes.data_fetch'))
|
fetch_url='.'+url_for('web.data_fetch'))
|
||||||
|
|
||||||
|
|
||||||
@web_routes.route('/page')
|
@web.route('/page')
|
||||||
async def empty():
|
async def empty():
|
||||||
return await render_template('empty.html.j2')
|
return await render_template('empty.html.j2')
|
||||||
|
|
||||||
|
|
||||||
@web_routes.route('/data-fetch')
|
@web.route('/data-fetch')
|
||||||
async def data_fetch():
|
async def data_fetch():
|
||||||
data = {
|
data = {
|
||||||
"update-time": format_datetime(format="medium"),
|
"update-time": format_datetime(format="medium"),
|
||||||
@@ -44,26 +42,26 @@ async def data_fetch():
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
@web_routes.route('/favicon-96x96.png')
|
@web.route('/favicon-96x96.png')
|
||||||
async def favicon():
|
async def favicon():
|
||||||
return await get_icon('favicon-96x96.png')
|
return await get_icon('favicon-96x96.png')
|
||||||
|
|
||||||
|
|
||||||
@web_routes.route('/favicon.ico')
|
@web.route('/favicon.ico')
|
||||||
async def favicon_ico():
|
async def favicon_ico():
|
||||||
return await get_icon('favicon.ico', 'image/x-icon')
|
return await get_icon('favicon.ico', 'image/x-icon')
|
||||||
|
|
||||||
|
|
||||||
@web_routes.route('/favicon.svg')
|
@web.route('/favicon.svg')
|
||||||
async def favicon_svg():
|
async def favicon_svg():
|
||||||
return await get_icon('favicon.svg', 'image/svg+xml')
|
return await get_icon('favicon.svg', 'image/svg+xml')
|
||||||
|
|
||||||
|
|
||||||
@web_routes.route('/apple-touch-icon.png')
|
@web.route('/apple-touch-icon.png')
|
||||||
async def apple_touch():
|
async def apple_touch():
|
||||||
return await get_icon('apple-touch-icon.png')
|
return await get_icon('apple-touch-icon.png')
|
||||||
|
|
||||||
|
|
||||||
@web_routes.route('/site.webmanifest')
|
@web.route('/site.webmanifest')
|
||||||
async def webmanifest():
|
async def webmanifest():
|
||||||
return await get_icon('site.webmanifest', 'application/manifest+json')
|
return await get_icon('site.webmanifest', 'application/manifest+json')
|
||||||
|
|||||||
@@ -55,9 +55,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="w3-bar-block">
|
<div class="w3-bar-block">
|
||||||
<button href="#" class="w3-bar-item w3-button w3-padding-16 w3-hide-large w3-dark-grey w3-hover-black" onclick="w3_close()" title="close menu"><i class="fa fa-remove fa-fw"></i> Close Menu</button>
|
<button href="#" class="w3-bar-item w3-button w3-padding-16 w3-hide-large w3-dark-grey w3-hover-black" onclick="w3_close()" title="close menu"><i class="fa fa-remove fa-fw"></i> Close Menu</button>
|
||||||
<a href=".{{ url_for('web_routes.index')}}" class="w3-bar-item w3-button w3-padding {% block menu1_class %}{% endblock %}"><i class="fa fa-network-wired fa-fw"></i> {{_('Connections')}}</a>
|
<a href=".{{ url_for('web.index')}}" class="w3-bar-item w3-button w3-padding {% block menu1_class %}{% endblock %}"><i class="fa fa-network-wired fa-fw"></i> {{_('Connections')}}</a>
|
||||||
<a href=".{{ url_for('web_routes.empty')}}" class="w3-bar-item w3-button w3-padding {% block menu2_class %}{% endblock %}"><i class="fa fa-database fa-fw"></i> MQTT</a>
|
<a href=".{{ url_for('web.empty')}}" class="w3-bar-item w3-button w3-padding {% block menu2_class %}{% endblock %}"><i class="fa fa-database fa-fw"></i> MQTT</a>
|
||||||
<a href=".{{ url_for('web_routes.empty')}}" class="w3-bar-item w3-button w3-padding"><i class="fa fa-file-export fa-fw {% block menu3_class %}{% endblock %}"></i> Downloads</a>
|
<a href=".{{ url_for('web.empty')}}" class="w3-bar-item w3-button w3-padding"><i class="fa fa-file-export fa-fw {% block menu3_class %}{% endblock %}"></i> Downloads</a>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user