Spaces:
Running
Running
Download tests/generators/make_hard_tests.py from optionalrudra/exportstatement: direct link, hf CLI and curl.
- Browser
- Download file 8.52 kB
-
https://huggingface.co/spaces/optionalrudra/exportstatement/resolve/main/tests/generators/make_hard_tests.py
- Command line
-
hf download hf://spaces/optionalrudra/exportstatement/tests/generators/make_hard_tests.py
-
curl -L -o make_hard_tests.py https://huggingface.co/spaces/optionalrudra/exportstatement/resolve/main/tests/generators/make_hard_tests.py
8.52 kB
| """ | |
| make_hard_tests.py — adversarial PDFs to try to BREAK the unedited converter. | |
| Each function builds one statement that stresses a different real-world failure | |
| mode. We then run converter.py on each and see what survives. | |
| """ | |
| from __future__ import annotations | |
| import random | |
| from pathlib import Path | |
| from reportlab.lib import colors | |
| from reportlab.lib.pagesizes import A4 | |
| from reportlab.lib.styles import getSampleStyleSheet | |
| from reportlab.lib.units import mm | |
| from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle | |
| HERE = Path(__file__).resolve().parent | |
| OUT = HERE / "hard" | |
| OUT.mkdir(exist_ok=True) | |
| random.seed(7) | |
| DESCS = ["AMAZON MKTPLACE", "WOOLWORTHS 1234", "SALARY ACME CORP", "BPAY ASIC", | |
| "UBER TRIP", "AGL ENERGY DD", "NETFLIX SUB", "INTEREST PAID", | |
| "TFR RENT 062000", "DIVIDEND VAS ETF", "COLES 0456", "OPTUS MOBILE"] | |
| def gen(n=15, opening_c=1_000_000): | |
| rows, running = [], opening_c | |
| d = 1 | |
| for i in range(n): | |
| d += random.randint(1, 2) | |
| is_cr = random.random() < 0.45 | |
| c = random.randint(500, 400000) | |
| running += c if is_cr else -c | |
| rows.append({ | |
| "date": f"{min(d,28):02d}/03/2025", | |
| "desc": f"{random.choice(DESCS)} REF{random.randint(1000,9999)}", | |
| "debit_c": 0 if is_cr else c, | |
| "credit_c": c if is_cr else 0, | |
| "bal_c": running, | |
| }) | |
| return rows, opening_c, running | |
| def usd(c): return f"${c/100:,.2f}" | |
| def eur(c): return f"€{c/100:,.2f}".replace(",", "X").replace(".", ",").replace("X", ".") # 1.234,56 | |
| def build(path, header, data_rows, meta_lines, grid=True): | |
| styles = getSampleStyleSheet() | |
| doc = SimpleDocTemplate(str(path), pagesize=A4, topMargin=16*mm, bottomMargin=16*mm, | |
| leftMargin=14*mm, rightMargin=14*mm) | |
| story = [Paragraph("<b>Test Bank Statement</b>", styles["Title"]), Spacer(1, 6)] | |
| for m in meta_lines: | |
| story.append(Paragraph(m, styles["Normal"])) | |
| story.append(Spacer(1, 10)) | |
| tbl = Table([header] + data_rows, repeatRows=1) | |
| style = [("FONTSIZE", (0, 0), (-1, -1), 7), ("ALIGN", (2, 0), (-1, -1), "RIGHT")] | |
| if grid: | |
| style += [("GRID", (0, 0), (-1, -1), 0.4, colors.grey), | |
| ("BACKGROUND", (0, 0), (-1, 0), colors.HexColor("#1f3a5f")), | |
| ("TEXTCOLOR", (0, 0), (-1, 0), colors.white)] | |
| tbl.setStyle(TableStyle(style)) | |
| story.append(tbl) | |
| doc.build(story) | |
| def std_meta(op_c, cl_c, op_label="Opening Balance", cl_label="Closing Balance"): | |
| return ["Account: 111222333 - TEST ACCOUNT", "Account Holder: TEST USER PTY LTD", | |
| "Statement Period: 01/03/2025 to 31/03/2025", | |
| f"{op_label}: {usd(op_c)}", f"{cl_label}: {usd(cl_c)}"] | |
| # ---- T1: borderless (no gridlines) ---- | |
| def t1_no_gridlines(): | |
| rows, op, cl = gen() | |
| data = [[r["date"], r["desc"], usd(r["debit_c"]) if r["debit_c"] else "", | |
| usd(r["credit_c"]) if r["credit_c"] else "", f"{usd(r['bal_c'])} CR"] for r in rows] | |
| build(OUT/"t1_no_gridlines.pdf", ["Date","Description","Debit","Credit","Balance"], | |
| data, std_meta(op, cl), grid=False) | |
| # ---- T2: scanned / image-only (no text layer) ---- | |
| def t2_scanned(): | |
| try: | |
| from PIL import Image, ImageDraw, ImageFont | |
| from reportlab.pdfgen import canvas | |
| except Exception as e: | |
| print(" (skip t2 scanned — PIL missing:", e, ")"); return | |
| def font(sz): | |
| for p in ("/System/Library/Fonts/Supplemental/Arial.ttf", "/Library/Fonts/Arial.ttf", | |
| "/System/Library/Fonts/Helvetica.ttc"): | |
| try: | |
| return ImageFont.truetype(p, sz) | |
| except Exception: | |
| pass | |
| return ImageFont.load_default(size=sz) | |
| rows, op, cl = gen() | |
| W, H = 1654, 2339 # A4 @ ~200dpi, crisp TrueType text = realistic clean scan | |
| img = Image.new("RGB", (W, H), "white"); dr = ImageDraw.Draw(img) | |
| f, fb = font(30), font(42) | |
| y = 60 | |
| dr.text((60, y), "Test Bank Statement", fill="black", font=fb); y += 80 | |
| for line in std_meta(op, cl): | |
| dr.text((60, y), line, fill="black", font=f); y += 46 | |
| y += 24 | |
| cols = [60, 320, 780, 1040, 1300] | |
| for x, h in zip(cols, ["Date", "Description", "Debit", "Credit", "Balance"]): | |
| dr.text((x, y), h, fill="black", font=f) | |
| y += 54 | |
| for r in rows: | |
| cells = [r["date"], r["desc"][:22], usd(r["debit_c"]) if r["debit_c"] else "", | |
| usd(r["credit_c"]) if r["credit_c"] else "", f"{usd(r['bal_c'])} CR"] | |
| for x, cell in zip(cols, cells): | |
| dr.text((x, y), cell, fill="black", font=f) | |
| y += 44 | |
| png = OUT/"t2_scan.png"; img.save(png) | |
| c = canvas.Canvas(str(OUT/"t2_scanned.pdf"), pagesize=A4) | |
| c.drawImage(str(png), 0, 0, width=A4[0], height=A4[1]); c.save() | |
| # ---- T3: renamed headers ---- | |
| def t3_renamed_headers(): | |
| rows, op, cl = gen() | |
| data = [[r["date"], r["desc"], usd(r["debit_c"]) if r["debit_c"] else "", | |
| usd(r["credit_c"]) if r["credit_c"] else "", f"{usd(r['bal_c'])} CR"] for r in rows] | |
| build(OUT/"t3_renamed_headers.pdf", | |
| ["Transaction Date","Details","Withdrawals","Deposits","Running Balance"], | |
| data, std_meta(op, cl)) | |
| # ---- T4: single signed Amount column ---- | |
| def t4_signed_amount(): | |
| rows, op, cl = gen() | |
| data = [[r["date"], r["desc"], | |
| usd(r["credit_c"]) if r["credit_c"] else f"-{usd(r['debit_c'])}", | |
| f"{usd(r['bal_c'])} CR"] for r in rows] | |
| build(OUT/"t4_signed_amount.pdf", ["Date","Description","Amount","Balance"], | |
| data, std_meta(op, cl)) | |
| # ---- T5: European number format + euro symbol ---- | |
| def t5_european_format(): | |
| rows, op, cl = gen() | |
| data = [[r["date"], r["desc"], eur(r["debit_c"]) if r["debit_c"] else "", | |
| eur(r["credit_c"]) if r["credit_c"] else "", f"{eur(r['bal_c'])} CR"] for r in rows] | |
| meta = ["Account: 111222333 - TEST ACCOUNT", "Account Holder: TEST USER", | |
| "Statement Period: 01/03/2025 to 31/03/2025", | |
| f"Opening Balance: {eur(op)}", f"Closing Balance: {eur(cl)}"] | |
| build(OUT/"t5_european_format.pdf", ["Date","Description","Debit","Credit","Balance"], data, meta) | |
| # ---- T6: two accounts in one PDF ---- | |
| def t6_multi_account(): | |
| styles = getSampleStyleSheet() | |
| doc = SimpleDocTemplate(str(OUT/"t6_multi_account.pdf"), pagesize=A4, | |
| topMargin=16*mm, bottomMargin=16*mm, leftMargin=14*mm, rightMargin=14*mm) | |
| story = [Paragraph("<b>Test Bank Statement</b>", styles["Title"]), Spacer(1, 6)] | |
| for acc in ("Account: AAA-111", "Account: BBB-222"): | |
| rows, op, cl = gen() | |
| for m in [acc, f"Opening Balance: {usd(op)}", f"Closing Balance: {usd(cl)}"]: | |
| story.append(Paragraph(m, styles["Normal"])) | |
| story.append(Spacer(1, 6)) | |
| data = [["Date","Description","Debit","Credit","Balance"]] + [ | |
| [r["date"], r["desc"], usd(r["debit_c"]) if r["debit_c"] else "", | |
| usd(r["credit_c"]) if r["credit_c"] else "", f"{usd(r['bal_c'])} CR"] for r in rows] | |
| t = Table(data, repeatRows=1) | |
| t.setStyle(TableStyle([("GRID",(0,0),(-1,-1),0.4,colors.grey),("FONTSIZE",(0,0),(-1,-1),7)])) | |
| story.append(t); story.append(Spacer(1, 14)) | |
| doc.build(story) | |
| # ---- T7: relabeled balance lines (no "Opening Balance:") ---- | |
| def t7_diff_balance_labels(): | |
| rows, op, cl = gen() | |
| data = [[r["date"], r["desc"], usd(r["debit_c"]) if r["debit_c"] else "", | |
| usd(r["credit_c"]) if r["credit_c"] else "", f"{usd(r['bal_c'])} CR"] for r in rows] | |
| build(OUT/"t7_diff_balance_labels.pdf", ["Date","Description","Debit","Credit","Balance"], | |
| data, std_meta(op, cl, "Balance Brought Forward", "Balance Carried Forward")) | |
| # ---- T8: text-month dates (should still survive) ---- | |
| def t8_text_dates(): | |
| rows, op, cl = gen() | |
| months = {3: "Mar"} | |
| data = [[f"{i+1:02d} Mar 2025", r["desc"], usd(r["debit_c"]) if r["debit_c"] else "", | |
| usd(r["credit_c"]) if r["credit_c"] else "", f"{usd(r['bal_c'])} CR"] | |
| for i, r in enumerate(rows)] | |
| build(OUT/"t8_text_dates.pdf", ["Date","Description","Debit","Credit","Balance"], | |
| data, std_meta(op, cl)) | |
| if __name__ == "__main__": | |
| for fn in (t1_no_gridlines, t2_scanned, t3_renamed_headers, t4_signed_amount, | |
| t5_european_format, t6_multi_account, t7_diff_balance_labels, t8_text_dates): | |
| fn() | |
| print("built:", fn.__name__) | |