Files
PaperPod/tests/test_ocr_parsing.py
T
ilia 2c28a623fb Add OCR naming and PNG-to-PDF export (modules 5-7, single-page only)
- ocr/: Tesseract-based vendor/date/total extraction. PSM 6 (single
  uniform text block) instead of the default fixes receipts where
  item-name/price columns were otherwise split into separate blocks and
  dropped. Vendor line picked by median (not max) word height within the
  top fraction of the crop, breaking ties by topmost line - max() was
  fooled by single descender/ascender glyphs (commas, parens) inflating
  one word's bounding box.
- naming/: merge OCR metadata into YYYY-MM-DD_vendor.pdf filenames,
  degrading gracefully to UNSORTED_<timestamp>.pdf; per-run dedup.
- pdf/: img2pdf-based multi-page-capable assembly (images_to_pdf).
- pipeline.run_export(): OCR + name + PDF each detected crop from a
  detect() report, writes pdf/ and export_summary.csv.
- New `paperpod export <video>` CLI command.
- Verified against synthetic Home Depot/Metro/Petro-Canada receipts:
  3/3 correct vendor, date, and total after tuning.

Known limitation (documented in README): no page-flip/multi-page
grouping yet (that's events/, still unbuilt) - every detected document
becomes its own single-page PDF.
2026-07-07 18:04:06 -04:00

56 lines
1.6 KiB
Python

"""Unit tests for the regex/heuristic parsing logic in paperpod.ocr.extract.
These test the parsing functions directly on strings so they stay
deterministic and don't depend on Tesseract's OCR accuracy. End-to-end OCR
accuracy against rendered receipts is exercised in test_ocr_integration.py.
"""
from paperpod.ocr.extract import extract_date, extract_total
def test_extract_date_iso():
assert extract_date("2023-03-14 09:41 #4821") == "2023-03-14"
def test_extract_date_slash_mdy():
assert extract_date("Purchased on 03/14/2023 at noon") == "2023-03-14"
def test_extract_date_month_name():
assert extract_date("Invoice date: March 14, 2023") == "2023-03-14"
assert extract_date("Mar 14 2023") == "2023-03-14"
def test_extract_date_none_when_absent():
assert extract_date("no date here, just text") is None
def test_extract_date_rejects_implausible_values():
# Not a real month/day -> not treated as a date.
assert extract_date("13/45/2023") is None
def test_extract_total_basic():
text = "SUBTOTAL 72.14\nHST 13% 9.38\nTOTAL 81.52\n"
assert extract_total(text) == "81.52"
def test_extract_total_ignores_subtotal():
text = "SUBTOTAL 30.63\n"
assert extract_total(text) is None
def test_extract_total_word_boundary_with_prefix():
# "FUEL TOTAL" should still match "total" as its own word.
text = "FUEL TOTAL 65.10\n"
assert extract_total(text) == "65.10"
def test_extract_total_amount_on_next_line():
text = "TOTAL\n81.52\n"
assert extract_total(text) == "81.52"
def test_extract_total_none_when_absent():
assert extract_total("no totals here") is None