#!/usr/bin/env python3 """Render resume.yaml -> MikeEberlein_Resume.odt. Builds the ODT zip from scratch (mimetype, manifest, meta, styles, content). The ODT styling lives inline as STYLES below; content comes from resume.yaml. DOCX is produced by piping this output through LibreOffice: soffice --headless --convert-to docx MikeEberlein_Resume.odt """ import os import shutil import sys import yaml import zipfile HERE = os.path.dirname(os.path.abspath(__file__)) DATA = os.path.join(HERE, "resume.yaml") OUT = os.path.join(HERE, "MikeEberlein_Resume.odt") MIMETYPE = "application/vnd.oasis.opendocument.text" MANIFEST = ''' ''' META = ''' Mike Eberlein Resumerender_odt.py ''' STYLES = ''' ''' CONTENT_HEAD = ''' ''' CONTENT_TAIL = '' # ---------- XML helpers ---------- XML_ESCAPES = {"&": "&", "<": "<", ">": ">"} def xesc(s: str) -> str: return "".join(XML_ESCAPES.get(c, c) for c in s) def p(style, content): return f'{content}' def b(text): return f'{xesc(text)}' def i(text): return f'{xesc(text)}' TAB = '' def bullet(text, style="Bullet"): return (f'' f'{xesc(text)}' f'') def render(data) -> str: parts = [] h = data["header"] parts.append(p("Name", xesc(h["name"]))) contact = f'{xesc(h["tagline"])} • {xesc(h["email"])}' parts.append(p("Contact", contact)) parts.append(p("Summary", xesc(data["summary"].strip()))) parts.append(p("SectionHead", "EXPERIENCE")) for company in data["experience"]: parts.append(f'{b(company["company"])}{TAB}{xesc(company["dates"])}') for role in company["roles"]: parts.append(f'{i(role["title"])}{TAB}{i(role["dates"])}') if "subroles" in role: for sr in role["subroles"]: parts.append(f'{xesc(sr["title"])}{TAB}{i(sr["dates"])}') for blt in sr["bullets"]: parts.append(bullet(blt, style="SubBullet")) elif "bullets" in role: for blt in role["bullets"]: parts.append(bullet(blt, style="Bullet")) parts.append(p("SectionHead", "EDUCATION")) for ed in data["education"]: parts.append(f'{b(ed["school"])}{TAB}{xesc(ed["dates"])}') for blt in ed["bullets"]: parts.append(bullet(blt, style="Bullet")) parts.append(p("SectionHead", "CERTIFICATIONS")) parts.append(p("Standard", " • ".join(xesc(c) for c in data["certifications"]))) return CONTENT_HEAD + "".join(parts) + CONTENT_TAIL def main(): with open(DATA, "r", encoding="utf-8") as f: data = yaml.safe_load(f) content = render(data) with zipfile.ZipFile(OUT, "w", zipfile.ZIP_DEFLATED) as z: zi = zipfile.ZipInfo("mimetype") zi.compress_type = zipfile.ZIP_STORED z.writestr(zi, MIMETYPE) z.writestr("META-INF/manifest.xml", MANIFEST) z.writestr("meta.xml", META) z.writestr("styles.xml", STYLES) z.writestr("content.xml", content) print(f"wrote {OUT}") if __name__ == "__main__": main()