Buckets:
| """Render results/report/REPORT.md (a small Markdown dialect) to results/report/decision_model_report.pdf. | |
| Dialect: '# ' title (first line), '## ' section, '### ' subsection, paragraphs, '- ' bullets, '1. ' numbered items, | |
| pipe tables, '> ' callout lines, '' figures from results/report_figs/<slug>.png, '---pagebreak---', | |
| '**bold**', '*italic*', '`code`'. Run with the report venv (reportlab + matplotlib's DejaVu fonts). | |
| """ | |
| from __future__ import annotations | |
| import glob | |
| import os | |
| import re | |
| import sys | |
| import matplotlib | |
| from reportlab.lib import colors | |
| from reportlab.lib.enums import TA_LEFT | |
| from reportlab.lib.pagesizes import A4 | |
| from reportlab.lib.styles import ParagraphStyle | |
| from reportlab.lib.units import cm | |
| from reportlab.pdfbase import pdfmetrics | |
| from reportlab.pdfbase.ttfonts import TTFont | |
| from reportlab.platypus import (Image, KeepTogether, ListFlowable, ListItem, PageBreak, Paragraph, Spacer, Table, | |
| TableStyle) | |
| from reportlab.platypus.doctemplate import BaseDocTemplate, PageTemplate | |
| from reportlab.platypus.frames import Frame | |
| from reportlab.platypus.tableofcontents import TableOfContents | |
| ROOT = "/workspace/decision-reasoner" | |
| SRC = sys.argv[1] if len(sys.argv) > 1 else f"{ROOT}/results/report/REPORT.md" | |
| OUT = sys.argv[2] if len(sys.argv) > 2 else f"{ROOT}/results/report/decision_model_report.pdf" | |
| FIGS = f"{ROOT}/results/report_figs" | |
| TTF = os.path.join(os.path.dirname(matplotlib.__file__), "mpl-data", "fonts", "ttf") | |
| for name, f in (("Sans", "DejaVuSans.ttf"), ("Sans-Bold", "DejaVuSans-Bold.ttf"), ("Sans-Italic", "DejaVuSans-Oblique.ttf"), | |
| ("Sans-BoldItalic", "DejaVuSans-BoldOblique.ttf"), ("Mono", "DejaVuSansMono.ttf")): | |
| pdfmetrics.registerFont(TTFont(name, os.path.join(TTF, f))) | |
| pdfmetrics.registerFontFamily("Sans", normal="Sans", bold="Sans-Bold", italic="Sans-Italic", boldItalic="Sans-BoldItalic") | |
| INK, MUTED, RULE, ACCENT, SOFT = (colors.HexColor(c) for c in ("#111827", "#6b7280", "#e5e7eb", "#2563eb", "#eff6ff")) | |
| BASE = ParagraphStyle("base", fontName="Sans", fontSize=9.4, leading=13.4, textColor=INK, alignment=TA_LEFT, spaceAfter=5) | |
| S = { | |
| "title": ParagraphStyle("title", parent=BASE, fontName="Sans-Bold", fontSize=22, leading=27, spaceAfter=8), | |
| "subtitle": ParagraphStyle("subtitle", parent=BASE, fontSize=11.5, leading=16, textColor=MUTED, spaceAfter=14), | |
| "h2": ParagraphStyle("h2", parent=BASE, fontName="Sans-Bold", fontSize=14.5, leading=19, spaceBefore=16, spaceAfter=6, keepWithNext=1), | |
| "h3": ParagraphStyle("h3", parent=BASE, fontName="Sans-Bold", fontSize=11, leading=15, spaceBefore=9, spaceAfter=4, keepWithNext=1), | |
| "body": BASE, | |
| "bullet": ParagraphStyle("bullet", parent=BASE, spaceAfter=2.5), | |
| "cell": ParagraphStyle("cell", parent=BASE, fontSize=8.1, leading=10.4, spaceAfter=0), | |
| "cellh": ParagraphStyle("cellh", parent=BASE, fontName="Sans-Bold", fontSize=8.1, leading=10.4, spaceAfter=0), | |
| "caption": ParagraphStyle("caption", parent=BASE, fontName="Sans-Italic", fontSize=8.2, leading=11, textColor=MUTED, spaceAfter=10), | |
| "callout": ParagraphStyle("callout", parent=BASE, fontSize=9.6, leading=13.6, spaceAfter=0), | |
| "tochead": ParagraphStyle("tochead", parent=BASE, fontName="Sans-Bold", fontSize=11, leading=15, spaceBefore=4, spaceAfter=6), | |
| "toc1": ParagraphStyle("toc1", parent=BASE, fontSize=10, leading=15, leftIndent=0), | |
| "toc2": ParagraphStyle("toc2", parent=BASE, fontSize=9, leading=12.5, leftIndent=14, textColor=MUTED), | |
| } | |
| def inline(t: str) -> str: | |
| t = t.replace("&", "&").replace("<", "<").replace(">", ">") | |
| t = re.sub(r"`([^`]+)`", r'<font name="Mono" size="8.3">\1</font>', t) | |
| t = re.sub(r"\*\*(.+?)\*\*", r"<b>\1</b>", t) | |
| t = re.sub(r"(?<![\w*])\*(?!\s)(.+?)(?<!\s)\*(?![\w*])", r"<i>\1</i>", t) | |
| t = re.sub(r"\[([^\]]+)\]\((https?://[^)]+)\)", r'<link href="\2" color="#2563eb">\1</link>', t) | |
| return t | |
| class Doc(BaseDocTemplate): | |
| def __init__(self, path, title): | |
| super().__init__(path, pagesize=A4, leftMargin=2 * cm, rightMargin=2 * cm, topMargin=2 * cm, bottomMargin=2 * cm, | |
| title=title, author="Claude (Anthropic), for the decision-model project") | |
| frame = Frame(self.leftMargin, self.bottomMargin, self.width, self.height, id="f") | |
| self.addPageTemplates([PageTemplate(id="p", frames=[frame], onPage=self.decorate)]) | |
| self.short = title | |
| def decorate(self, canv, doc): | |
| if doc.page == 1: | |
| return | |
| canv.saveState() | |
| canv.setFont("Sans", 7.5) | |
| canv.setFillColor(MUTED) | |
| canv.drawString(2 * cm, A4[1] - 1.3 * cm, self.short) | |
| canv.drawRightString(A4[0] - 2 * cm, 1.2 * cm, str(doc.page)) | |
| canv.setStrokeColor(RULE) | |
| canv.line(2 * cm, A4[1] - 1.45 * cm, A4[0] - 2 * cm, A4[1] - 1.45 * cm) | |
| canv.restoreState() | |
| def afterFlowable(self, f): | |
| if isinstance(f, Paragraph) and f.style.name in ("h2", "h3"): | |
| level = 0 if f.style.name == "h2" else 1 | |
| text = f.getPlainText() | |
| key = f"h{id(f)}" | |
| self.canv.bookmarkPage(key) | |
| self.canv.addOutlineEntry(text, key, level=level, closed=level > 0) | |
| if level == 0: | |
| self.notify("TOCEntry", (level, text, self.page, key)) | |
| def table(rows: list[list[str]], width: float) -> Table: | |
| ncol = len(rows[0]) | |
| data = [[Paragraph(inline(c), S["cellh"] if i == 0 else S["cell"]) for c in r] for i, r in enumerate(rows)] | |
| # column widths: every column at least as wide as its longest word, the rest of the width shared by text length | |
| plain = lambda x: re.sub(r"[*`]", "", x) | |
| sw = lambda x, bold: pdfmetrics.stringWidth(x, "Sans-Bold" if bold else "Sans", 8.1) + 9 | |
| minw = [max(sw(w, i == 0) for i, r in enumerate(rows) for w in (plain(r[j]).split() or [""])) for j in range(ncol)] | |
| pref = [max(sw(plain(r[j]), i == 0) for i, r in enumerate(rows)) for j in range(ncol)] | |
| if sum(minw) >= width: | |
| widths = [width * m / sum(minw) for m in minw] | |
| else: | |
| extra = [max(p - m, 0) for p, m in zip(pref, minw)] | |
| room = width - sum(minw) | |
| widths = [m + (room * e / sum(extra) if sum(extra) else room / ncol) for m, e in zip(minw, extra)] | |
| widths = [min(w, p) for w, p in zip(widths, pref)] | |
| if sum(widths) < width: # all fit on one line: spread the slack evenly | |
| widths = [w + (width - sum(widths)) / ncol for w in widths] | |
| t = Table(data, colWidths=widths, repeatRows=1, hAlign="LEFT") | |
| t.setStyle(TableStyle([ | |
| ("LINEBELOW", (0, 0), (-1, 0), 0.8, INK), ("LINEBELOW", (0, 1), (-1, -1), 0.3, RULE), | |
| ("VALIGN", (0, 0), (-1, -1), "TOP"), ("TOPPADDING", (0, 0), (-1, -1), 3), ("BOTTOMPADDING", (0, 0), (-1, -1), 3), | |
| ("LEFTPADDING", (0, 0), (-1, -1), 4), ("RIGHTPADDING", (0, 0), (-1, -1), 4), | |
| ])) | |
| return t | |
| CAPTIONS = {} | |
| for _f in glob.glob(f"{FIGS}/manifest_*.json"): | |
| import json as _json | |
| for _m in _json.load(open(_f)): | |
| CAPTIONS[_m["slug"]] = _m["caption"] | |
| def figure(slug: str, caption: str, width: float, n: int): | |
| caption = caption or CAPTIONS.get(slug, "") | |
| path = f"{FIGS}/{slug}.png" | |
| if not os.path.exists(path): | |
| return [Paragraph(f"[missing figure: {slug}]", S["caption"])] | |
| from reportlab.lib.utils import ImageReader | |
| iw, ih = ImageReader(path).getSize() | |
| w = width | |
| h = w * ih / iw | |
| if h > 15 * cm: # keep tall figures on one page | |
| h = 15 * cm | |
| w = h * iw / ih | |
| return [KeepTogether([Image(path, width=w, height=h), Spacer(1, 3), | |
| Paragraph(f"<b>Figure {n}.</b> " + inline(caption), S["caption"])])] | |
| def callout(lines: list[str], width: float) -> Table: | |
| t = Table([[Paragraph("<br/>".join(inline(x) for x in lines), S["callout"])]], colWidths=[width]) | |
| t.setStyle(TableStyle([("BACKGROUND", (0, 0), (-1, -1), SOFT), ("LINEBEFORE", (0, 0), (0, -1), 2.5, ACCENT), | |
| ("LEFTPADDING", (0, 0), (-1, -1), 9), ("RIGHTPADDING", (0, 0), (-1, -1), 9), | |
| ("TOPPADDING", (0, 0), (-1, -1), 7), ("BOTTOMPADDING", (0, 0), (-1, -1), 7)])) | |
| return t | |
| def build() -> None: | |
| lines = open(SRC, encoding="utf-8").read().splitlines() | |
| title = lines[0].lstrip("# ").strip() | |
| doc = Doc(OUT, title) | |
| W = doc.width | |
| story, para, i, nfig = [], [], 1, 0 | |
| def flush(): | |
| nonlocal para | |
| if para: | |
| story.append(Paragraph(inline(" ".join(para)), S["body"])) | |
| para = [] | |
| story.append(Spacer(1, 3.2 * cm)) | |
| story.append(Paragraph(inline(title), S["title"])) | |
| while i < len(lines): | |
| ln = lines[i] | |
| s = ln.strip() | |
| if s.startswith("%subtitle "): | |
| story.append(Paragraph(inline(s[10:]), S["subtitle"])); i += 1; continue | |
| if s == "%toc": | |
| flush() | |
| toc = TableOfContents() | |
| toc.levelStyles = [S["toc1"], S["toc2"]] | |
| story += [Paragraph("Contents", S["tochead"]), toc] | |
| i += 1; continue | |
| if s == "---pagebreak---": | |
| flush(); story.append(PageBreak()); i += 1; continue | |
| if not s: | |
| flush(); i += 1; continue | |
| if s.startswith("## "): | |
| flush(); story.append(Paragraph(inline(s[3:]), S["h2"])); i += 1; continue | |
| if s.startswith("### "): | |
| flush(); story.append(Paragraph(inline(s[4:]), S["h3"])); i += 1; continue | |
| m = re.match(r"!\[([\w\-]+)\]\((.*)\)$", s) | |
| if m: | |
| flush(); nfig += 1; story += figure(m.group(1), m.group(2), W, nfig); i += 1; continue | |
| if s.startswith(">"): | |
| flush(); block = [] | |
| while i < len(lines) and lines[i].strip().startswith(">"): | |
| block.append(lines[i].strip()[1:].strip()); i += 1 | |
| story += [callout(block, W), Spacer(1, 8)]; continue | |
| if s.startswith("|"): | |
| flush(); rows = [] | |
| while i < len(lines) and lines[i].strip().startswith("|"): | |
| cells = [c.strip() for c in lines[i].strip().strip("|").split("|")] | |
| if not all(re.fullmatch(r":?-{2,}:?", c) for c in cells): | |
| rows.append(cells) | |
| i += 1 | |
| story += [table(rows, W), Spacer(1, 8)]; continue | |
| if re.match(r"(- |\d+\. )", s): | |
| flush(); items, numbered = [], bool(re.match(r"\d+\. ", s)) | |
| while i < len(lines) and re.match(r"\s*(- |\d+\. )", lines[i]): | |
| txt = re.sub(r"^\s*(- |\d+\. )", "", lines[i]); i += 1 | |
| while i < len(lines) and lines[i].startswith(" ") and not re.match(r"\s*(- |\d+\. )", lines[i]) and lines[i].strip(): | |
| txt += " " + lines[i].strip(); i += 1 | |
| items.append(ListItem(Paragraph(inline(txt), S["bullet"]), leftIndent=12)) | |
| story += [ListFlowable(items, bulletType="1" if numbered else "bullet", start="1" if numbered else None, | |
| bulletFontName="Sans", bulletFontSize=8 if not numbered else 9, leftIndent=12, | |
| bulletColor=MUTED if not numbered else INK), Spacer(1, 4)] | |
| continue | |
| para.append(s); i += 1 | |
| flush() | |
| doc.multiBuild(story) | |
| print(f"wrote {OUT} ({os.path.getsize(OUT) / 1e6:.2f} MB), {nfig} figures") | |
| if __name__ == "__main__": | |
| build() | |
Xet Storage Details
- Size:
- 11.4 kB
- Xet hash:
- e96feb506a3c6fdc3429a5255bc1edfc21430b28e7694c459d5b00d4789844e3
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.