Blame template.py

3c1a85
3c1a85
import os
3c1a85
import html
3c1a85
import markdown
3c1a85
os.path.dirname(os.path.abspath(__file__))
3c1a85
3c1a85
3c1a85
def dictGet(d, path):
3c1a85
    for k in str(path).split('.'):
3c1a85
        if type(d) is list or type(d) is tuple:
3c1a85
            d = d[int(k)]
3c1a85
            continue
3c1a85
        elif type(d) is dict:
3c1a85
            if (not k in d) and ('ex' in d):
3c1a85
                print("query ex for field: %s (%s)" % (path, k))
3c1a85
                d = d['ex'](d)
3c1a85
            if k in d:
3c1a85
                d = d[k]
3c1a85
                continue
3c1a85
        print("variable not found: %s (%s)" % (path, k))
3c1a85
        return None
3c1a85
    return d
3c1a85
3c1a85
def tostr(s):
3c1a85
    return '' if s is None else str(s) 
3c1a85
def text2html(t):
3c1a85
    return html.escape(tostr(t))
3c1a85
def md2html(t):
3c1a85
    return markdown.markdown(tostr(t))
3c1a85
3c1a85
3c1a85
tabsym = ' -- '
3c1a85
3c1a85
3c1a85
class TemplateText:
3c1a85
    def __init__(self, text = ''):
3c1a85
        self.text = text
3c1a85
    def write(self, writer, context):
3c1a85
        writer.write(self.text)
3c1a85
    def optimize(self):
3c1a85
        return self if self.text else None
3c1a85
    def dbgPrint(self, tab):
3c1a85
        print("%stext(%s)" % (tab, self.text.replace("\n", "\\n")))
3c1a85
3c1a85
class TemplateVarHtml:
3c1a85
    def __init__(self, varpath = None):
3c1a85
        self.varpath = varpath
3c1a85
    def write(self, writer, context):
3c1a85
        writer.write(tostr(dictGet( context, self.varpath )))
3c1a85
    def optimize(self):
3c1a85
        return self
3c1a85
    def dbgPrint(self, tab):
3c1a85
        print("%svarHtml(%s)" % (tab, self.varpath))
3c1a85
3c1a85
class TemplateVarMd:
3c1a85
    def __init__(self, varpath = None):
3c1a85
        self.varpath = varpath
3c1a85
    def write(self, writer, context):
3c1a85
        writer.write(md2html(dictGet( context, self.varpath )))
3c1a85
    def optimize(self):
3c1a85
        return self
3c1a85
    def dbgPrint(self, tab):
3c1a85
        print("%svarMd(%s)" % (tab, self.varpath))
3c1a85
3c1a85
class TemplateVarText:
3c1a85
    def __init__(self, varpath = None):
3c1a85
        self.varpath = varpath
3c1a85
    def write(self, writer, context):
3c1a85
        writer.write(text2html(dictGet( context, self.varpath )))
3c1a85
    def optimize(self):
3c1a85
        return self
3c1a85
    def dbgPrint(self, tab):
3c1a85
        print("%svarText(%s)" % (tab, self.varpath))
3c1a85
3c1a85
class TemplateVarCount:
3c1a85
    def __init__(self, varpath = None):
3c1a85
        self.varpath = varpath
3c1a85
    def write(self, writer, context):
3c1a85
        v = dictGet( context, self.varpath )
3c1a85
        writer.write(str(len(v) if hasattr(v, '__len__') else 0))
3c1a85
    def optimize(self):
3c1a85
        return self
3c1a85
    def dbgPrint(self, tab):
3c1a85
        print("%svarCount(%s)" % (tab, self.varpath))
3c1a85
3c1a85
class TemplateVarField:
3c1a85
    def __init__(self, varpath = None):
3c1a85
        self.varpath = varpath
3c1a85
    def write(self, writer, context):
3c1a85
        f = self.varpath.split('.')[-1]
3c1a85
        writer.write("

%s: %s

" % (
3c1a85
            text2html( f ),
3c1a85
            text2html( dictGet(context, self.varpath) ) ))
3c1a85
    def optimize(self):
3c1a85
        return self
3c1a85
    def dbgPrint(self, tab):
3c1a85
        print("%svarField(%s)" % (tab, self.varpath))
3c1a85
3c1a85
class TemplateIf:
3c1a85
    def __init__(self, varpath = None, sub = None, alt = None):
3c1a85
        self.varpath = varpath
3c1a85
        self.sub = sub
3c1a85
        self.alt = alt
3c1a85
    def write(self, writer, context):
3c1a85
        if dictGet(context, self.varpath):
3c1a85
            if self.sub: self.sub.write(writer, context)
3c1a85
        else:
