<commit_before># -*- coding: utf-8 -*-
import collections
import cStringIO
import datetime
import hashlib
import json
import logging
import math
import os
import re
import sys
import xml  # FIXME use lxml and etree
import itertools
import lxml.html
from urlparse import urlparse

import babel
import babel.dates
import werkzeug
from PIL import Image

import openerp.http
import openerp.tools
import openerp.tools.func
import openerp.tools.lru
from openerp.http import request
from openerp.tools.safe_eval import safe_eval as eval
from openerp.osv import osv, orm, fields
from openerp.tools.translate import _
from openerp import SUPERUSER_ID

_logger = logging.getLogger(__name__)

#--------------------------------------------------------------------
# QWeb template engine
#--------------------------------------------------------------------
class QWebException(Exception):
    def __init__(self, message, **kw):
        Exception.__init__(self, message)
        self.qweb = dict(kw)

class QWebTemplateNotFound(QWebException):
    pass

def raise_qweb_exception(etype=None, **kw):
    if etype is None:
        etype = QWebException
    orig_type, original, tb = sys.exc_info()
    try:
        raise etype, original, tb
    except etype, e:
        for k, v in kw.items():
            e.qweb[k] = v
        # Will use `raise foo from bar` in python 3 and rename cause to __cause__
        e.qweb['cause'] = original
        raise

class QWebContext(dict):
    def __init__(self, cr, uid, data, loader=None, templates=None, context=None):
        self.cr = cr
        self.uid = uid
        self.loader = loader
        self.templates = templates or {}
        self.context = context
        dic = dict(data)
        super(QWebContext, self).__init__(dic)
        self['defined'] = lambda key: key in self

    def safe_eval(self, expr):
        locals_dict = collections.defaultdict(lambda: None)
        locals_dict.update(self)
        locals_dict.pop('cr', None)
        locals_dict.pop('loader', None)
        return eval(expr, None, locals_dict, nocopy=True, locals_builtins=True)

    def copy(self):
        return QWebContext(self.cr, self.uid, dict.copy(self),
                           loader=self.loader,
                           templates=self.templates,
                           context=self.context)

    def __copy__(self):
        return self.copy()

