Spaces:
Running
Running
Download src/doc_verify/samples.py from gaurabdas/doc-verify: direct link, hf CLI and curl.
- Browser
- Download file 5.56 kB
-
https://huggingface.co/spaces/gaurabdas/doc-verify/resolve/main/src/doc_verify/samples.py
- Command line
-
hf download hf://spaces/gaurabdas/doc-verify/src/doc_verify/samples.py
-
curl -L -o samples.py https://huggingface.co/spaces/gaurabdas/doc-verify/resolve/main/src/doc_verify/samples.py
5.56 kB
| """Fictional invoices with separately specified expected discrepancy labels.""" | |
| import copy | |
| import json | |
| from pathlib import Path | |
| from reportlab.lib import colors | |
| from reportlab.lib.pagesizes import A4 | |
| from reportlab.lib.styles import getSampleStyleSheet | |
| from reportlab.platypus import PageBreak, Paragraph, SimpleDocTemplate, Spacer, Table, TableStyle | |
| CASES = [ | |
| ("Matching documents", []), | |
| ("Price discrepancy", ["price_mismatch"]), | |
| ("Quantity discrepancy", ["quantity_mismatch"]), | |
| ("Incorrect total", ["total_mismatch"]), | |
| ("Missing invoice number", ["missing_field"]), | |
| ("Different currency", ["currency_mismatch"]), | |
| ( | |
| "Ambiguous purchase lines", | |
| ["ambiguous_match", "unmatched_purchase_line", "subtotal_mismatch"], | |
| ), | |
| ("Incorrect line amount", ["line_total_mismatch", "subtotal_mismatch"]), | |
| ("Missing date", ["missing_field"]), | |
| ("Unmatched product", ["unmatched_line", "unmatched_purchase_line"]), | |
| ] | |
| def sample_data(index): | |
| case = index % 10 | |
| inv = { | |
| "vendor": "Northwind Demo Supplies", | |
| "number": f"INV-{index + 1:03}", | |
| "date": "2026-09-01", | |
| "currency": "INR", | |
| "subtotal": "1200.00", | |
| "tax": "120.00", | |
| "total": "1320.00", | |
| "lines": [ | |
| { | |
| "sku": "MON-01", | |
| "description": "Desktop monitor stand", | |
| "quantity": "10", | |
| "unit_price": "120.00", | |
| "amount": "1200.00", | |
| } | |
| ], | |
| "sources": [], | |
| "notes": [], | |
| } | |
| po = copy.deepcopy(inv) | |
| po["number"] = f"PO-{index + 1:03}" | |
| if case == 1: | |
| inv["lines"][0].update(unit_price="125.00", amount="1250.00") | |
| inv.update(subtotal="1250.00", total="1370.00") | |
| elif case == 2: | |
| inv["lines"][0].update(quantity="12", amount="1440.00") | |
| inv.update(subtotal="1440.00", total="1560.00") | |
| elif case == 3: | |
| inv["total"] = "1400.00" | |
| elif case == 4: | |
| inv["number"] = None | |
| elif case == 5: | |
| inv["currency"] = "USD" | |
| elif case == 6: | |
| po["lines"].append(copy.deepcopy(po["lines"][0])) | |
| elif case == 7: | |
| inv["lines"][0]["amount"] = "1190.00" | |
| elif case == 8: | |
| inv["date"] = None | |
| elif case == 9: | |
| inv["lines"][0].update(sku="CHAIR-01", description="Office chair") | |
| return inv, po | |
| def pdf(path, doc, kind, multipage=False): | |
| styles = getSampleStyleSheet() | |
| story = [ | |
| Paragraph("NORTHWIND DEMO SUPPLIES", styles["Title"]), | |
| Spacer(1, 12), | |
| Paragraph(kind, styles["Heading1"]), | |
| Paragraph("Fictional portfolio sample. Not a payment request.", styles["Normal"]), | |
| Spacer(1, 20), | |
| ] | |
| for field in ("vendor", "number", "date", "currency"): | |
| if doc[field] is not None: | |
| story.append(Paragraph(f"{field.title()}: {doc[field]}", styles["Normal"])) | |
| if multipage: | |
| story += [ | |
| Spacer(1, 20), | |
| Paragraph("Line items and totals continue on page 2.", styles["Normal"]), | |
| PageBreak(), | |
| ] | |
| story.append(Spacer(1, 20)) | |
| rows = [["SKU", "Description", "Quantity", "Unit price", "Amount"]] | |
| rows += [ | |
| [line[key] for key in ("sku", "description", "quantity", "unit_price", "amount")] | |
| for line in doc["lines"] | |
| ] | |
| table = Table(rows, colWidths=[65, 185, 60, 80, 80]) | |
| table.setStyle( | |
| TableStyle( | |
| [ | |
| ("BACKGROUND", (0, 0), (-1, 0), colors.HexColor("#234e70")), | |
| ("TEXTCOLOR", (0, 0), (-1, 0), colors.white), | |
| ("FONTSIZE", (0, 0), (-1, -1), 9), | |
| ("BOTTOMPADDING", (0, 0), (-1, -1), 12), | |
| ("TOPPADDING", (0, 0), (-1, -1), 12), | |
| ("LINEBELOW", (0, 0), (-1, -1), 0.5, colors.HexColor("#c8d5df")), | |
| ] | |
| ) | |
| ) | |
| story += [table, Spacer(1, 25)] | |
| for field in ("subtotal", "tax", "total"): | |
| story.append(Paragraph(f"{field.title()}: {doc[field]}", styles["Heading3"])) | |
| SimpleDocTemplate(str(path), pagesize=A4, leftMargin=50, rightMargin=50).build(story) | |
| def generate(root=Path("data/samples")): | |
| root.mkdir(parents=True, exist_ok=True) | |
| labels = [] | |
| for index in range(20): | |
| inv, po = sample_data(index) | |
| inv_path, po_path = ( | |
| root / f"invoice-{index + 1:02}.pdf", | |
| root / f"purchase-{index + 1:02}.pdf", | |
| ) | |
| pdf(inv_path, inv, "Invoice", multipage=index == 18) | |
| pdf(po_path, po, "Purchase order", multipage=index == 18) | |
| if index in {10, 11, 12, 19}: | |
| from PIL import ImageFilter | |
| from .extraction import render | |
| image = render(inv_path.read_bytes()) | |
| if index == 19: | |
| image = image.resize((image.width // 2, image.height // 2)).filter( | |
| ImageFilter.GaussianBlur(0.6) | |
| ) | |
| inv_path = inv_path.with_suffix(".png") | |
| image.save(inv_path) | |
| labels.append( | |
| { | |
| "id": index + 1, | |
| "name": CASES[index % 10][0] + (" (scan)" if inv_path.suffix == ".png" else ""), | |
| "invoice_file": inv_path.name, | |
| "purchase_file": po_path.name, | |
| "invoice": inv, | |
| "purchase": po, | |
| "expected_codes": CASES[index % 10][1], | |
| "split": "tuning" if index < 10 else "heldout", | |
| } | |
| ) | |
| (root / "labels.json").write_text(json.dumps(labels, indent=2), encoding="utf-8") | |
| return labels | |