3c1a85
            if self.alt: self.alt.write(writer, context)
3c1a85
    def optimize(self):
3c1a85
        self.sub = self.sub.optimize() if self.sub else None
3c1a85
        self.alt = self.alt.optimize() if self.alt else None
3c1a85
        return self if self.sub or self.alt else None
3c1a85
    def dbgPrint(self, tab):
3c1a85
        print("%sif(%s):" % (tab, self.varpath))
3c1a85
        if self.sub:
3c1a85
            self.sub.dbgPrint(tab + tabsym)
3c1a85
        if self.alt:
3c1a85
            print("%selse:" % tab)
3c1a85
            self.alt.dbgPrint(tab + tabsym)
3c1a85
3c1a85
3c1a85
class TemplateWith:
3c1a85
    def __init__(self, varpath = None, varname = None, sub = None):
3c1a85
        self.varpath = varpath
3c1a85
        self.varname = varname
3c1a85
        self.sub = sub
3c1a85
    def write(self, writer, context):
3c1a85
        if self.sub:
3c1a85
            context = dict(context)
3c1a85
            context[self.varname] = dictGet(context, self.varpath)
3c1a85
            self.sub.write(writer, context)
3c1a85
    def optimize(self):
3c1a85
        self.sub = self.sub.optimize() if self.sub else None
3c1a85
        return self if self.sub else None
3c1a85
    def dbgPrint(self, tab):
3c1a85
        print("%swith(%s:%s):" % (tab, self.varpath, self.varname))
3c1a85
        if self.sub: self.sub.dbgPrint(tab + tabsym)
3c1a85
3c1a85
3c1a85
class TemplateComment:
3c1a85
    def __init__(self, text = None):
3c1a85
        self.text = text
3c1a85
    def write(self, writer, context):
3c1a85
        pass
3c1a85
    def optimize(self):
3c1a85
        return None
3c1a85
    def dbgPrint(self, tab):
3c1a85
        print("%scomment(%s)" % (texttab, self.text))
3c1a85
3c1a85
3c1a85
class TemplateLoop:
3c1a85
    def __init__(self, varpath = None, keyvarname = None, varname = None, sub = None, sep = None):
3c1a85
        self.varpath = varpath
3c1a85
        self.keyvarname = keyvarname
3c1a85
        self.varname = varname
3c1a85
        self.sub = sub
3c1a85
        self.sep = sep
3c1a85
    def writeItem(self, writer, context, first, k, v):
3c1a85
        if self.sep and not first:
3c1a85
            self.sep.write(writer, context)
3c1a85
        if self.sub:
3c1a85
            ctx = dict(context)
3c1a85
            ctx[self.keyvarname] = k
3c1a85
            ctx[self.varname] = v
3c1a85
            self.sub.write(writer, ctx)
3c1a85
    def write(self, writer, context):
3c1a85
        if self.sep or self.sub:
3c1a85
            vv = dictGet(context, self.varpath)
3c1a85
            if type(vv) is dict:
3c1a85
                f = True
3c1a85
                keys = list(vv.keys())
3c1a85
                keys.sort()
3c1a85
                for k in keys:
3c1a85
                    self.writeItem(writer, context, f, k, vv[k])
3c1a85
                    f = False
3c1a85
            elif hasattr(vv, '__iter__'):
3c1a85
                idx = 0
3c1a85
                for v in vv:
3c1a85
                    self.writeItem(writer, context, not idx, idx, v)
3c1a85
                    idx = idx + 1
3c1a85
    def optimize(self):
3c1a85
        self.sub = self.sub.optimize() if self.sub else None
3c1a85
        self.sep = self.sep.optimize() if self.sep else None
3c1a85
        return self if self.sub or self.sep else None
3c1a85
    def dbgPrint(self, tab):
3c1a85
        print("%sloop(%s:%s:%s):" % (tab, self.varpath, self.keyvarname, self.varname))
3c1a85
        if self.sub:
3c1a85
            self.sub.dbgPrint(tab + tabsym)
3c1a85
        if self.sep:
3c1a85
            print("%ssep:" % tab)
3c1a85
            self.sep.dbgPrint(tab + tabsym)
3c1a85
3c1a85
class Template:
3c1a85
    def __init__(self):
3c1a85
        self.items = []
3c1a85
    def write(self, writer, context):
3c1a85
        for i in self.items:
3c1a85
            i.write(writer, context)
3c1a85
    def optimize(self):
3c1a85
        it = []
3c1a85
        for i in self.items:
3c1a85
            ii = i.optimize()
3c1a85
            if ii:
3c1a85
                if it and type(ii) is TemplateText and type(it[-1]) is TemplateText:
