Пример #1
0
 def create_sms(self, ids, data={}, name_id=None, related_id=None, context={}):
     print("SMSTemplate.create_sms",ids)
     obj = self.browse(ids)[0]
     try:
         phone = render_template(obj.phone or "", data)
     except:
         raise Exception("Failed to render 'Phone' in template: %s" % obj.name)
     try:
         body = render_template(obj.body or "", data)
     except:
         raise Exception("Failed to render 'Body' in template: %s" % obj.name)
     if obj.related and not related_id:
         try:
             related_id = render_template(obj.related or "", data)
         except:
             raise Exception("Failed to render 'Related To' in template: %s" % obj.name)
     if obj.contact and not name_id:
         try:
             name_id = render_template(obj.contact or "", data)
         except:
             raise Exception("Failed to render 'Contact' in template: %s" % obj.name)
     else:
         name_id=None
     vals = {
         "date": time.strftime("%Y-%m-%d %H:%M:%S"),
         "phone": phone,
         "body": body,
         "state": "to_send",
         "account_id": obj.account_id.id,
     }
     print("vals",vals)
     sms_id = get_model("sms.message").create(vals)
     return sms_id
Пример #2
0
 def create_email(self, ids, data={}, name_id=None, related_id=None, mailbox_id=None, context={}):
     obj = self.browse(ids)[0]
     try:
         from_addr = render_template(obj.from_addr or "", data)
     except:
         raise Exception("Failed to render 'From Address' in template: %s" % obj.name)
     try:
         to_addrs = render_template(obj.to_addrs or "", data)
     except:
         raise Exception("Failed to render 'To Addresses' in template: %s" % obj.name)
     if obj.cc_addrs:
         try:
             cc_addrs = render_template(obj.cc_addrs or "", data)
         except:
             raise Exception("Failed to render 'Cc Addresses' in template: %s" % obj.name)
     try:
         subject = render_template(obj.subject, data)
     except:
         raise Exception("Failed to render 'Subject' in template: %s" % obj.name)
     try:
         body = render_template(obj.body.replace(""",'"'), data)
     except:
         raise Exception("Failed to render 'Body' in template: %s" % obj.body)
     if obj.related and not related_id:
         try:
             related_id = render_template(obj.related or "", data)
         except:
             raise Exception("Failed to render 'Related To' in template: %s" % obj.name)
     attachments = []
     if obj.attachments:
         try:
             files = render_template(obj.attachments, data)
             for f in files.split("|"):
                 fname = f.strip()
                 if not fname:
                     continue
                 attach_vals = {
                     "file": fname,
                 }
                 attachments.append(("create", attach_vals))
         except:
             raise Exception("Failed to render 'Attachments' in template: %s" % obj.name)
     vals = {
         "type": "out",
         "date": time.strftime("%Y-%m-%d %H:%M:%S"),
         "from_addr": from_addr,
         "to_addrs": to_addrs,
         "subject": subject,
         "body": body,
         "state": "to_send",
         "attachments": attachments,
         "name_id": name_id,
         "related_id": related_id,
     }
     if mailbox_id:
         vals["mailbox_id"] = mailbox_id
     email_id = get_model("email.message").create(vals)
     return email_id
Пример #3
0
 def create_sms(self,
                ids,
                data={},
                name_id=None,
                related_id=None,
                context={}):
     print("SMSTemplate.create_sms", ids)
     obj = self.browse(ids)[0]
     try:
         phone = render_template(obj.phone or "", data)
     except:
         raise Exception("Failed to render 'Phone' in template: %s" %
                         obj.name)
     try:
         body = render_template(obj.body or "", data)
     except:
         raise Exception("Failed to render 'Body' in template: %s" %
                         obj.name)
     if obj.related and not related_id:
         try:
             related_id = render_template(obj.related or "", data)
         except:
             raise Exception(
                 "Failed to render 'Related To' in template: %s" % obj.name)
     if obj.contact and not name_id:
         try:
             name_id = render_template(obj.contact or "", data)
         except:
             raise Exception("Failed to render 'Contact' in template: %s" %
                             obj.name)
     else:
         name_id = None
     vals = {
         "date": time.strftime("%Y-%m-%d %H:%M:%S"),
         "phone": phone,
         "body": body,
         "state": "to_send",
         "account_id": obj.account_id.id,
     }
     print("vals", vals)
     sms_id = get_model("sms.message").create(vals)
     return sms_id
Пример #4
0
 def create_reminders(self, ids, context={}):
     for obj in self.browse(ids):
         categ = obj.categ_id
         if not categ:
             continue
         obj.write({"reminders": [("delete_all",)]})
         for tmpl in categ.reminder_templates:
             s = tmpl.scheduled_date.strip()
             days = int(s)
             d = datetime.datetime.strptime(obj.expiry_date, "%Y-%m-%d") + datetime.timedelta(days=days)
             ctx = {"doc": obj}
             subject = render_template(tmpl.subject, ctx)
             body = render_template(tmpl.body or "", ctx)
             vals = {
                 "scheduled_date": d.strftime("%Y-%m-%d"),
                 "doc_id": obj.id,
                 "user_id": tmpl.user_id.id,
                 "subject": subject,
                 "body": body,
             }
             get_model("reminder").create(vals)
Пример #5
0
 def create_reminders(self, ids, context={}):
     for obj in self.browse(ids):
         categ = obj.categ_id
         if not categ:
             continue
         obj.write({"reminders": [("delete_all", )]})
         for tmpl in categ.reminder_templates:
             s = tmpl.scheduled_date.strip()
             days = int(s)
             d = datetime.datetime.strptime(
                 obj.expiry_date,
                 "%Y-%m-%d") + datetime.timedelta(days=days)
             ctx = {"doc": obj}
             subject = render_template(tmpl.subject, ctx)
             body = render_template(tmpl.body or "", ctx)
             vals = {
                 "scheduled_date": d.strftime("%Y-%m-%d"),
                 "doc_id": obj.id,
                 "user_id": tmpl.user_id.id,
                 "subject": subject,
                 "body": body,
             }
             get_model("reminder").create(vals)
Пример #6
0
def report_render_odt(tmpl_name, data):
    print("REPORT_RENDER_ODT", tmpl_name, data)
    try:
        tmpl_data = get_report_template(tmpl_name, "odt")
    except:
        tmpl_data = get_report_template(tmpl_name, "odt2")  # XXX
    tmpl_f = BytesIO(tmpl_data)
    zf = zipfile.ZipFile(tmpl_f)

    f_out = BytesIO()
    zf_out = zipfile.ZipFile(f_out, "w")
    img_no = 1
    add_files = []
    for tmpl_fname in ("content.xml", "styles.xml"):
        getns = {
            "text": "urn:oasis:names:tc:opendocument:xmlns:text:1.0",
            "draw": "urn:oasis:names:tc:opendocument:xmlns:drawing:1.0",
            "svg-com":
            "urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0",
            "table": "urn:oasis:names:tc:opendocument:xmlns:table:1.0"
        }
        ns_manifest = "urn:oasis:names:tc:opendocument:xmlns:manifest:1.0"
        ns_svg = "urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0"
        f = zf.open(tmpl_fname)
        tree = etree.parse(f)
        open("/tmp/odt_template_orig.xml",
             "w").write(etree.tostring(tree, pretty_print=True).decode())

        chunks = get_text_chunks(tree)
        print("#" * 80)
        print("CHUNKS", [c[1] for c in chunks])
        found_expr = False
        expr = None
        expr_pos = []
        for i, (p, t) in enumerate(chunks):
            if i + 1 < len(chunks):
                next_t = chunks[i + 1][1]
            else:
                next_t = None
            if not found_expr:
                if t.find("{{") != -1 or next_t and t[-1] == "{" and next_t[
                        0] == "{":
                    found_expr = True
                    expr = ""
                    expr_pos = []
            if found_expr:
                expr += t
                expr_pos.append(p)
                if expr.find("}}") != -1:
                    for p in expr_pos:
                        set_pos_text(p, "")
                    set_pos_text(expr_pos[0], expr)
                    print("EXPR", expr)
                    if not check_hbs_expression(expr):
                        raise Exception("Invalid expression: '%s'" % expr)
                    found_expr = False

        open("/tmp/odt_template_join.xml",
             "w").write(etree.tostring(tree, pretty_print=True).decode())

        chunks = get_text_chunks(tree)
        blocks = []
        level = 0
        for p, t in chunks:
            for expr in re.findall("{{[#/].*?}}", t):
                if expr[2] == "#":
                    blocks.append((p, expr, level))
                    level += 1
                elif expr[2] == "/":
                    level -= 1
                    blocks.append((p, expr, level))
        print("#" * 80)
        print("BLOCKS", [(b[1], b[2]) for b in blocks])
        pairs = []
        for i, (p, t, level) in enumerate(blocks):
            if t[2] == "#":
                found = False
                for p2, t2, level2 in blocks[i + 1:]:
                    if level2 == level:
                        pairs.append((p, t, p2, t2))
                        found = True
                        break
                if not found:
                    raise Exception("No closing expression found for %s" % t)
        print("PAIRS", [(t, t2) for p, t, p2, t2 in pairs])
        for p, t, p2, t2 in pairs:
            if p[0] == p2[0]:
                continue
            parent = get_common_parent(p[0], p2[0])
            start = stop = None
            for i, c in enumerate(parent):
                if check_el_contains(c, p[0]):
                    start = i
                if check_el_contains(c, p2[0]):
                    stop = i
            print("relocate pair: '%s' '%s'" % (t, t2))
            print("  parent=%s start=%s stop=%s" % (parent, start, stop))
            if stop > start + 1:
                remove_keep_tail(parent, start)
                remove_keep_tail(parent, stop - 1)
                if start > 0:
                    parent[start - 1].tail = (parent[start - 1].tail or "") + t
                else:
                    parent.text = (parent.text or "") + t
                parent[stop - 2].tail = t2 + (parent[stop - 2].tail or "")

        open("/tmp/odt_template_block.xml",
             "w").write(etree.tostring(tree, pretty_print=True).decode())

        def _repl(m):
            var = m.group(1)
            return "{{{odt_linebreak %s}}}" % var

        textp = tree.findall(".//*")
        for textel in textp:
            if not textel.text:
                continue
            if textel.text.find("{{") == -1:
                continue
            t = re.sub("{{\s*(\w+)\s*}}", _repl, textel.text)
            if t != textel.text:
                textel.text = t

        doc_tmpl = etree.tostring(tree, pretty_print=True, encoding="unicode")
        # XXX sometimes they're found as "”" instead of &#8221;
        doc_tmpl = doc_tmpl.replace("“", "\"")
        doc_tmpl = doc_tmpl.replace("”", "\"")
        doc_tmpl = doc_tmpl.replace("&#8220;", "\"")
        doc_tmpl = doc_tmpl.replace("&#8221;", "\"")

        open("/tmp/odt_template_use.xml", "w").write(doc_tmpl)
        odt_xml = template.render_template(doc_tmpl, data)  # XXX
        open("/tmp/odt_render_out.xml", "w").write(odt_xml)

        tree = etree.fromstring(odt_xml)
        for frame_el in tree.findall(".//draw:frame",
                                     namespaces={"draw": getns["draw"]}):
            title_el = frame_el.find("svg:title", namespaces={"svg": ns_svg})
            if title_el is not None:
                fname = title_el.text
            else:
                fname = None
            if not fname:
                continue
            img_path = utils.get_file_path(fname)
            if not os.path.exists(img_path):  # XXX
                continue
            new_zip_path = "Pictures/_img%d.png" % img_no
            img_no += 1
            image_el = frame_el[0]
            image_el.attrib[
                '{http://www.w3.org/1999/xlink}href'] = new_zip_path
            add_files.append((new_zip_path, img_path, "image/png"))
            cx = frame_el.attrib["{%s}width" % getns["svg-com"]]
            cy = frame_el.attrib["{%s}height" % getns["svg-com"]]
            cx_unit = cx[-2] + cx[-1]
            cx = cx[:-2]
            cy_unit = cy[-2] + cy[-1]
            cy = cy[:-2]
            img = Image.open(img_path)
            w, h = img.size
            scale = min(float(cx) / w, float(cy) / h)
            cx2 = w * scale
            cy2 = h * scale
            frame_el.attrib["{%s}width" %
                            getns["svg-com"]] = str(cx2) + cx_unit
            frame_el.attrib["{%s}height" %
                            getns["svg-com"]] = str(cy2) + cy_unit

        odt_xml = etree.tostring(tree, pretty_print=True, encoding="unicode")
        zf_out.writestr(tmpl_fname, odt_xml)

    print("add_files", add_files)

    f = zf.open("META-INF/manifest.xml")
    tree = etree.parse(f)
    root = tree.getroot()
    for zip_path, img_path, media_type in add_files:
        el = etree.Element("{%s}file-entry" % ns_manifest)
        el.attrib["{%s}full-path" % ns_manifest] = zip_path
        el.attrib["{%s}media-type" % ns_manifest] = media_type
        root.append(el)
    manif_xml = etree.tostring(tree, pretty_print=True, encoding="unicode")
    zf_out.writestr("META-INF/manifest.xml", manif_xml)

    out_names = set(zf_out.namelist())
    for name in zf.namelist():
        if name in out_names:
            continue
        data = zf.read(name)
        zf_out.writestr(name, data)
    for zip_path, img_path, media_type in add_files:
        img_data = open(img_path, "rb").read()
        zf_out.writestr(zip_path, img_data)
    zf.close()
    zf_out.close()
    data = f_out.getvalue()
    f_out.close()
    return data
Пример #7
0
def report_render_doc(tmpl_name, data):  # Do they delete the newline thing
    print("report_render_doc", tmpl_name, data)
    tmpl_data = get_report_template(tmpl_name, "docx")
    tmpl_f = BytesIO(tmpl_data)
    zf = zipfile.ZipFile(tmpl_f)

    ns = "http://schemas.openxmlformats.org/package/2006/relationships"
    f = zf.open("word/_rels/document.xml.rels")
    tree = etree.parse(f)
    rels = {}
    for el in tree.findall(".//ns:Relationship", namespaces={"ns": ns}):
        rels[el.attrib.get("Id")] = el.attrib.get("Target")
    print("rels", rels)

    ns = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
    f = zf.open("word/document.xml")
    tree = etree.parse(f)
    print("*** DOC_SRC *********************")
    print(etree.tostring(tree, pretty_print=True).decode())

    for el in tree.iterfind(".//w:p", namespaces={"w": ns}):
        vals = []
        start = False
        parent = el
        children = el.findall(".//w:r", namespaces={"w": ns})
        vals = []
        for child in children:
            grandchild = child.findall(".//w:t", namespaces={"w": ns})
            if grandchild and grandchild[0].text:
                text = grandchild[0].text
                if text and (text.find("{{") != -1 or text.find("}}") != -1
                             or start):
                    vals.append((text, child, grandchild[0]))
                    start = False if text.find("}}") != -1 and not (
                        text.find("{{") != -1) else True
        text = "".join([text for text, c, g in vals])
        for i, val in enumerate(vals):
            if i == 0:
                val[2].text = text
            else:
                parent.remove(val[1])

    images = {}
    change_list = []
    change_list.append("word/document.xml")
    for el in tree.iterfind(".//w:tc", namespaces={"w": ns}):
        expr = el.xpath("string()")
        if expr and expr.find("{{image") != -1:
            expr = expr.replace("\u201c", "\"")  # XXX
            expr = expr.replace("\u201d", "\"")
            m = re.search("{{image \"(.*?)\"", expr)
            if not m:
                raise Exception("Failed to parse image expression: %s" % expr)
            n = m.group(1)
            v = data.get(n)
            if v:
                res = el.findall(".//w:drawing", namespaces={"w": ns})
                if not res:
                    raise Exception("Failed to replace image")
                el_drawing = res[0]
                el_blip = el_drawing.find(
                    ".//a:blip",
                    namespaces={
                        "a":
                        "http://schemas.openxmlformats.org/drawingml/2006/main"
                    })
                embed = el_blip.attrib[
                    "{http://schemas.openxmlformats.org/officeDocument/2006/relationships}embed"]
                zip_path = rels[embed]
                change_list.append("word/" + zip_path)
                images[zip_path] = v
                el_extent = el_drawing.find(
                    ".//wp:extent",
                    namespaces={
                        "wp":
                        "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing"
                    })
                cx = int(el_extent.attrib["cx"])
                cy = int(el_extent.attrib["cy"])
                img_path = utils.get_file_path(v)
                img = Image.open(img_path)
                w, h = img.size
                scale = min(float(cx) / w, float(cy) / h)
                cx2 = int(round(w * scale))
                cy2 = int(round(h * scale))
                el_extent.attrib["cx"] = str(cx2)
                el_extent.attrib["cy"] = str(cy2)
                el_ext = el_drawing.find(
                    ".//a:ext",
                    namespaces={
                        "a":
                        "http://schemas.openxmlformats.org/drawingml/2006/main"
                    })
                el_ext.attrib["cx"] = str(cx2)
                el_ext.attrib["cy"] = str(cy2)
            wts = el.findall(".//w:t", namespaces={"w": ns})
            for wt in wts:
                if wt.text and wt.text.find("{{image") != -1:
                    parent = wt.getparent()
                    grandparent = parent.getparent()
                    grandparent.remove(parent)

    for el in tree.iterfind(".//w:t", namespaces={"w": ns}):
        expr = el.text
        if expr.find("{{#") != -1 or expr.find("{{/") != -1:
            el_tr = el.getparent()
            while not el_tr.tag.endswith("}tr"):
                el_tr = el_tr.getparent()
            p = el_tr.getparent()
            # XXX
            if p:
                i = p.index(el_tr)
                p.remove(el_tr)
                p[i - 1].tail = expr

    doc_tmpl = etree.tostring(tree, pretty_print=True, encoding="unicode")
    doc_tmpl = doc_tmpl.replace("&#8220;", "\"")
    doc_tmpl = doc_tmpl.replace("&#8221;", "\"")
    doc_tmpl = doc_tmpl.replace("\u201c", "\"")
    doc_tmpl = doc_tmpl.replace("\u201d", "\"")
    print("*** DOC_TMPL *********************")
    for i, l in enumerate(doc_tmpl.split("\n")):
        print(i + 1, l)
    doc_xml = template.render_template(doc_tmpl, data)
    f_out = BytesIO()
    zf_out = zipfile.ZipFile(f_out, "w")
    for name in zf.namelist():
        print("XXX", name)
        if name in change_list:
            pass
        else:
            data = zf.read(name)
            zf_out.writestr(name, data)
    for zip_path, v in images.items():
        img_path = utils.get_file_path(v)
        img_data = open(img_path, "rb").read()
        zf_out.writestr("word/" + zip_path, img_data)
    zf_out.writestr("word/document.xml", doc_xml)
    zf.close()
    zf_out.close()
    data = f_out.getvalue()
    f_out.close()
    return data
Пример #8
0
def report_render_xls(tmpl_name, data, fast_render=False):
    print("report_render_xls", tmpl_name, data)
    tmpl_data = get_report_template(tmpl_name, "xlsx")
    tmpl_f = BytesIO(tmpl_data)
    zf_in = zipfile.ZipFile(tmpl_f)
    ns = "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
    nsd = "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing"
    nsr = "http://schemas.openxmlformats.org/package/2006/relationships"
    nsd2 = "http://schemas.openxmlformats.org/drawingml/2006/main"
    nsr2 = "http://schemas.openxmlformats.org/officeDocument/2006/relationships"

    f = zf_in.open("xl/sharedStrings.xml")
    tree = etree.parse(f)
    f.close()
    strings = []
    for el in tree.findall(".//ns:si", namespaces={"ns": ns}):
        strings.append(el[0].text)
    print("strings", strings)

    f = zf_in.open("xl/styles.xml")
    style_tree = etree.parse(f)
    f.close()
    el_fmt = etree.Element("numFmt")
    el_fmt.attrib["formatCode"] = "#,##0.00_);\(#,##0.00\)"
    el_fmt.attrib["numFmtId"] = "999"  # XXX
    el_p = style_tree.find(".//ns:numFmts", namespaces={"ns": ns})
    if el_p:
        el_p.append(el_fmt)
        el_p.attrib["count"] = str(int(el_p.attrib["count"]) + 1)
    else:
        el_p = etree.Element("numFmts")
        style_tree.getroot().insert(0, el_p)
        el_p.append(el_fmt)
        el_p.attrib["count"] = "1"

    currency_styles = {}

    def _make_currency_style(old_style):
        if old_style in currency_styles:
            return currency_styles[old_style]
        el_p = style_tree.find(".//ns:cellXfs", namespaces={"ns": ns})
        if el_p is None:
            raise Exception("Cell styles not found!")
        if old_style:
            el_old = el_p[old_style]
            el_new = deepcopy(el_old)
        else:
            el_new = etree.Element("xf")
            el_new.attrib["xfId"] = "0"  # XXX
        el_new.attrib["numFmtId"] = "999"
        el_p.append(el_new)
        new_style = int(el_p.attrib["count"])
        currency_styles[old_style] = new_style
        el_p.attrib["count"] = str(new_style + 1)
        return new_style

    drawing_no = 0
    images = {}
    while True:
        drawing_no += 1
        drawing_path = "xl/drawings/drawing%s.xml" % drawing_no
        try:
            f = zf_in.open(drawing_path)
        except:
            break
        print("processing drawing %s" % drawing_no)
        tree = etree.parse(f)
        f.close()
        el = tree.find(".//ns:cNvPr", namespaces={"ns": nsd})
        expr = el.attrib.get("name")
        if not expr:
            continue
        fname = template.render_template(expr, data).strip()
        print("drawing fname", fname)
        if not fname:
            continue

        f = zf_in.open("xl/drawings/_rels/drawing%s.xml.rels" % drawing_no)
        rel_tree = etree.parse(f)
        f.close()
        rels = {}
        for el in rel_tree.findall(".//ns:Relationship",
                                   namespaces={"ns": nsr}):
            rels[el.attrib.get("Id")] = el.attrib.get("Target")

        el_blip = tree.find(".//ns:blip", namespaces={"ns": nsd2})
        embed = el_blip.attrib["{%s}embed" % nsr2]
        zip_path = rels[embed].replace("..", "xl")
        print("zip_path", zip_path)

        img_data = open(fname, "rb").read()
        images[zip_path] = img_data

    sheet_no = 0
    sheet_out = {}
    while True:
        sheet_no += 1
        sheet_path = "xl/worksheets/sheet%s.xml" % sheet_no
        try:
            f = zf_in.open(sheet_path)
        except:
            break
        print("processing sheet %s" % sheet_no)
        print("converting to inline strings...")
        tree = etree.parse(f)
        f.close()
        for el in tree.findall(".//ns:c[@t='s']", namespaces={"ns": ns}):
            i = int(el[0].text)
            s = strings[i]
            if not s:
                continue
            if s.find("{{currency") != -1:
                el.attrib["t"] = "n"
                if "s" in el.attrib:
                    old_style = int(el.attrib["s"])
                else:
                    old_style = None
                new_style = _make_currency_style(old_style)
                el.attrib["s"] = str(new_style)

                def _repl(m):
                    return "{{currency %s nogroup=1}}" % m.group(1)

                el[0].text = re.sub(r"{{currency (.*?)}}", _repl, s)
            else:
                el.remove(el[0])
                el.attrib["t"] = "inlineStr"
                el_t = etree.Element("t")
                el_t.text = s
                el_is = etree.Element("is")
                el_is.append(el_t)
                el.append(el_is)

        #print("*** SHEET_SRC *********************")
        # print(etree.tostring(tree,pretty_print=True).decode()[:10000])

        print("renaming template expressions...")
        for el in tree.findall(".//t"):
            s = el.text
            if s.find("{{#") == -1 and s.find("{{/") == -1:
                continue
            s = s.replace("\u201c", "\"")  # XXX
            s = s.replace("\u201d", "\"")
            # print("XXX",s)
            col = el.getparent().getparent()
            row = col.getparent()
            p = row.getparent()
            if get_cell_col(col.attrib["r"]) == "A":
                i = p.index(row)
                p.remove(row)
                if i == 0:
                    p.text = (p.text or "") + "\n" + s + "\n"
                else:
                    p[i - 1].tail = (p[i - 1].tail or "") + "\n" + s + "\n"
            else:
                if s.find("{{#") != -1 and s.find("{{/") == -1:
                    col_no = get_cell_col(col.attrib["r"])
                    m = re.search("{{#each (.*?)}}", s)
                    if not m:
                        raise Exception("Invalid 'each' expression")
                    list_name = m.group(1)
                    i = row.index(col)
                    if i == 0:
                        row.text = "\n{{#first %s}}\n" % list_name
                    else:
                        row[i - 1].tail = "\n{{#first %s}}\n" % list_name
                    for col2 in row:
                        if get_cell_col(col2.attrib["r"]) >= col_no:
                            row.remove(col2)
                    next_row = row.getnext()
                    for next_col in next_row:
                        if get_cell_col(next_col.attrib["r"]) >= col_no:
                            col2 = deepcopy(next_col)
                            row.append(col2)
                    row[-1].tail = "\n{{/first}}\n"
                    row.tail = "\n{{#after_first %s}}\n" % list_name
                elif s.find("{{#") == -1 and s.find("{{/") != -1:
                    i = p.index(row)
                    p[i - 1].tail = "\n{{/after_first}}\n"
                    p.remove(row)

        sheet_tmpl = etree.tostring(tree, pretty_print=True).decode()
        sheet_tmpl = sheet_tmpl.replace("&#8220;", "\"")
        sheet_tmpl = sheet_tmpl.replace("&#8221;", "\"")
        #print("*** SHEET_TMPL *********************")
        # print(sheet_tmpl[:10000])
        print("template size: %s chars" % len(sheet_tmpl))
        print("number of pieces: %d" % len(sheet_tmpl.split("{{")))
        print("rendering template...")
        if fast_render:  # FIXME; remove need for this!!! (make new compiler faster)
            sheet_xml = template.render_template_old(sheet_tmpl, data)
        else:
            sheet_xml = template.render_template(sheet_tmpl, data)
        #print("*** SHEET_XML1 **********************")
        # print(sheet_xml[:10000])

        print("parsing rendered tree...")
        tree = etree.fromstring(sheet_xml)

        print("renaming cells...")
        # rename rows/cells
        row_no = 1
        cell_rename = {}
        for el_row in tree.findall(".//ns:row", namespaces={"ns": ns}):
            el_row.attrib["r"] = str(row_no)
            for el_c in el_row:
                old_r = el_c.attrib["r"]
                new_r = get_cell_col(old_r) + str(row_no)
                el_c.attrib["r"] = new_r
                cell_rename[old_r] = new_r
            row_no += 1

        # rename merged cells
        for el in tree.findall(".//ns:mergeCell", namespaces={"ns": ns}):
            old_ref = el.attrib["ref"]
            c1, c2 = old_ref.split(":")
            if c1 in cell_rename and c2 in cell_rename:
                new_ref = cell_rename[c1] + ":" + cell_rename[c2]
                el.attrib["ref"] = new_ref
            else:
                el.getparent().remove(el)
        el = tree.find(".//ns:mergeCells", namespaces={"ns": ns})
        if el is not None:
            if len(el) > 0:
                el.attrib["count"] = str(len(el))
            else:
                el.getparent().remove(el)

        # fix OO bug for column widths
        for el in tree.findall(".//ns:col", namespaces={"ns": ns}):
            if el.attrib.get("width") is not None:
                el.attrib["customWidth"] = "1"

        print("hiding columns...")
        # hide columns
        del_cols = []
        for el in tree.xpath(".//ns:t[text()='[[HIDE_COL]]']",
                             namespaces={"ns": ns}):
            c = el.getparent().getparent()
            r = c.attrib.get("r")
            if not r:
                continue
            col = get_cell_col(r)
            i = ord(col) - ord("A") + 1
            del_cols.append(i)
        del_cols = sorted(list(set(del_cols)))
        if del_cols:
            print("DEL_COLS", del_cols)
            cols_el = tree.find(".//ns:cols", namespaces={"ns": ns})
            for col_el in cols_el:  # XXX: need to be careful if max>min (multi cols in 1 col tag)
                cmin = int(col_el.attrib.get("min", "1"))
                cmax = int(col_el.attrib.get("max", "999"))
                hide = False
                for i in del_cols:
                    if i >= cmin and i <= cmax:
                        hide = True
                        break
                if hide:
                    col_el.attrib["hidden"] = "true"
                    col_el.attrib["width"] = "0"

        print("writing sheet out...")
        sheet_out[sheet_path] = etree.tostring(tree,
                                               pretty_print=True).decode()
        #print("*** SHEET_XML2 **********************")
        # print(sheet_xml[:10000])

    print("updating zip file...")
    f_out = BytesIO()
    zf_out = zipfile.ZipFile(f_out, "w")
    for name in zf_in.namelist():
        if name in sheet_out:
            zf_out.writestr(name, sheet_out[name])
        elif name in images:
            zf_out.writestr(name, images[name])
        elif name == "xl/styles.xml":
            styles_xml = etree.tostring(style_tree, pretty_print=True).decode()
            zf_out.writestr("xl/styles.xml", styles_xml)
        else:
            data = zf_in.read(name)
            zf_out.writestr(name, data)
    zf_out.close()
    zf_in.close()
    data = f_out.getvalue()
    f_out.close()
    return data
Пример #9
0
 def create_email(self, ids, data={}, name_id=None, related_id=None, mailbox_id=None, state=None, context={}):
     print("EmailTemplate.create_email",ids)
     obj = self.browse(ids)[0]
     try:
         from_addr = render_template(obj.from_addr or "", data)
     except:
         raise Exception("Failed to render 'From Address' in template: %s" % obj.name)
     try:
         to_addrs = render_template(obj.to_addrs or "", data)
     except:
         raise Exception("Failed to render 'To Addresses' in template: %s" % obj.name)
     if obj.cc_addrs:
         try:
             cc_addrs = render_template(obj.cc_addrs or "", data)
         except:
             raise Exception("Failed to render 'Cc Addresses' in template: %s" % obj.name)
     else:
             cc_addrs=None
     try:
         subject = render_template(obj.subject, data)
     except:
         raise Exception("Failed to render 'Subject' in template: %s" % obj.name)
     try:
         body = render_template(obj.body.replace("&quot;",'"'), data)
     except:
         raise Exception("Failed to render 'Body' in template: %s" % obj.body)
     if obj.related and not related_id:
         try:
             related_id = render_template(obj.related or "", data)
         except:
             raise Exception("Failed to render 'Related To' in template: %s" % obj.name)
     if obj.contact and not name_id:
         try:
             name_id = render_template(obj.contact or "", data)
         except:
             raise Exception("Failed to render 'Contact' in template: %s" % obj.name)
     else:
         name_id=None
     attachments = []
     if obj.attachments:
         try:
             files = render_template(obj.attachments, data)
             for f in files.split("|"):
                 fname = f.strip()
                 if not fname:
                     continue
                 attach_vals = {
                     "file": fname,
                 }
                 attachments.append(("create", attach_vals))
         except:
             raise Exception("Failed to render 'Attachments' in template: %s" % obj.name)
     vals = {
         "type": "out",
         "date": time.strftime("%Y-%m-%d %H:%M:%S"),
         "from_addr": from_addr,
         "to_addrs": to_addrs,
         "cc_addrs": cc_addrs,
         "subject": subject,
         "body": body,
         "state": state or "to_send",
         "attachments": attachments,
         "name_id": name_id,
         "related_id": related_id,
         "template_id": obj.id,
     }
     print("vals",vals)
     if mailbox_id:
         vals["mailbox_id"] = mailbox_id
     email_id = get_model("email.message").create(vals)
     return email_id
Пример #10
0
 def send_from_template(self, template=None, from_user=None, to_user=None, context={}):
     print("####################################################")
     print("Message.send_from_template", template, from_user, to_user)
     res = get_model("message.tmpl").search([["name", "=", template]])
     if not res:
         raise Exception("Template not found: %s" % template)
     tmpl_id = res[0]
     tmpl = get_model("message.tmpl").browse(tmpl_id)
     try:
         trigger_model = context.get("trigger_model")
         if not trigger_model:
             raise Exception("Missing trigger model")
         print("trigger_model", trigger_model)
         tm = get_model(trigger_model)
         trigger_ids = context.get("trigger_ids")
         if trigger_ids is None:
             raise Exception("Missing trigger ids")
         print("trigger_ids", trigger_ids)
         user_id = get_active_user()
         user = get_model("base.user").browse(user_id)
         for obj in tm.browse(trigger_ids):
             tmpl_ctx = {"obj": obj, "user": user}
             from_user_ = from_user
             if not from_user_:
                 try:
                     from_user_ = render_template(tmpl.from_user or "", tmpl_ctx)
                 except:
                     raise Exception("Error in 'From User': %s" % tmpl.from_user)
             if not from_user_:
                 raise Exception("Missing 'From User'")
             res = get_model("base.user").search([["login", "=", from_user_]])
             if not res:
                 raise Exception("'From User' not found: %s" % from_user_)
             from_id = res[0]
             to_user_ = to_user
             if not to_user_:
                 try:
                     to_user_ = render_template(tmpl.to_user or "", tmpl_ctx)
                 except:
                     raise Exception("Error in 'To User': %s" % tmpl.to_user)
             if not to_user_:
                 raise Exception("Missing 'To User'")
             to_ids = []
             for login in [x.strip() for x in to_user_.split(",")]:
                 res = get_model("base.user").search([["login", "=", login]])
                 if not res:
                     raise Exception("'To User' not found: %s" % login)
                 to_id = res[0]
                 to_ids.append(to_id)
             try:
                 subject = render_template(tmpl.subject, tmpl_ctx)
             except:
                 raise Exception("Error in  'Subject': %s" % tmpl.subject)
             try:
                 body = render_template(tmpl.body, tmpl_ctx)
             except:
                 raise Exception("Error in 'Body': %s" % tmpl.body)
             for to_id in to_ids:
                 vals = {
                     "from_id": from_id,
                     "to_id": to_id,
                     "subject": subject,
                     "body": body,
                 }
                 self.create(vals)
     except Exception as e:
         import traceback
         traceback.print_exc()
         raise Exception("Error in template %s: %s" % (template, e))
Пример #11
0
 def send_from_template(self,
                        template=None,
                        from_user=None,
                        to_user=None,
                        context={}):
     print("####################################################")
     print("Message.send_from_template", template, from_user, to_user)
     res = get_model("message.tmpl").search([["name", "=", template]])
     if not res:
         raise Exception("Template not found: %s" % template)
     tmpl_id = res[0]
     tmpl = get_model("message.tmpl").browse(tmpl_id)
     try:
         trigger_model = context.get("trigger_model")
         if not trigger_model:
             raise Exception("Missing trigger model")
         print("trigger_model", trigger_model)
         tm = get_model(trigger_model)
         trigger_ids = context.get("trigger_ids")
         if trigger_ids is None:
             raise Exception("Missing trigger ids")
         print("trigger_ids", trigger_ids)
         user_id = get_active_user()
         user = get_model("base.user").browse(user_id)
         for obj in tm.browse(trigger_ids):
             tmpl_ctx = {"obj": obj, "user": user}
             from_user_ = from_user
             if not from_user_:
                 try:
                     from_user_ = render_template(tmpl.from_user or "",
                                                  tmpl_ctx)
                 except:
                     raise Exception("Error in 'From User': %s" %
                                     tmpl.from_user)
             if not from_user_:
                 raise Exception("Missing 'From User'")
             res = get_model("base.user").search(
                 [["login", "=", from_user_]])
             if not res:
                 raise Exception("'From User' not found: %s" % from_user_)
             from_id = res[0]
             to_user_ = to_user
             if not to_user_:
                 try:
                     to_user_ = render_template(tmpl.to_user or "",
                                                tmpl_ctx)
                 except:
                     raise Exception("Error in 'To User': %s" %
                                     tmpl.to_user)
             if not to_user_:
                 raise Exception("Missing 'To User'")
             to_ids = []
             for login in [x.strip() for x in to_user_.split(",")]:
                 res = get_model("base.user").search([["login", "=",
                                                       login]])
                 if not res:
                     raise Exception("'To User' not found: %s" % login)
                 to_id = res[0]
                 to_ids.append(to_id)
             try:
                 subject = render_template(tmpl.subject, tmpl_ctx)
             except:
                 raise Exception("Error in  'Subject': %s" % tmpl.subject)
             try:
                 body = render_template(tmpl.body, tmpl_ctx)
             except:
                 raise Exception("Error in 'Body': %s" % tmpl.body)
             for to_id in to_ids:
                 vals = {
                     "from_id": from_id,
                     "to_id": to_id,
                     "subject": subject,
                     "body": body,
                 }
                 self.create(vals)
     except Exception as e:
         import traceback
         traceback.print_exc()
         raise Exception("Error in template %s: %s" % (template, e))
Пример #12
0
def report_render_odt(tmpl_name, data):
    print("REPORT_RENDER_ODT", tmpl_name, data)
    try:
        tmpl_data = get_report_template(tmpl_name, "odt")
    except:
        tmpl_data = get_report_template(tmpl_name, "odt2")  # XXX
    tmpl_f = BytesIO(tmpl_data)
    zf = zipfile.ZipFile(tmpl_f)

    f_out = BytesIO()
    zf_out = zipfile.ZipFile(f_out, "w")
    img_no = 1
    add_files = []
    for tmpl_fname in ("content.xml", "styles.xml"):
        getns = {
            "text": "urn:oasis:names:tc:opendocument:xmlns:text:1.0",
            "draw": "urn:oasis:names:tc:opendocument:xmlns:drawing:1.0",
            "svg-com": "urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0",
            "table": "urn:oasis:names:tc:opendocument:xmlns:table:1.0"
        }
        ns_manifest = "urn:oasis:names:tc:opendocument:xmlns:manifest:1.0"
        ns_svg = "urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0"
        f = zf.open(tmpl_fname)
        tree = etree.parse(f)
        open("/tmp/odt_template_orig.xml", "w").write(etree.tostring(tree, pretty_print=True).decode())

        chunks = get_text_chunks(tree)
        print("#" * 80)
        print("CHUNKS", [c[1] for c in chunks])
        found_expr = False
        expr = None
        expr_pos = []
        for i, (p, t) in enumerate(chunks):
            if i + 1 < len(chunks):
                next_t = chunks[i + 1][1]
            else:
                next_t = None
            if not found_expr:
                if t.find("{{") != -1 or next_t and t[-1] == "{" and next_t[0] == "{":
                    found_expr = True
                    expr = ""
                    expr_pos = []
            if found_expr:
                expr += t
                expr_pos.append(p)
                if expr.find("}}") != -1:
                    for p in expr_pos:
                        set_pos_text(p, "")
                    set_pos_text(expr_pos[0], expr)
                    print("EXPR", expr)
                    if not check_hbs_expression(expr):
                        raise Exception("Invalid expression: '%s'" % expr)
                    found_expr = False

        open("/tmp/odt_template_join.xml", "w").write(etree.tostring(tree, pretty_print=True).decode())

        chunks = get_text_chunks(tree)
        blocks = []
        level = 0
        for p, t in chunks:
            for expr in re.findall("{{[#/].*?}}", t):
                if expr[2] == "#":
                    blocks.append((p, expr, level))
                    level += 1
                elif expr[2] == "/":
                    level -= 1
                    blocks.append((p, expr, level))
        print("#" * 80)
        print("BLOCKS", [(b[1], b[2]) for b in blocks])
        pairs = []
        for i, (p, t, level) in enumerate(blocks):
            if t[2] == "#":
                found = False
                for p2, t2, level2 in blocks[i + 1:]:
                    if level2 == level:
                        pairs.append((p, t, p2, t2))
                        found = True
                        break
                if not found:
                    raise Exception("No closing expression found for %s" % t)
        print("PAIRS", [(t, t2) for p, t, p2, t2 in pairs])
        for p, t, p2, t2 in pairs:
            if p[0] == p2[0]:
                continue
            parent = get_common_parent(p[0], p2[0])
            start = stop = None
            for i, c in enumerate(parent):
                if check_el_contains(c, p[0]):
                    start = i
                if check_el_contains(c, p2[0]):
                    stop = i
            print("relocate pair: '%s' '%s'" % (t, t2))
            print("  parent=%s start=%s stop=%s" % (parent, start, stop))
            if stop > start + 1:
                remove_keep_tail(parent, start)
                remove_keep_tail(parent, stop - 1)
                if start > 0:
                    parent[start - 1].tail = (parent[start - 1].tail or "") + t
                else:
                    parent.text = (parent.text or "") + t
                parent[stop - 2].tail = t2 + (parent[stop - 2].tail or "")

        open("/tmp/odt_template_block.xml", "w").write(etree.tostring(tree, pretty_print=True).decode())

        def _repl(m):
            var = m.group(1)
            return "{{{odt_linebreak %s}}}" % var
        textp = tree.findall(".//*")
        for textel in textp:
            if not textel.text:
                continue
            if textel.text.find("{{") == -1:
                continue
            t = re.sub("{{\s*(\w+)\s*}}", _repl, textel.text)
            if t != textel.text:
                textel.text = t

        doc_tmpl = etree.tostring(tree, pretty_print=True, encoding="unicode")
        # XXX sometimes they're found as "”" instead of &#8221;
        doc_tmpl = doc_tmpl.replace("“", "\"")
        doc_tmpl = doc_tmpl.replace("”", "\"")
        doc_tmpl = doc_tmpl.replace("&#8220;", "\"")
        doc_tmpl = doc_tmpl.replace("&#8221;", "\"")

        open("/tmp/odt_template_use.xml", "w").write(doc_tmpl)
        odt_xml = template.render_template(doc_tmpl, data)  # XXX
        open("/tmp/odt_render_out.xml", "w").write(odt_xml)

        tree = etree.fromstring(odt_xml)
        for frame_el in tree.findall(".//draw:frame", namespaces={"draw": getns["draw"]}):
            title_el = frame_el.find("svg:title", namespaces={"svg": ns_svg})
            if title_el is not None:
                fname = title_el.text
            else:
                fname = None
            if not fname:
                continue
            img_path = utils.get_file_path(fname)
            if not os.path.exists(img_path):  # XXX
                continue
            new_zip_path = "Pictures/_img%d.png" % img_no
            img_no += 1
            image_el = frame_el[0]
            image_el.attrib['{http://www.w3.org/1999/xlink}href'] = new_zip_path
            add_files.append((new_zip_path, img_path, "image/png"))
            cx = frame_el.attrib["{%s}width" % getns["svg-com"]]
            cy = frame_el.attrib["{%s}height" % getns["svg-com"]]
            cx_unit = cx[-2] + cx[-1]
            cx = cx[:-2]
            cy_unit = cy[-2] + cy[-1]
            cy = cy[:-2]
            img = Image.open(img_path)
            w, h = img.size
            scale = min(float(cx) / w, float(cy) / h)
            cx2 = w * scale
            cy2 = h * scale
            frame_el.attrib["{%s}width" % getns["svg-com"]] = str(cx2) + cx_unit
            frame_el.attrib["{%s}height" % getns["svg-com"]] = str(cy2) + cy_unit

        odt_xml = etree.tostring(tree, pretty_print=True, encoding="unicode")
        zf_out.writestr(tmpl_fname, odt_xml)

    print("add_files", add_files)

    f = zf.open("META-INF/manifest.xml")
    tree = etree.parse(f)
    root = tree.getroot()
    for zip_path, img_path, media_type in add_files:
        el = etree.Element("{%s}file-entry" % ns_manifest)
        el.attrib["{%s}full-path" % ns_manifest] = zip_path
        el.attrib["{%s}media-type" % ns_manifest] = media_type
        root.append(el)
    manif_xml = etree.tostring(tree, pretty_print=True, encoding="unicode")
    zf_out.writestr("META-INF/manifest.xml", manif_xml)

    out_names = set(zf_out.namelist())
    for name in zf.namelist():
        if name in out_names:
            continue
        data = zf.read(name)
        zf_out.writestr(name, data)
    for zip_path, img_path, media_type in add_files:
        img_data = open(img_path, "rb").read()
        zf_out.writestr(zip_path, img_data)
    zf.close()
    zf_out.close()
    data = f_out.getvalue()
    f_out.close()
    return data
Пример #13
0
def report_render_doc(tmpl_name, data):  # Do they delete the newline thing
    print("report_render_doc", tmpl_name, data)
    tmpl_data = get_report_template(tmpl_name, "docx")
    tmpl_f = BytesIO(tmpl_data)
    zf = zipfile.ZipFile(tmpl_f)

    ns = "http://schemas.openxmlformats.org/package/2006/relationships"
    f = zf.open("word/_rels/document.xml.rels")
    tree = etree.parse(f)
    rels = {}
    for el in tree.findall(".//ns:Relationship", namespaces={"ns": ns}):
        rels[el.attrib.get("Id")] = el.attrib.get("Target")
    print("rels", rels)

    ns = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
    f = zf.open("word/document.xml")
    tree = etree.parse(f)
    print("*** DOC_SRC *********************")
    print(etree.tostring(tree, pretty_print=True).decode())

    for el in tree.iterfind(".//w:p", namespaces={"w": ns}):
        vals = []
        start = False
        parent = el
        children = el.findall(".//w:r", namespaces={"w": ns})
        vals = []
        for child in children:
            grandchild = child.findall(".//w:t", namespaces={"w": ns})
            if grandchild and grandchild[0].text:
                text = grandchild[0].text
                if text and (text.find("{{") != -1 or text.find("}}") != -1 or start):
                    vals.append((text, child, grandchild[0]))
                    start = False if text.find("}}") != -1 and not (text.find("{{") != -1) else True
        text = "".join([text for text, c, g in vals])
        for i, val in enumerate(vals):
            if i == 0:
                val[2].text = text
            else:
                parent.remove(val[1])

    images = {}
    change_list = []
    change_list.append("word/document.xml")
    for el in tree.iterfind(".//w:tc", namespaces={"w": ns}):
        expr = el.xpath("string()")
        if expr and expr.find("{{image") != -1:
            expr = expr.replace("\u201c", "\"")  # XXX
            expr = expr.replace("\u201d", "\"")
            m = re.search("{{image \"(.*?)\"", expr)
            if not m:
                raise Exception("Failed to parse image expression: %s" % expr)
            n = m.group(1)
            v = data.get(n)
            if v:
                res = el.findall(".//w:drawing", namespaces={"w": ns})
                if not res:
                    raise Exception("Failed to replace image")
                el_drawing = res[0]
                el_blip = el_drawing.find(
                    ".//a:blip", namespaces={"a": "http://schemas.openxmlformats.org/drawingml/2006/main"})
                embed = el_blip.attrib["{http://schemas.openxmlformats.org/officeDocument/2006/relationships}embed"]
                zip_path = rels[embed]
                change_list.append("word/" + zip_path)
                images[zip_path] = v
                el_extent = el_drawing.find(
                    ".//wp:extent", namespaces={"wp": "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing"})
                cx = int(el_extent.attrib["cx"])
                cy = int(el_extent.attrib["cy"])
                img_path = utils.get_file_path(v)
                img = Image.open(img_path)
                w, h = img.size
                scale = min(float(cx) / w, float(cy) / h)
                cx2 = int(round(w * scale))
                cy2 = int(round(h * scale))
                el_extent.attrib["cx"] = str(cx2)
                el_extent.attrib["cy"] = str(cy2)
                el_ext = el_drawing.find(
                    ".//a:ext", namespaces={"a": "http://schemas.openxmlformats.org/drawingml/2006/main"})
                el_ext.attrib["cx"] = str(cx2)
                el_ext.attrib["cy"] = str(cy2)
            wts = el.findall(".//w:t", namespaces={"w": ns})
            for wt in wts:
                if wt.text and wt.text.find("{{image") != -1:
                    parent = wt.getparent()
                    grandparent = parent.getparent()
                    grandparent.remove(parent)

    for el in tree.iterfind(".//w:t", namespaces={"w": ns}):
        expr = el.text
        if expr.find("{{#") != -1 or expr.find("{{/") != -1:
            el_tr = el.getparent()
            while not el_tr.tag.endswith("}tr"):
                el_tr = el_tr.getparent()
            p = el_tr.getparent()
            # XXX
            if p:
                i = p.index(el_tr)
                p.remove(el_tr)
                p[i - 1].tail = expr

    doc_tmpl = etree.tostring(tree, pretty_print=True, encoding="unicode")
    doc_tmpl = doc_tmpl.replace("&#8220;", "\"")
    doc_tmpl = doc_tmpl.replace("&#8221;", "\"")
    doc_tmpl = doc_tmpl.replace("\u201c", "\"")
    doc_tmpl = doc_tmpl.replace("\u201d", "\"")
    print("*** DOC_TMPL *********************")
    for i, l in enumerate(doc_tmpl.split("\n")):
        print(i + 1, l)
    doc_xml = template.render_template(doc_tmpl, data)
    f_out = BytesIO()
    zf_out = zipfile.ZipFile(f_out, "w")
    for name in zf.namelist():
        print("XXX", name)
        if name in change_list:
            pass
        else:
            data = zf.read(name)
            zf_out.writestr(name, data)
    for zip_path, v in images.items():
        img_path = utils.get_file_path(v)
        img_data = open(img_path, "rb").read()
        zf_out.writestr("word/" + zip_path, img_data)
    zf_out.writestr("word/document.xml", doc_xml)
    zf.close()
    zf_out.close()
    data = f_out.getvalue()
    f_out.close()
    return data
Пример #14
0
def report_render_xls(tmpl_name, data, fast_render=False):
    print("report_render_xls", tmpl_name, data)
    tmpl_data = get_report_template(tmpl_name, "xlsx")
    tmpl_f = BytesIO(tmpl_data)
    zf_in = zipfile.ZipFile(tmpl_f)
    ns = "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
    nsd = "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing"
    nsr = "http://schemas.openxmlformats.org/package/2006/relationships"
    nsd2 = "http://schemas.openxmlformats.org/drawingml/2006/main"
    nsr2 = "http://schemas.openxmlformats.org/officeDocument/2006/relationships"

    f = zf_in.open("xl/sharedStrings.xml")
    tree = etree.parse(f)
    f.close()
    strings = []
    for el in tree.findall(".//ns:si", namespaces={"ns": ns}):
        strings.append(el[0].text)
    print("strings", strings)

    f = zf_in.open("xl/styles.xml")
    style_tree = etree.parse(f)
    f.close()
    el_fmt = etree.Element("numFmt")
    el_fmt.attrib["formatCode"] = "#,##0.00_);\(#,##0.00\)"
    el_fmt.attrib["numFmtId"] = "999"  # XXX
    el_p = style_tree.find(".//ns:numFmts", namespaces={"ns": ns})
    if el_p:
        el_p.append(el_fmt)
        el_p.attrib["count"] = str(int(el_p.attrib["count"]) + 1)
    else:
        el_p = etree.Element("numFmts")
        style_tree.getroot().insert(0, el_p)
        el_p.append(el_fmt)
        el_p.attrib["count"] = "1"

    currency_styles = {}

    def _make_currency_style(old_style):
        if old_style in currency_styles:
            return currency_styles[old_style]
        el_p = style_tree.find(".//ns:cellXfs", namespaces={"ns": ns})
        if el_p is None:
            raise Exception("Cell styles not found!")
        if old_style:
            el_old = el_p[old_style]
            el_new = deepcopy(el_old)
        else:
            el_new = etree.Element("xf")
            el_new.attrib["xfId"] = "0"  # XXX
        el_new.attrib["numFmtId"] = "999"
        el_p.append(el_new)
        new_style = int(el_p.attrib["count"])
        currency_styles[old_style] = new_style
        el_p.attrib["count"] = str(new_style + 1)
        return new_style

    drawing_no = 0
    images = {}
    while True:
        drawing_no += 1
        drawing_path = "xl/drawings/drawing%s.xml" % drawing_no
        try:
            f = zf_in.open(drawing_path)
        except:
            break
        print("processing drawing %s" % drawing_no)
        tree = etree.parse(f)
        f.close()
        el = tree.find(".//ns:cNvPr", namespaces={"ns": nsd})
        expr = el.attrib.get("name")
        if not expr:
            continue
        fname = template.render_template(expr, data).strip()
        print("drawing fname", fname)
        if not fname:
            continue

        f = zf_in.open("xl/drawings/_rels/drawing%s.xml.rels" % drawing_no)
        rel_tree = etree.parse(f)
        f.close()
        rels = {}
        for el in rel_tree.findall(".//ns:Relationship", namespaces={"ns": nsr}):
            rels[el.attrib.get("Id")] = el.attrib.get("Target")

        el_blip = tree.find(".//ns:blip", namespaces={"ns": nsd2})
        embed = el_blip.attrib["{%s}embed" % nsr2]
        zip_path = rels[embed].replace("..", "xl")
        print("zip_path", zip_path)

        img_data = open(fname, "rb").read()
        images[zip_path] = img_data

    sheet_no = 0
    sheet_out = {}
    while True:
        sheet_no += 1
        sheet_path = "xl/worksheets/sheet%s.xml" % sheet_no
        try:
            f = zf_in.open(sheet_path)
        except:
            break
        print("processing sheet %s" % sheet_no)
        print("converting to inline strings...")
        tree = etree.parse(f)
        f.close()
        for el in tree.findall(".//ns:c[@t='s']", namespaces={"ns": ns}):
            i = int(el[0].text)
            s = strings[i]
            if not s:
                continue
            if s.find("{{currency") != -1:
                el.attrib["t"] = "n"
                if "s" in el.attrib:
                    old_style = int(el.attrib["s"])
                else:
                    old_style = None
                new_style = _make_currency_style(old_style)
                el.attrib["s"] = str(new_style)

                def _repl(m):
                    return "{{currency %s nogroup=1}}" % m.group(1)
                el[0].text = re.sub(r"{{currency (.*?)}}", _repl, s)
            else:
                el.remove(el[0])
                el.attrib["t"] = "inlineStr"
                el_t = etree.Element("t")
                el_t.text = s
                el_is = etree.Element("is")
                el_is.append(el_t)
                el.append(el_is)

        #print("*** SHEET_SRC *********************")
        # print(etree.tostring(tree,pretty_print=True).decode()[:10000])

        print("renaming template expressions...")
        for el in tree.findall(".//t"):
            s = el.text
            if s.find("{{#") == -1 and s.find("{{/") == -1:
                continue
            s = s.replace("\u201c", "\"")  # XXX
            s = s.replace("\u201d", "\"")
            # print("XXX",s)
            col = el.getparent().getparent()
            row = col.getparent()
            p = row.getparent()
            if get_cell_col(col.attrib["r"]) == "A":
                i = p.index(row)
                p.remove(row)
                if i == 0:
                    p.text = (p.text or "") + "\n" + s + "\n"
                else:
                    p[i - 1].tail = (p[i - 1].tail or "") + "\n" + s + "\n"
            else:
                if s.find("{{#") != -1 and s.find("{{/") == -1:
                    col_no = get_cell_col(col.attrib["r"])
                    m = re.search("{{#each (.*?)}}", s)
                    if not m:
                        raise Exception("Invalid 'each' expression")
                    list_name = m.group(1)
                    i = row.index(col)
                    if i == 0:
                        row.text = "\n{{#first %s}}\n" % list_name
                    else:
                        row[i - 1].tail = "\n{{#first %s}}\n" % list_name
                    for col2 in row:
                        if get_cell_col(col2.attrib["r"]) >= col_no:
                            row.remove(col2)
                    next_row = row.getnext()
                    for next_col in next_row:
                        if get_cell_col(next_col.attrib["r"]) >= col_no:
                            col2 = deepcopy(next_col)
                            row.append(col2)
                    row[-1].tail = "\n{{/first}}\n"
                    row.tail = "\n{{#after_first %s}}\n" % list_name
                elif s.find("{{#") == -1 and s.find("{{/") != -1:
                    i = p.index(row)
                    p[i - 1].tail = "\n{{/after_first}}\n"
                    p.remove(row)

        sheet_tmpl = etree.tostring(tree, pretty_print=True).decode()
        sheet_tmpl = sheet_tmpl.replace("&#8220;", "\"")
        sheet_tmpl = sheet_tmpl.replace("&#8221;", "\"")
        #print("*** SHEET_TMPL *********************")
        # print(sheet_tmpl[:10000])
        print("template size: %s chars" % len(sheet_tmpl))
        print("number of pieces: %d" % len(sheet_tmpl.split("{{")))
        print("rendering template...")
        if fast_render:  # FIXME; remove need for this!!! (make new compiler faster)
            sheet_xml = template.render_template_old(sheet_tmpl, data)
        else:
            sheet_xml = template.render_template(sheet_tmpl, data)
        #print("*** SHEET_XML1 **********************")
        # print(sheet_xml[:10000])

        print("parsing rendered tree...")
        tree = etree.fromstring(sheet_xml)

        print("renaming cells...")
        # rename rows/cells
        row_no = 1
        cell_rename = {}
        for el_row in tree.findall(".//ns:row", namespaces={"ns": ns}):
            el_row.attrib["r"] = str(row_no)
            for el_c in el_row:
                old_r = el_c.attrib["r"]
                new_r = get_cell_col(old_r) + str(row_no)
                el_c.attrib["r"] = new_r
                cell_rename[old_r] = new_r
            row_no += 1

        # rename merged cells
        for el in tree.findall(".//ns:mergeCell", namespaces={"ns": ns}):
            old_ref = el.attrib["ref"]
            c1, c2 = old_ref.split(":")
            if c1 in cell_rename and c2 in cell_rename:
                new_ref = cell_rename[c1] + ":" + cell_rename[c2]
                el.attrib["ref"] = new_ref
            else:
                el.getparent().remove(el)
        el = tree.find(".//ns:mergeCells", namespaces={"ns": ns})
        if el is not None:
            if len(el) > 0:
                el.attrib["count"] = str(len(el))
            else:
                el.getparent().remove(el)

        # fix OO bug for column widths
        for el in tree.findall(".//ns:col", namespaces={"ns": ns}):
            if el.attrib.get("width") is not None:
                el.attrib["customWidth"] = "1"

        print("hiding columns...")
        # hide columns
        del_cols = []
        for el in tree.xpath(".//ns:t[text()='[[HIDE_COL]]']", namespaces={"ns": ns}):
            c = el.getparent().getparent()
            r = c.attrib.get("r")
            if not r:
                continue
            col = get_cell_col(r)
            i = ord(col) - ord("A") + 1
            del_cols.append(i)
        del_cols = sorted(list(set(del_cols)))
        if del_cols:
            print("DEL_COLS", del_cols)
            cols_el = tree.find(".//ns:cols", namespaces={"ns": ns})
            for col_el in cols_el:  # XXX: need to be careful if max>min (multi cols in 1 col tag)
                cmin = int(col_el.attrib.get("min", "1"))
                cmax = int(col_el.attrib.get("max", "999"))
                hide = False
                for i in del_cols:
                    if i >= cmin and i <= cmax:
                        hide = True
                        break
                if hide:
                    col_el.attrib["hidden"] = "true"
                    col_el.attrib["width"] = "0"

        print("writing sheet out...")
        sheet_out[sheet_path] = etree.tostring(tree, pretty_print=True).decode()
        #print("*** SHEET_XML2 **********************")
        # print(sheet_xml[:10000])

    print("updating zip file...")
    f_out = BytesIO()
    zf_out = zipfile.ZipFile(f_out, "w")
    for name in zf_in.namelist():
        if name in sheet_out:
            zf_out.writestr(name, sheet_out[name])
        elif name in images:
            zf_out.writestr(name, images[name])
        elif name == "xl/styles.xml":
            styles_xml = etree.tostring(style_tree, pretty_print=True).decode()
            zf_out.writestr("xl/styles.xml", styles_xml)
        else:
            data = zf_in.read(name)
            zf_out.writestr(name, data)
    zf_out.close()
    zf_in.close()
    data = f_out.getvalue()
    f_out.close()
    return data