Blame template.py

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

%s: %s

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