3c1a85
                    it[-1].text = it[-1].text + ii.text
3c1a85
                else:
3c1a85
                    it.append(ii)
3c1a85
        self.items = it
3c1a85
        if len(self.items) == 1: return self.items[0]
3c1a85
        return self if self.items else None
3c1a85
    def dbgPrint(self, tab):
3c1a85
        print("%stemplate:" % tab)
3c1a85
        for i in self.items:
3c1a85
            i.dbgPrint(tab + tabsym)
3c1a85
3c1a85
3c1a85
class TplLoader:
3c1a85
    @staticmethod
3c1a85
    def loadtext(filename):
3c1a85
        filename = os.path.abspath(filename)
3c1a85
        path = os.path.dirname(filename)
3c1a85
        res = ''
3c1a85
        text = ''
3c1a85
        with open(filename, 'r') as f: text = f.read()
3c1a85
        ii = 0
3c1a85
        i = 0
3c1a85
        while i < len(text):
3c1a85
            if text[i:i+9] == '{include:':
3c1a85
                res = res + text[ii:i]
3c1a85
                a = i+9
3c1a85
                b = text.find('}', a)
3c1a85
                if b<0: raise Exception("include parse error: %s:%d" % (filename, a))
3c1a85
                fn = text[a:b]
3c1a85
                res = res + TplLoader.loadtext(path +'/' + fn)
3c1a85
                i = b+1
3c1a85
                ii = i
3c1a85
            else:
3c1a85
                i = i + 1
3c1a85
        return res + text[ii:i]
3c1a85
3c1a85
    def __init__(self, filename):
3c1a85
        self.filename = os.path.abspath(filename)
3c1a85
        self.text = TplLoader.loadtext(self.filename)
3c1a85
        self.ptr = 0
3c1a85
3c1a85
    def error(self):
3c1a85
        with open('tplerr.txt', 'w') as f: f.write(self.text)
3c1a85
        raise Exception("parse error: %s:%d" % (self.filename, self.ptr))
3c1a85
3c1a85
    def readKey(self, k):
3c1a85
        if self.text[self.ptr:self.ptr + len(k)] == k:
3c1a85
            self.ptr = self.ptr + len(k)
3c1a85
            return True
3c1a85
        return False
3c1a85
3c1a85
    def readVarname(self):
3c1a85
        i = self.ptr
3c1a85
        while i < len(self.text) and (self.text[i].isalnum() or self.text[i] in '_'):
3c1a85
            i = i + 1
3c1a85
        if i == self.ptr: return None
3c1a85
        i, self.ptr = self.ptr, i
3c1a85
        return self.text[i:self.ptr]
3c1a85
3c1a85
    def readVarpath(self):
3c1a85
        i = self.ptr
3c1a85
        varpath = None
3c1a85
        while True:
3c1a85
            varname = self.readVarname()
3c1a85
            if not varname:
3c1a85
                if varpath: self.error()
3c1a85
                break
3c1a85
            varpath = varpath + '.' + varname if varpath else varname
3c1a85
            if not self.readKey('.'): break
3c1a85
        if not varpath:
3c1a85
            self.ptr = i
3c1a85
        return varpath
3c1a85
3c1a85
    def loadVarText(self):
3c1a85
        if not self.readKey("{:"): return None
3c1a85
        varpath = self.readVarpath()
3c1a85
        if not varpath or not self.readKey("}"): self.error()
3c1a85
        return TemplateVarText(varpath)
3c1a85
3c1a85
    def loadVarHtml(self):
3c1a85
        if not self.readKey("{html:"): return None
3c1a85
        varpath = self.readVarpath()
3c1a85
        if not varpath or not self.readKey("}"): self.error()
3c1a85
        return TemplateVarHtml(varpath)
3c1a85
3c1a85
    def loadVarMd(self):
3c1a85
        if not self.readKey("{md:"): return None
3c1a85
        varpath = self.readVarpath()
3c1a85
        if not varpath or not self.readKey("}"): self.error()
3c1a85
        return TemplateVarMd(varpath)
3c1a85
3c1a85
    def loadVarCount(self):
3c1a85
        if not self.readKey("{count:"): return None
3c1a85
        varpath = self.readVarpath()
3c1a85
        if not varpath or not self.readKey("}"): self.error()
3c1a85
        return TemplateVarCount(varpath)
3c1a85
3c1a85
    def loadVarField(self):
3c1a85
        if not self.readKey("{f:"): return None
3c1a85
        varpath = self.readVarpath()
3c1a85
        if not varpath or not self.readKey("}"): self.error()
3c1a85
        return TemplateVarField(varpath)
3c1a85
3c1a85
    def loadIf(self):