class QWeb(orm.AbstractModel):
    """QWeb Xml templating engine

    The templating engine use a very simple syntax based "magic" xml
    attributes, to produce textual output (even non-xml).

    The core magic attributes are:

    flow attributes:
        t-if t-foreach t-call

    output attributes:
        t-att t-raw t-esc t-trim

    assignation attribute:
        t-set

    QWeb can be extended like any OpenERP model and new attributes can be
    added.

    If you need to customize t-fields rendering, subclass the ir.qweb.field
    model (and its sub-models) then override :meth:`~.get_converter_for` to
    fetch the right field converters for your qweb model.

    Beware that if you need extensions or alterations which could be
    incompatible with other subsystems, you should create a local object
    inheriting from ``ir.qweb`` and customize that.
    """

    _name = 'ir.qweb'

    node = xml.dom.Node
    _void_elements = frozenset([
        'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen',
        'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr'])
    _format_regex = re.compile(
        '(?:'
            # ruby-style pattern
            '#\{(.+?)\}'
        ')|(?:'
            # jinja-style pattern
            '\{\{(.+?)\}\}'
        ')')

    def __init__(self, pool, cr):
        super(QWeb, self).__init__(pool, cr)

        self._render_tag = self.prefixed_methods('render_tag_')
        self._render_att = self.prefixed_methods('render_att_')

    def prefixed_methods(self, prefix):
        """ Extracts all methods prefixed by ``prefix``, and returns a mapping
        of (t-name, method) where the t-name is the method name with prefix
        removed and underscore converted to dashes

        :param str prefix:
        :return: dict
        """
        n_prefix = len(prefix)
        return dict(
            (name[n_prefix:].replace('_', '-'), getattr(type(self), name))
            for name in dir(self)
            if name.startswith(prefix)
        )

    def register_tag(self, tag, func):
        self._render_tag[tag] = func

    def add_template(self, qwebcontext, name, node):
        """Add a parsed template in the context. Used to preprocess templates."""
        qwebcontext.templates[name] = node

    def load_document(self, document, res_id, qwebcontext):
        """
        Loads an XML document and installs any contained template in the engine
        """
        if hasattr(document, 'documentElement'):
            dom = document
        elif document.startswith("<?xml"):
            dom = xml.dom.minidom.parseString(document)
        else:
            dom = xml.dom.minidom.parse(document)

        for node in dom.documentElement.childNodes:
            if node.nodeType == self.node.ELEMENT_NODE:
                if node.getAttribute('t-name'):
                    name = str(node.getAttribute("t-name"))
                    self.add_template(qwebcontext, name, node)
                if res_id and node.tagName == "t":
                    self.add_template(qwebcontext, res_id, node)
                    res_id = None

    def get_template(self, name, qwebcontext):
        origin_template = qwebcontext.get('__caller__') or qwebcontext['__stack__'][0]
        if qwebcontext.loader and name not in qwebcontext.templates:
            try:
                xml_doc = qwebcontext.loader(name)
            except ValueError:
                raise_qweb_exception(QWebTemplateNotFound, message="Loader could not find template %r" % name, template=origin_template)
            self.load_document(xml_doc, isinstance(name, (int, long)) and name or None, qwebcontext=qwebcontext)

        if name in qwebcontext.templates:
            return qwebcontext.templates[name]

        raise QWebTemplateNotFound("Template %r not found" % name, template=origin_template)

    def eval(self, expr, qwebcontext):
        try:
            return qwebcontext.safe_eval(expr)
        except Exception:
            template = qwebcontext.get('__template__')
            raise_qweb_exception(message="Could not evaluate expression %r" % expr, expression=expr, template=template)

    def eval_object(self, expr, qwebcontext):
        return self.eval(expr, qwebcontext)

    def eval_str(self, expr, qwebcontext):
        if expr == "0":
            return qwebcontext.get(0, '')
        val = self.eval(expr, qwebcontext)
        if isinstance(val, unicode):
            return val.encode("utf8")
        if val is False or val is None:
            return ''
        return str(val)

    def eval_format(self, expr, qwebcontext):
        expr, replacements = self._format_regex.subn(
            lambda m: self.eval_str(m.group(1) or m.group(2), qwebcontext),
            expr
        )

        if replacements:
            return expr

        try:
            return str(expr % qwebcontext)
        except Exception:
            template = qwebcontext.get('__template__')
            raise_qweb_exception(message="Format error for expression %r" % expr, expression=expr, template=template)

    def eval_bool(self, expr, qwebcontext):
        return int(bool(self.eval(expr, qwebcontext)))

    def render(self, cr, uid, id_or_xml_id, qwebcontext=None, loader=None, context=None):
        if qwebcontext is None:
            qwebcontext = {}

        if not isinstance(qwebcontext, QWebContext):
            qwebcontext = QWebContext(cr, uid, qwebcontext, loader=loader, context=context)

        qwebcontext['__template__'] = id_or_xml_id
        stack = qwebcontext.get('__stack__', [])
        if stack:
            qwebcontext['__caller__'] = stack[-1]
        stack.append(id_or_xml_id)
        qwebcontext['__stack__'] = stack
        qwebcontext['xmlid'] = str(stack[0]) # Temporary fix
        return self.render_node(self.get_template(id_or_xml_id, qwebcontext), qwebcontext)

    def render_node(self, element, qwebcontext):
        result = ""
        if element.nodeType == self.node.TEXT_NODE or element.nodeType == self.node.CDATA_SECTION_NODE:
            result = element.data.encode("utf8")
        elif element.nodeType == self.node.ELEMENT_NODE:
            generated_attributes = ""
            t_render = None
            template_attributes = {}
            for (attribute_name, attribute_value) in element.attributes.items():
                attribute_name = str(attribute_name)
                if attribute_name == "groups":
                    cr = qwebcontext.get('request') and qwebcontext['request'].cr or None
                    uid = qwebcontext.get('request') and qwebcontext['request'].uid or None
                    can_see = self.user_has_groups(cr, uid, groups=attribute_value) if cr and uid else False
                    if not can_see:
                        return ''
                    continue

                if isinstance(attribute_value, unicode):
                    attribute_value = attribute_value.encode("utf8")
                else:
                    attribute_value = attribute_value.nodeValue.encode("utf8")

                if attribute_name.startswith("t-"):
                    for attribute in self._render_att:
                        if attribute_name[2:].startswith(attribute):
                            att, val = self._render_att[attribute](self, element, attribute_name, attribute_value, qwebcontext)
                            generated_attributes += val and ' %s="%s"' % (att, werkzeug.utils.escape(val)) or " "
                            break
                    else:
                        if attribute_name[2:] in self._render_tag:
                            t_render = attribute_name[2:]
                        template_attributes[attribute_name[2:]] = attribute_value
                else:
                    generated_attributes += ' %s="%s"' % (attribute_name, werkzeug.utils.escape(attribute_value))

            if 'debug' in template_attributes:
                debugger = template_attributes.get('debug', 'pdb')
                __import__(debugger).set_trace()  # pdb, ipdb, pudb, ...
            if t_render:
                result = self._render_tag[t_render](self, element, template_attributes, generated_attributes, qwebcontext)
            else:
                result = self.render_element(element, template_attributes, generated_attributes, qwebcontext)
        if isinstance(result, unicode):
            return result.encode('utf-8')
        return result

    def render_element(self, element, template_attributes, generated_attributes, qwebcontext, inner=None):
        # element: element
        # template_attributes: t-* attributes
        # generated_attributes: generated attributes
        # qwebcontext: values
        # inner: optional innerXml
        if inner:
            g_inner = inner
        else:
            g_inner = []
            for current_node in element.childNodes:
                try:
                    g_inner.append(self.render_node(current_node, qwebcontext))
                except QWebException:
                    raise
                except Exception:
                    template = qwebcontext.get('__template__')
                    raise_qweb_exception(message="Could not render element %r" % element.nodeName, node=element, template=template)
        name = str(element.nodeName)
        inner = "".join(g_inner)
        trim = template_attributes.get("trim", 0)
        if trim == 0:
            pass
        elif trim == 'left':
            inner = inner.lstrip()
        elif trim == 'right':
            inner = inner.rstrip()
        elif trim == 'both':
            inner = inner.strip()
        if name == "t":
            return inner
        elif len(inner) or name not in self._void_elements:
            return "<%s%s>%s</%s>" % tuple(
                qwebcontext if isinstance(qwebcontext, str) else qwebcontext.encode('utf-8')
                for qwebcontext in (name, generated_attributes, inner, name)
            )
        else:
            return "<%s%s/>" % (name, generated_attributes)

    # Attributes
    def render_att_att(self, element, attribute_name, attribute_value, qwebcontext):
        if attribute_name.startswith("t-attf-"):
            att, val = attribute_name[7:], self.eval_format(attribute_value, qwebcontext)
        elif attribute_name.startswith("t-att-"):
            att, val = attribute_name[6:], self.eval(attribute_value, qwebcontext)
            if isinstance(val, unicode):
                val = val.encode("utf8")
        else:
            att, val = self.eval_object(attribute_value, qwebcontext)
        return att, val

    # Tags
    def render_tag_raw(self, element, template_attributes, generated_attributes, qwebcontext):
        inner = self.eval_str(template_attributes["raw"], qwebcontext)
        return self.render_element(element, template_attributes, generated_attributes, qwebcontext, inner)

    def render_tag_esc(self, element, template_attributes, generated_attributes, qwebcontext):
        options = json.loads(template_attributes.get('esc-options') or '{}')
        widget = self.get_widget_for(options.get('widget', ''))
        inner = widget.format(template_attributes['esc'], options, qwebcontext)
        return self.render_element(element, template_attributes, generated_attributes, qwebcontext, inner)

    def render_tag_foreach(self, element, template_attributes, generated_attributes, qwebcontext):
        expr = template_attributes["foreach"]
        enum = self.eval_object(expr, qwebcontext)
        if enum is not None:
            varname = template_attributes['as'].replace('.', '_')
            copy_qwebcontext = qwebcontext.copy()
            size = -1
            if isinstance(enum, (list, tuple)):
                size = len(enum)
            elif hasattr(enum, 'count'):
                size = enum.count()
            copy_qwebcontext["%s_size" % varname] = size
            copy_qwebcontext["%s_all" % varname] = enum
            index = 0
            ru = []
            for item in enum:
                copy_qwebcontext.update({
                    varname: item,
                    '%s_value' % varname: item,
                    '%s_index' % varname: index,
                    '%s_first' % varname: index == 0,
                    '%s_last' % varname: index + 1 == size,
                })
                if index % 2:
                    copy_qwebcontext.update({
                        '%s_parity' % varname: 'odd',
                        '%s_even' % varname: False,
                        '%s_odd' % varname: True,
                    })
                else:
                    copy_qwebcontext.update({
                        '%s_parity' % varname: 'even',
                        '%s_even' % varname: True,
                        '%s_odd' % varname: False,
                    })
                ru.append(self.render_element(element, template_attributes, generated_attributes, copy_qwebcontext))
                index += 1
            return "".join(ru)
        else:
            template = qwebcontext.get('__template__')
            raise QWebException("foreach enumerator %r is not defined while rendering template %r" % (expr, template), template=template)

    def render_tag_if(self, element, template_attributes, generated_attributes, qwebcontext):
        if self.eval_bool(template_attributes["if"], qwebcontext):
            return self.render_element(element, template_attributes, generated_attributes, qwebcontext)
        return ""

    def render_tag_call(self, element, template_attributes, generated_attributes, qwebcontext):
        d = qwebcontext.copy()
        d[0] = self.render_element(element, template_attributes, generated_attributes, d)
        cr = d.get('request') and d['request'].cr or None
        uid = d.get('request') and d['request'].uid or None

        template = self.eval_format(template_attributes["call"], d)
        try:
            template = int(template)
        except ValueError:
            pass
        return self.render(cr, uid, template, d)

    def render_tag_call_assets(self, element, template_attributes, generated_attributes, qwebcontext):
        """ This special 't-call' tag can be used in order to aggregate/minify javascript and css assets"""
        name = template_attributes['call-assets']

        # Backward compatibility hack for manifest usage<commit_msg>[IMP] use enumerate instead of explicit index thing<commit_after># -*- coding: utf-8 -*-
