← Files VeraARCHIVED FILE

modules/invoice-xml/scripts/source_evidence.py

4.6 KB · Oct 2, 2026 · 00:29 UTC

↓ Download file

"""Prepare local PDF text/pages and image evidence without inferring fields."""

from __future__ import annotations

import argparse
import hashlib
import json
import logging
from pathlib import Path
from typing import Any

import fitz
from invoice_workflow import _context, _json, _regular, _within, _write_new, digest

__all__ = ["prepare_source_evidence", "main"]


def prepare_source_evidence(
    selection: list[dict[str, str]], *, input_root: Path, output_dir: Path
) -> Path:
    """Retain every selected page; extraction completeness never implies truth."""
    if not selection:
        raise ValueError("Select at least one bound source")
    captured = []
    ids: set[str] = set()
    for source in selection:
        if source["id"] in ids:
            raise ValueError("Duplicate source ID")
        ids.add(source["id"])
        path = _within(input_root, source["path"])
        payload = _regular(path)
        suffix = path.suffix.lower()
        if suffix not in {
            ".pdf",
            ".png",
            ".jpg",
            ".jpeg",
            ".webp",
            ".json",
            ".txt",
        }:
            raise ValueError(
                "Unsupported source format; preserve it and request readable evidence"
            )
        captured.append(
            (dict(source, sha256=hashlib.sha256(payload).hexdigest()), payload, suffix)
        )
    sources = [source for source, _, _ in captured]
    folder = _within(output_dir, f"intake-{digest(sources)}")
    folder.mkdir(parents=True, exist_ok=True, mode=0o700)
    material = []
    for source, payload, suffix in captured:
        key = source["sha256"]
        views = []
        if suffix == ".pdf":
            with fitz.open(stream=payload, filetype="pdf") as document:
                if document.needs_pass or document.page_count > 100:
                    raise ValueError(
                        "Encrypted or over-100-page PDF; select a bounded readable document"
                    )
                for index, page in enumerate(document):
                    if page.rect.width * page.rect.height * 2.25 > 25_000_000:
                        raise ValueError(
                            "Oversized PDF page; select a bounded readable document"
                        )
                    text_name = f"{key}-page-{index + 1}.txt"
                    image_name = f"{key}-page-{index + 1}.png"
                    _write_new(folder / text_name, page.get_text().encode())
                    _write_new(
                        folder / image_name,
                        page.get_pixmap(
                            matrix=fitz.Matrix(1.5, 1.5), alpha=False
                        ).tobytes("png"),
                    )
                    views.append(
                        {"page": index + 1, "text": text_name, "image": image_name}
                    )
        else:
            name = key + suffix
            _write_new(folder / name, payload)
            views.append(
                {
                    "page": 1,
                    "image" if suffix not in {".txt", ".json"} else "text": name,
                }
            )
        material.append({"source_id": source["id"], "views": views})
    _write_new(
        folder / "source_evidence.json",
        _json(
            {
                "schema_version": 1,
                "sources": sources,
                "material": material,
                "interpretation_status": "awaiting_model_extraction",
                "model_visibility": "Record pages actually read in the run-level model-data report; this inventory does not prove model exposure.",
            }
        ),
    )
    return folder


def main(argv: list[str] | None = None) -> int:
    """Prepare evidence only inside the selected Studio Archive run."""
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--client-engagement", type=Path, required=True)
    parser.add_argument("--output", type=Path, required=True)
    parser.add_argument("--selection", type=Path, required=True)
    args = parser.parse_args(argv)
    context = _context(args.client_engagement, args.output)
    if not args.selection.resolve().is_relative_to(args.output.resolve()):
        raise ValueError("Save source selection in this run's output directory")
    result = prepare_source_evidence(
        json.loads(_regular(args.selection)),
        input_root=Path(context["input_dir"]),
        output_dir=args.output,
    )
    logging.info("Source evidence ready for interpretation: %s", result)
    return 0


if __name__ == "__main__":
    logging.basicConfig(level=logging.INFO, format="%(message)s")
    raise SystemExit(main())

SHA-256: 0e7a664c6fd1647f37471a7b46efd15c7961f5f9fb20889050e7c8fdae58b465