mirror of
https://github.com/checktheroads/hyperglass
synced 2024-05-11 05:55:08 +00:00
167 lines
5.2 KiB
Python
167 lines
5.2 KiB
Python
"""Renders Jinja2 & Sass templates for use by the front end application."""
|
|
|
|
# Standard Library Imports
|
|
import asyncio
|
|
import json
|
|
import time
|
|
from pathlib import Path
|
|
|
|
# Third Party Imports
|
|
import jinja2
|
|
from aiofile import AIOFile
|
|
|
|
# Project Imports
|
|
from hyperglass.configuration import frontend_devices
|
|
from hyperglass.configuration import frontend_networks
|
|
from hyperglass.configuration import frontend_params
|
|
from hyperglass.configuration import params
|
|
from hyperglass.exceptions import HyperglassError
|
|
from hyperglass.util import log
|
|
|
|
# File & Path Definitions
|
|
CWD = Path(__file__).parent
|
|
ROOT_DIR = CWD.parent
|
|
FRONTEND_CONFIG = ROOT_DIR / "static/src/js/frontend.json"
|
|
FONT_DIR = ROOT_DIR / "static/src/sass/fonts"
|
|
FONT_CMD = ROOT_DIR / "static/src/node_modules/get-google-fonts/cli.js"
|
|
THEME_FILE = ROOT_DIR / "static/src/sass/theme.sass"
|
|
STATIC_DIR = ROOT_DIR / "static/src"
|
|
|
|
# Jinja2 Config
|
|
JINJA_FILE_LOADER = jinja2.FileSystemLoader(str(CWD))
|
|
JINJA_ENV = jinja2.Environment(
|
|
loader=JINJA_FILE_LOADER,
|
|
autoescape=True,
|
|
extensions=["jinja2.ext.autoescape"],
|
|
enable_async=True,
|
|
)
|
|
|
|
|
|
async def _render_frontend_config():
|
|
"""Render user config to JSON for use by frontend."""
|
|
log.debug("Rendering front end config...")
|
|
|
|
try:
|
|
async with AIOFile(FRONTEND_CONFIG, "w+") as file:
|
|
await file.write(
|
|
json.dumps(
|
|
{
|
|
"config": frontend_params,
|
|
"networks": frontend_networks,
|
|
"devices": frontend_devices,
|
|
}
|
|
)
|
|
)
|
|
await file.fsync()
|
|
log.debug("Rendered front end config")
|
|
except Exception as e:
|
|
raise HyperglassError(f"Error rendering front end config: {str(e)}")
|
|
|
|
|
|
async def _get_fonts():
|
|
"""Download Google fonts."""
|
|
log.debug("Downloading theme fonts...")
|
|
|
|
font_base = "https://fonts.googleapis.com/css?family={p}|{m}&display=swap"
|
|
font_primary = "+".join(params.branding.font.primary.name.split(" ")).strip()
|
|
font_mono = "+".join(params.branding.font.mono.name.split(" ")).strip()
|
|
font_url = font_base.format(p=font_primary + ":300,400,700", m=font_mono + ":400")
|
|
|
|
font_command = f"node {str(FONT_CMD)} -w -i '{font_url}' -o {str(FONT_DIR)}"
|
|
|
|
try:
|
|
proc = await asyncio.create_subprocess_shell(
|
|
cmd=font_command,
|
|
stdout=asyncio.subprocess.PIPE,
|
|
stderr=asyncio.subprocess.PIPE,
|
|
cwd=STATIC_DIR,
|
|
)
|
|
stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=60)
|
|
|
|
for line in stdout.decode().strip().split("\n"):
|
|
log.debug(line)
|
|
|
|
if proc.returncode != 0:
|
|
output_error = stderr.decode("utf-8")
|
|
log.error(output_error)
|
|
raise RuntimeError(f"Error downloading font from URL {font_url}")
|
|
|
|
await proc.wait()
|
|
log.debug("Downloaded theme fonts")
|
|
|
|
except Exception as e:
|
|
raise HyperglassError(str(e))
|
|
|
|
|
|
async def _render_theme():
|
|
"""Render Jinja2 template to Sass file."""
|
|
log.debug("Rendering theme elements...")
|
|
try:
|
|
template = JINJA_ENV.get_template("templates/theme.sass.j2")
|
|
rendered_theme = await template.render_async(params.branding)
|
|
|
|
log.debug(f"Branding variables:\n{params.branding.json(indent=4)}")
|
|
log.debug(f"Rendered theme:\n{str(rendered_theme)}")
|
|
|
|
async with AIOFile(THEME_FILE, "w+") as file:
|
|
await file.write(rendered_theme)
|
|
await file.fsync()
|
|
log.debug("Rendered theme elements")
|
|
except Exception as e:
|
|
raise HyperglassError(f"Error rendering theme: {e}")
|
|
|
|
|
|
async def _build_assets():
|
|
"""Build, bundle, and minify Sass/CSS/JS web assets."""
|
|
log.debug("Building web assets...")
|
|
|
|
yarn_command = "yarn --silent --emoji false --json --no-progress build"
|
|
try:
|
|
proc = await asyncio.create_subprocess_shell(
|
|
cmd=yarn_command,
|
|
stdout=asyncio.subprocess.PIPE,
|
|
stderr=asyncio.subprocess.PIPE,
|
|
cwd=STATIC_DIR,
|
|
)
|
|
|
|
stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=60)
|
|
output_out = json.loads(stdout.decode("utf-8").split("\n")[0])
|
|
|
|
if proc.returncode != 0:
|
|
output_error = json.loads(stderr.decode("utf-8").strip("\n"))
|
|
raise RuntimeError(
|
|
f'Error building web assets with script {output_out["data"]}:'
|
|
f'{output_error["data"]}'
|
|
)
|
|
|
|
await proc.wait()
|
|
log.debug(f'Built web assets with script {output_out["data"]}')
|
|
except Exception as e:
|
|
raise HyperglassError(str(e))
|
|
|
|
|
|
async def _render_all():
|
|
"""Run all asset rendering/building functions.
|
|
|
|
Raises:
|
|
HyperglassError: Raised if any downstream errors occur.
|
|
"""
|
|
try:
|
|
await _render_frontend_config()
|
|
await _get_fonts()
|
|
await _render_theme()
|
|
await _build_assets()
|
|
except HyperglassError as e:
|
|
raise HyperglassError(str(e)) from None
|
|
|
|
|
|
def render_assets():
|
|
"""Render assets."""
|
|
start = time.time()
|
|
|
|
asyncio.run(_render_all())
|
|
|
|
end = time.time()
|
|
elapsed = round(end - start, 2)
|
|
log.debug(f"Rendered assets in {elapsed} seconds.")
|