File size: 3,945 Bytes
1cbec06
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
"""Tests for the CanonicalBuilder."""

from __future__ import annotations

import pytest
from src.app.domain.models import (
    EvidenceType,
    Geometry,
    GeometryStatus,
    Provenance,
)
from src.app.domain.models.status import BlockRole, InputType
from src.app.normalization.canonical_builder import CanonicalBuilder


def _geo() -> Geometry:
    return Geometry(bbox=(100, 200, 300, 50), status=GeometryStatus.EXACT)


def _prov(ref: str = "$.test") -> Provenance:
    return Provenance(
        provider="test",
        adapter="test.v1",
        source_ref=ref,
        evidence_type=EvidenceType.PROVIDER_NATIVE,
    )


class TestCanonicalBuilder:
    def test_minimal_document(self) -> None:
        builder = CanonicalBuilder("doc1", InputType.IMAGE, "test.png")
        page = builder.add_page("p1", 0, 2480, 3508)
        region = page.add_text_region("tb1", _geo(), _prov(), role=BlockRole.BODY)
        line = region.add_line("tl1", _geo(), _prov())
        line.add_word("w1", "Hello", _geo(), _prov(), confidence=0.95)

        doc = builder.build()
        assert doc.document_id == "doc1"
        assert len(doc.pages) == 1
        assert len(doc.pages[0].text_regions) == 1
        assert doc.pages[0].text_regions[0].lines[0].words[0].text == "Hello"

    def test_reading_order_auto_built(self) -> None:
        builder = CanonicalBuilder("doc1", InputType.IMAGE)
        page = builder.add_page("p1", 0, 100, 100)
        page.add_text_region("tb1", _geo(), _prov()).\
            add_line("tl1", _geo(), _prov()).\
            add_word("w1", "A", _geo(), _prov())
        page.add_text_region("tb2", _geo(), _prov()).\
            add_line("tl2", _geo(), _prov()).\
            add_word("w2", "B", _geo(), _prov())

        doc = builder.build()
        assert doc.pages[0].reading_order == ["tb1", "tb2"]

    def test_no_pages_raises(self) -> None:
        builder = CanonicalBuilder("doc1", InputType.IMAGE)
        with pytest.raises(ValueError, match="at least one page"):
            builder.build()

    def test_empty_region_raises(self) -> None:
        builder = CanonicalBuilder("doc1", InputType.IMAGE)
        page = builder.add_page("p1", 0, 100, 100)
        page.add_text_region("tb1", _geo(), _prov())
        with pytest.raises(ValueError, match="no lines"):
            builder.build()

    def test_empty_line_raises(self) -> None:
        builder = CanonicalBuilder("doc1", InputType.IMAGE)
        page = builder.add_page("p1", 0, 100, 100)
        region = page.add_text_region("tb1", _geo(), _prov())
        region.add_line("tl1", _geo(), _prov())
        with pytest.raises(ValueError, match="no words"):
            builder.build()

    def test_multiple_words_per_line(self) -> None:
        builder = CanonicalBuilder("doc1", InputType.IMAGE)
        page = builder.add_page("p1", 0, 1000, 1000)
        region = page.add_text_region("tb1", _geo(), _prov())
        line = region.add_line("tl1", _geo(), _prov())
        line.add_word("w1", "Hello", _geo(), _prov())
        line.add_word("w2", "world", _geo(), _prov())

        doc = builder.build()
        assert len(doc.pages[0].text_regions[0].lines[0].words) == 2
        assert doc.pages[0].text_regions[0].lines[0].text == "Hello world"

    def test_with_metadata(self) -> None:
        builder = CanonicalBuilder(
            "doc1", InputType.IMAGE, metadata={"project": "test"}
        )
        page = builder.add_page("p1", 0, 100, 100)
        page.set_metadata({"page_quality": "good"})
        region = page.add_text_region("tb1", _geo(), _prov())
        line = region.add_line("tl1", _geo(), _prov())
        line.add_word("w1", "ok", _geo(), _prov(), metadata={"custom": True})

        doc = builder.build()
        assert doc.metadata == {"project": "test"}
        assert doc.pages[0].metadata == {"page_quality": "good"}
        assert doc.pages[0].text_regions[0].lines[0].words[0].metadata == {"custom": True}