import collections
import cStringIO
import datetime
import hashlib
import json
import logging
import math
import os
import re
import sys
import xml  # FIXME use lxml and etree
import itertools
import lxml.html
from urlparse import urlparse

import babel
import babel.dates
import werkzeug
from PIL import Image

import openerp.http
import openerp.tools
import openerp.tools.func
import openerp.tools.lru
from openerp.http import request
from openerp.tools.safe_eval import safe_eval as eval
from openerp.osv import osv, orm, fields
from openerp.tools.translate import _
from openerp import SUPERUSER_ID

_logger = logging.getLogger(__name__)

#--------------------------------------------------------------------
# QWeb template engine
#--------------------------------------------------------------------
class QWebException(Exception):
    def __init__(self, message, **kw):
        Exception.__init__(self, message)
        self.qweb = dict(kw)

class QWebTemplateNotFound(QWebException):
    pass

def raise_qweb_exception(etype=None, **kw):
    if etype is None:
        etype = QWebException
    orig_type, original, tb = sys.exc_info()
    try:
        raise etype, original, tb
    except etype, e:
        for k, v in kw.items():
            e.qweb[k] = v
        # Will use `raise foo from bar` in python 3 and rename cause to __cause__
        e.qweb['cause'] = original
        raise

class QWebContext(dict):
    def __init__(self, cr, uid, data, loader=None, templates=None, context=None):
        self.cr = cr
        self.uid = uid
        self.loader = loader
        self.templates = templates or {}
        self.context = context
        dic = dict(data)
        super(QWebContext, self).__init__(dic)
        self['defined'] = lambda key: key in self

    def safe_eval(self, expr):
        locals_dict = collections.defaultdict(lambda: None)
        locals_dict.update(self)
        locals_dict.pop('cr', None)
        locals_dict.pop('loader', None)
        return eval(expr, None, locals_dict, nocopy=True, locals_builtins=True)

    def copy(self):
        return QWebContext(self.cr, self.uid, dict.copy(self),
                           loader=self.loader,
                           templates=self.templates,
                           context=self.context)

    def __copy__(self):
        return self.copy()