3c1a85
        if not self.readKey("{if:"): return None
3c1a85
        varpath = self.readVarpath()
3c1a85
        if not varpath or not self.readKey("}"): self.error()
3c1a85
        res = TemplateIf(varpath, Template(), Template())
3c1a85
        e = False
3c1a85
        while True:
3c1a85
            if self.readKey("{endif}"): break
3c1a85
            if self.readKey("{else}"):
3c1a85
                if e: self.error()
3c1a85
                e = True
3c1a85
                continue
3c1a85
            item = self.loadTemplate()
3c1a85
            if not item: self.error()
3c1a85
            if e:
3c1a85
                res.alt.items.append(item)
3c1a85
            else:
3c1a85
                res.sub.items.append(item)
3c1a85
        return res
3c1a85
3c1a85
    def loadWith(self):
3c1a85
        if not self.readKey("{with:"): return None
3c1a85
        varpath = self.readVarpath()
3c1a85
        if not varpath or not self.readKey(":"): self.error()
3c1a85
        varname = self.readVarname()
3c1a85
        if not varname or not self.readKey("}"): self.error()
3c1a85
        res = TemplateWith(varpath, varname, Template())
3c1a85
        while True:
3c1a85
            if self.readKey("{endwith}"): break
3c1a85
            item = self.loadTemplate()
3c1a85
            if not item: self.error()
3c1a85
            res.sub.items.append(item)
3c1a85
        return res
3c1a85
3c1a85
    def loadComment(self):
3c1a85
        if not self.readKey("{comment}"): return None
3c1a85
        res = TemplateComment()
3c1a85
        i = self.text.find('{endcomment}', self.ptr)
3c1a85
        if i < 0:
3c1a85
            i = len(self.text)
3c1a85
        res = TemplateComment(self.text[self.ptr:i])
3c1a85
        self.ptr = i
3c1a85
        if not self.readKey("{endcomment}"): self.error()
3c1a85
        return res
3c1a85
3c1a85
    def loadLoop(self):
3c1a85
        if not self.readKey("{loop:"): return None
3c1a85
        varpath = self.readVarpath()
3c1a85
        if not varpath or not self.readKey(":"): self.error()
3c1a85
        keyvarname = self.readVarname()
3c1a85
        if not self.readKey(":"): self.error()
3c1a85
        varname = self.readVarname()
3c1a85
        if not varname or not self.readKey("}"): self.error()
3c1a85
        res = TemplateLoop(varpath, keyvarname, varname, Template(), Template())
3c1a85
        s = False
3c1a85
        while True:
3c1a85
            if self.readKey("{endloop}"): break
3c1a85
            if self.readKey("{sep}"):
3c1a85
                if s: self.error()
3c1a85
                s = True
3c1a85
                continue
3c1a85
            item = self.loadTemplate()
3c1a85
            if not item: self.error()
3c1a85
            if s:
3c1a85
                res.sep.items.append(item)
3c1a85
            else:
3c1a85
                res.sub.items.append(item)
3c1a85
        return res
3c1a85
3c1a85
    def loadTemplate(self):
3c1a85
        t = self.loadVarText()
3c1a85
        if t: return t
3c1a85
        t = self.loadVarHtml()
3c1a85
        if t: return t
3c1a85
        t = self.loadVarMd()
3c1a85
        if t: return t
3c1a85
        t = self.loadVarCount()
3c1a85
        if t: return t
3c1a85
        t = self.loadVarField()
3c1a85
        if t: return t
3c1a85
        t = self.loadIf()
3c1a85
        if t: return t
3c1a85
        t = self.loadWith()
3c1a85
        if t: return t
3c1a85
        t = self.loadComment()
3c1a85
        if t: return t
3c1a85
        t = self.loadLoop()
3c1a85
        if t: return t
3c1a85
3c1a85
        i = self.text.find('{', self.ptr)
3c1a85
        if i == self.ptr:
3c1a85
            i = self.text.find('{', self.ptr+1)
3c1a85
        if i < 0:
3c1a85
            i = len(self.text)
3c1a85
        if i <= self.ptr:
3c1a85
            return None
3c1a85
        i, self.ptr = self.ptr, i
3c1a85
        return TemplateText(self.text[i:self.ptr])
3c1a85
3c1a85
    @staticmethod
3c1a85
    def load(filename):
3c1a85
        l = TplLoader(filename)
3c1a85
        t = Template()
3c1a85
        while True:
3c1a85
            tt = l.loadTemplate()
3c1a85
            if not tt: break
3c1a85
            t.items.append(tt)
3c1a85
        if l.ptr < len(l.text): l.error()
3c1a85
        t = t.optimize()
3c1a85
        return t if t else TemplateText()