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
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
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
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)
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)
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 ” doc_tmpl = doc_tmpl.replace("“", "\"") doc_tmpl = doc_tmpl.replace("”", "\"") doc_tmpl = doc_tmpl.replace("“", "\"") doc_tmpl = doc_tmpl.replace("”", "\"") 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
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("“", "\"") doc_tmpl = doc_tmpl.replace("”", "\"") 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
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("“", "\"") sheet_tmpl = sheet_tmpl.replace("”", "\"") #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
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(""",'"'), 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
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))
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))
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 ” doc_tmpl = doc_tmpl.replace("“", "\"") doc_tmpl = doc_tmpl.replace("”", "\"") doc_tmpl = doc_tmpl.replace("“", "\"") doc_tmpl = doc_tmpl.replace("”", "\"") 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
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("“", "\"") doc_tmpl = doc_tmpl.replace("”", "\"") 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