class QWeb(orm.AbstractModel):
    """QWeb Xml templating engine

    The templating engine use a very simple syntax based "magic" xml
    attributes, to produce textual output (even non-xml).

    The core magic attributes are:

    flow attributes:
        t-if t-foreach t-call

    output attributes:
        t-att t-raw t-esc t-trim

    assignation attribute:
        t-set

    QWeb can be extended like any OpenERP model and new attributes can be
    added.

    If you need to customize t-fields rendering, subclass the ir.qweb.field
    model (and its sub-models) then override :meth:`~.get_converter_for` to
    fetch the right field converters for your qweb model.

    Beware that if you need extensions or alterations which could be
    incompatible with other subsystems, you should create a local object
    inheriting from ``ir.qweb`` and customize that.
    """

    _name = 'ir.qweb'

    node = xml.dom.Node
    _void_elements = frozenset([
        'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen',
        'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr'])
    _format_regex = re.compile(
        '(?:'
            # ruby-style pattern
            '#\{(.+?)\}'
        ')|(?:'
            # jinja-style pattern
            '\{\{(.+?)\}\}'
        ')')

    def __init__(self, pool, cr):
        super(QWeb, self).__init__(pool, cr)

        self._render_tag = self.prefixed_methods('render_tag_')
        self._render_att = self.prefixed_methods('render_att_')

    def prefixed_methods(self, prefix):
        """ Extracts all methods prefixed by ``prefix``, and returns a mapping
        of (t-name, method) where the t-name is the method name with prefix
        removed and underscore converted to dashes

        :param str prefix:
        :return: dict
        """
        n_prefix = len(prefix)
        return dict(
            (name[n_prefix:].replace('_', '-'), getattr(type(self), name))
            for name in dir(self)
            if name.startswith(prefix)
        )

    def register_tag(self, tag, func):
        self._render_tag[tag] = func

    def add_template(self, qwebcontext, name, node):
        """Add a parsed template in the context. Used to preprocess templates."""
        qwebcontext.templates[name] = node

    def load_document(self, document, res_id, qwebcontext):
        """
        Loads an XML document and installs any contained template in the engine
        """
        if hasattr(document, 'documentElement'):
            dom = document
        elif document.startswith("<?xml"):
            dom = xml.dom.minidom.parseString(document)
        else:
            dom = xml.dom.minidom.parse(document)

        for node in dom.documentElement.childNodes:
            if node.nodeType == self.node.ELEMENT_NODE:
                if node.getAttribute('t-name'):
                    name = str(node.getAttribute("t-name"))
                    self.add_template(qwebcontext, name, node)
                if res_id and node.tagName == "t":
                    self.add_template(qwebcontext, res_id, node)
                    res_id = None

    def get_template(self, name, qwebcontext):
        origin_template = qwebcontext.get('__caller__') or qwebcontext['__stack__'][0]
        if qwebcontext.loader and name not in qwebcontext.templates:
            try:
                xml_doc = qwebcontext.loader(name)
            except ValueError:
                raise_qweb_exception(QWebTemplateNotFound, message="Loader could not find template %r" % name, template=origin_template)
            self.load_document(xml_doc, isinstance(name, (int, long)) and name or None, qwebcontext=qwebcontext)

        if name in qwebcontext.templates:
            return qwebcontext.templates[name]

        raise QWebTemplateNotFound("Template %r not found" % name, template=origin_template)

    def eval(self, expr, qwebcontext):
        try:
            return qwebcontext.safe_eval(expr)
        except Exception:
            template = qwebcontext.get('__template__')
            raise_qweb_exception(message="Could not evaluate expression %r" % expr, expression=expr, template=template)

    def eval_object(self, expr, qwebcontext):
        return self.eval(expr, qwebcontext)

    def eval_str(self, expr, qwebcontext):
        if expr == "0":
            return qwebcontext.get(0, '')
        val = self.eval(expr, qwebcontext)
        if isinstance(val, unicode):
            return val.encode("utf8")
        if val is False or val is None:
            return ''
        return str(val)

    def eval_format(self, expr, qwebcontext):
        expr, replacements = self._format_regex.subn(
            lambda m: self.eval_str(m.group(1) or m.group(2), qwebcontext),
            expr
        )

        if replacements:
            return expr

        try:
            return str(expr % qwebcontext)
        except Exception:
            template = qwebcontext.get('__template__')
            raise_qweb_exception(message="Format error for expression %r" % expr, expression=expr, template=template)

    def eval_bool(self, expr, qwebcontext):
        return int(bool(self.eval(expr, qwebcontext)))

    def render(self, cr, uid, id_or_xml_id, qwebcontext=None, loader=None, context=None):
        if qwebcontext is None:
            qwebcontext = {}

        if not isinstance(qwebcontext, QWebContext):
            qwebcontext = QWebContext(cr, uid, qwebcontext, loader=loader, context=context)

        qwebcontext['__template__'] = id_or_xml_id
        stack = qwebcontext.get('__stack__', [])
        if stack:
            qwebcontext['__caller__'] = stack[-1]
        stack.append(id_or_xml_id)
        qwebcontext['__stack__'] = stack
        qwebcontext['xmlid'] = str(stack[0]) # Temporary fix
        return self.render_node(self.get_template(id_or_xml_id, qwebcontext), qwebcontext)

    def render_node(self, element, qwebcontext):
        result = ""
        if element.nodeType == self.node.TEXT_NODE or element.nodeType == self.node.CDATA_SECTION_NODE:
            result = element.data.encode("utf8")
        elif element.nodeType == self.node.ELEMENT_NODE:
            generated_attributes = ""
            t_render = None
            template_attributes = {}
            for (attribute_name, attribute_value) in element.attributes.items():
                attribute_name = str(attribute_name)
                if attribute_name == "groups":
                    cr = qwebcontext.get('request') and qwebcontext['request'].cr or None
                    uid = qwebcontext.get('request') and qwebcontext['request'].uid or None
                    can_see = self.user_has_groups(cr, uid, groups=attribute_value) if cr and uid else False
                    if not can_see:
                        return ''
                    continue

                if isinstance(attribute_value, unicode):
                    attribute_value = attribute_value.encode("utf8")
                else:
                    attribute_value = attribute_value.nodeValue.encode("utf8")

                if attribute_name.startswith("t-"):
                    for attribute in self._render_att:
                        if attribute_name[2:].startswith(attribute):
                            att, val = self._render_att[attribute](self, element, attribute_name, attribute_value, qwebcontext)
                            generated_attributes += val and ' %s="%s"' % (att, werkzeug.utils.escape(val)) or " "
                            break
                    else:
                        if attribute_name[2:] in self._render_tag:
                            t_render = attribute_name[2:]
                        template_attributes[attribute_name[2:]] = attribute_value
                else:
                    generated_attributes += ' %s="%s"' % (attribute_name, werkzeug.utils.escape(attribute_value))

            if 'debug' in template_attributes:
                debugger = template_attributes.get('debug', 'pdb')
                __import__(debugger).set_trace()  # pdb, ipdb, pudb, ...
            if t_render:
                result = self._render_tag[t_render](self, element, template_attributes, generated_attributes, qwebcontext)
            else:
                result = self.render_element(element, template_attributes, generated_attributes, qwebcontext)
        if isinstance(result, unicode):
            return result.encode('utf-8')
        return result

    def render_element(self, element, template_attributes, generated_attributes, qwebcontext, inner=None):
        # element: element
        # template_attributes: t-* attributes
        # generated_attributes: generated attributes
        # qwebcontext: values
        # inner: optional innerXml
        if inner:
            g_inner = inner
        else:
            g_inner = []
            for current_node in element.childNodes:
                try:
                    g_inner.append(self.render_node(current_node, qwebcontext))
                except QWebException:
                    raise
                except Exception:
                    template = qwebcontext.get('__template__')
                    raise_qweb_exception(message="Could not render element %r" % element.nodeName, node=element, template=template)
        name = str(element.nodeName)
        inner = "".join(g_inner)
        trim = template_attributes.get("trim", 0)
        if trim == 0:
            pass
        elif trim == 'left':
            inner = inner.lstrip()
        elif trim == 'right':
            inner = inner.rstrip()
        elif trim == 'both':
            inner = inner.strip()
        if name == "t":
            return inner
        elif len(inner) or name not in self._void_elements:
            return "<%s%s>%s</%s>" % tuple(
                qwebcontext if isinstance(qwebcontext, str) else qwebcontext.encode('utf-8')
                for qwebcontext in (name, generated_attributes, inner, name)
            )
        else:
            return "<%s%s/>" % (name, generated_attributes)

    # Attributes
    def render_att_att(self, element, attribute_name, attribute_value, qwebcontext):
        if attribute_name.startswith("t-attf-"):
            att, val = attribute_name[7:], self.eval_format(attribute_value, qwebcontext)
        elif attribute_name.startswith("t-att-"):
            att, val = attribute_name[6:], self.eval(attribute_value, qwebcontext)
            if isinstance(val, unicode):
                val = val.encode("utf8")
        else:
            att, val = self.eval_object(attribute_value, qwebcontext)
        return att, val

    # Tags
    def render_tag_raw(self, element, template_attributes, generated_attributes, qwebcontext):
        inner = self.eval_str(template_attributes["raw"], qwebcontext)
        return self.render_element(element, template_attributes, generated_attributes, qwebcontext, inner)

    def render_tag_esc(self, element, template_attributes, generated_attributes, qwebcontext):
        options = json.loads(template_attributes.get('esc-options') or '{}')
        widget = self.get_widget_for(options.get('widget', ''))
        inner = widget.format(template_attributes['esc'], options, qwebcontext)
        return self.render_element(element, template_attributes, generated_attributes, qwebcontext, inner)

    def render_tag_foreach(self, element, template_attributes, generated_attributes, qwebcontext):
        expr = template_attributes["foreach"]
        enum = self.eval_object(expr, qwebcontext)
        if enum is not None:
            varname = template_attributes['as'].replace('.', '_')
            copy_qwebcontext = qwebcontext.copy()
            size = -1
            if isinstance(enum, (list, tuple)):
                size = len(enum)
            elif hasattr(enum, 'count'):
                size = enum.count()
            copy_qwebcontext["%s_size" % varname] = size
            copy_qwebcontext["%s_all" % varname] = enum
            ru = []
            for index, item in enumerate(enum):
                copy_qwebcontext.update({
                    varname: item,
                    '%s_value' % varname: item,
                    '%s_index' % varname: index,
                    '%s_first' % varname: index == 0,
                    '%s_last' % varname: index + 1 == size,
                })
                if index % 2:
                    copy_qwebcontext.update({
                        '%s_parity' % varname: 'odd',
                        '%s_even' % varname: False,
                        '%s_odd' % varname: True,
                    })
                else:
                    copy_qwebcontext.update({
                        '%s_parity' % varname: 'even',
                        '%s_even' % varname: True,
                        '%s_odd' % varname: False,
                    })
                ru.append(self.render_element(element, template_attributes, generated_attributes, copy_qwebcontext))
            return "".join(ru)
        else:
            template = qwebcontext.get('__template__')
            raise QWebException("foreach enumerator %r is not defined while rendering template %r" % (expr, template), template=template)

    def render_tag_if(self, element, template_attributes, generated_attributes, qwebcontext):
        if self.eval_bool(template_attributes["if"], qwebcontext):
            return self.render_element(element, template_attributes, generated_attributes, qwebcontext)
        return ""

    def render_tag_call(self, element, template_attributes, generated_attributes, qwebcontext):
        d = qwebcontext.copy()
        d[0] = self.render_element(element, template_attributes, generated_attributes, d)
        cr = d.get('request') and d['request'].cr or None
        uid = d.get('request') and d['request'].uid or None

        template = self.eval_format(template_attributes["call"], d)
        try:
            template = int(template)
        except ValueError:
            pass
        return self.render(cr, uid, template, d)

    def render_tag_call_assets(self, element, template_attributes, generated_attributes, qwebcontext):
        """ This special 't-call' tag can be used in order to aggregate/minify javascript and css assets"""
        name = template_attributes['call-assets']

        # Backward compatibility hack for manifest usage
{'fraction_non_alphanumeric': 0.10679003133860618, 'fraction_numerical': 0.0017907774958961348, 'mean_word_length': 2.1588573583482606, 'pattern_counts': {'":': 8, '<': 11, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 32}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1344394', 'n_tokens_mistral': 9255, 'n_tokens_neox': 8984, 'n_words': 2353}