izzylib/IzzyLib/template.py

184 lines
4.1 KiB
Python

'''functions for web template management and rendering'''
import codecs, traceback, os, json, aiohttp
from os import listdir, makedirs
from os.path import isfile, isdir, getmtime, abspath
from jinja2 import Environment, FileSystemLoader, ChoiceLoader
from hamlpy.hamlpy import Compiler
from markdown import markdown
from . import logging
from .color import *
env = None
global_variables = {
'markdown': markdown,
'lighten': lighten,
'darken': darken,
'saturate': saturate,
'desaturate': desaturate,
'rgba': rgba
}
search_path = list()
build_path_pairs = dict()
def addSearchPath(path):
tplPath = abspath(path)
if tplPath not in search_path:
search_path.append(tplPath)
def delSearchPath(path):
tplPath = abspath(path)
if tplPath in search_path:
search_path.remove(tplPath)
def addBuildPath(name, source, destination):
src = abspath(source)
dest = abspath(destination)
if not isdir(src):
raise FileNotFoundError('Source path doesn\'t exist: {src}')
build_path_pairs.update({
name: {
'source': src,
'destination': dest
}
})
addSearchPath(dest)
def delBuildPath(name):
if not build_path_pairs.get(name):
raise ValueError(f'"{name}" not in build paths')
del build_path_pairs[src]
def getBuildPath(name=None):
paths = list()
template = build_path_pairs.get(name)
if name:
if template:
paths.append((template['source'], template['destination']))
else:
raise ValueError(f'"{name}" not in build paths')
else:
for k, tpl in build_path_pairs.items():
paths.append((tpl['source'], tpl['destination']))
return paths
def addEnv(data):
if not isinstance(data, dict):
raise TypeError(f'environment data is not a dict')
global_variables.update(data)
def delEnv(var):
if not global_variables.get(var):
raise ValueError(f'"{var}" not in global variables')
del global_variables[var]
def setup():
global env
env = Environment(
loader=ChoiceLoader([FileSystemLoader(path) for path in search_path])
)
def renderTemplate(tplfile, context, request=None, headers=dict(), cookies=dict(), **kwargs):
if not isinstance(context, dict):
raise TypeError(f'context for {tplfile} not a dict')
data = global_variables.copy()
data['request'] = request if request else {'headers': headers, 'cookies': cookies}
data.update(context)
return env.get_template(tplfile).render(data)
def aiohttpTemplate(*args, **kwargs):
ctype = kwargs.get('contentType', 'text/html')
status = kwargs.get('status', 200)
html = renderTemplate(*args, **kwargs)
return aiohttp.web.Response(body=html, status=status, content_type=ctype)
def buildTemplates(name=None):
paths = getBuildPath(name)
for tplPaths in paths:
src = tplPaths['source']
dest = tplPaths['destination']
timefile = f'{dest}/times.json'
updated = False
if not isdir(f'{dest}'):
makedirs(f'{dest}')
if isfile(timefile):
try:
times = json.load(open(timefile))
except:
times = {}
else:
times = {}
for filename in listdir(f'{src}/templates'):
fullPath = f'{src}/{filename}'
modtime = getmtime(fullPath)
base, ext = filename.split('.')
if ext != 'haml':
pass
elif base not in times or times.get(base) != modtime:
updated = True
logging.verbose(f"Template '{filename}' was changed. Building...")
try:
destination = f'{dest}/{base}.html'
haml_lines = codecs.open(fullPath, 'r', encoding='utf-8').read().splitlines()
compiler = Compiler()
output = compiler.process_lines(haml_lines)
outfile = codecs.open(destination, 'w', encoding='utf-8')
outfile.write(output)
logging.info(f"Template '{filename}' has been built")
except Exception as e:
'''I'm actually not sure what sort of errors can happen here, so generic catch-all for now'''
traceback.print_exc()
logging.error(f'Failed to build {filename}: {e}')
times[base] = modtime
if updated:
with open(timefile, 'w') as filename:
filename.write(json.dumps(times))
__all__ = ['addSearchPath', 'delSearchPath', 'addBuildPath', 'delSearchPath', 'addEnv', 'delEnv', 'setup', 'renderTemplate', 'aiohttp', 'buildTemplates']