Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BJKMLHdxMwSUeYcdkwqX4W
101 lines
4.2 KiB
Python
101 lines
4.2 KiB
Python
import math
|
||
|
||
import pytest
|
||
|
||
from arm_statements import Cell, DocumentType, Source, default_rules
|
||
from src.normalizer.ocr.adapter import TableStructureError, confidence_of, header_cells, table_candidate
|
||
|
||
RULES = default_rules()
|
||
DATA_OCR = {
|
||
"label": {"form_name": "Бухгалтерская отчетность", "confidence": 100.0},
|
||
"organization": {"form_name": "ООО «Пример»", "confidence": 100.0},
|
||
"inn": {"form_name": "3|1 21 2|", "confidence": 61.538464},
|
||
"units": {"form_name": "тыс. руб.", "confidence": 100.0},
|
||
"date": {"form_name": "", "confidence": 0.0},
|
||
}
|
||
|
||
|
||
def row(index: object, *cells: tuple[object, object, object]) -> dict[str, object]:
|
||
return {"row_index": index, "cells": [{"column_index": c, "value": v, "confidence": p} for c, v, p in cells]}
|
||
|
||
|
||
def table(label: str, *rows: dict[str, object]) -> dict[str, object]:
|
||
return {"label": {"form_name": label, "confidence": 100.0}, "rows": list(rows)}
|
||
|
||
|
||
def test_header_is_copied_from_json() -> None:
|
||
h = header_cells(DATA_OCR)
|
||
assert h.organization_name == Cell("ООО «Пример»", None, 100.0, "data_ocr.organization")
|
||
assert h.inn == Cell("3|1 21 2|", None, 61.538464, "data_ocr.inn")
|
||
assert h.units == Cell("тыс. руб.", None, 100.0, "data_ocr.units")
|
||
assert (h.report_date, h.okud, h.okpo, h.okopf, h.okfs, h.okei, h.address) == (None,) * 7
|
||
|
||
|
||
def test_grid_by_row_and_column_index() -> None:
|
||
t = table(
|
||
"Бухгалтерский баланс",
|
||
row(9, (0, "Итого", 100.0)),
|
||
row(8, (1, "[190 :", 25.0), (0, "Прочие", 100.0), (4, "", 0.0), (5, None, 100.0)),
|
||
)
|
||
cand = table_candidate("j.json", 0, t, header_cells(DATA_OCR), RULES)
|
||
assert (cand.source, cand.locator, cand.type_hint) == (Source.OCR, "file:j.json;table:0", DocumentType.BALANCE)
|
||
assert cand.segment_breaks == () and cand.warnings == ()
|
||
assert cand.evidence.okud is None
|
||
assert cand.evidence.title == Cell("Бухгалтерский баланс", None, 100.0, "table:0.label")
|
||
assert cand.rows == (
|
||
(
|
||
Cell("Прочие", None, 100.0, "row:8"),
|
||
Cell("[190 :", None, 25.0, "row:8"),
|
||
None,
|
||
None,
|
||
Cell("", None, 0.0, "row:8"),
|
||
Cell("", None, 100.0, "row:8"),
|
||
),
|
||
(Cell("Итого", None, 100.0, "row:9"), None, None, None, None, None),
|
||
)
|
||
assert cand.header == header_cells(DATA_OCR)
|
||
|
||
|
||
def test_multiline_text_is_kept() -> None:
|
||
t = table("Бухгалтерский баланс", row(93, (1, "1500\n1700", 78.0), (2, "917 775\n2 371 333", 100.0)))
|
||
cand = table_candidate("j.json", 0, t, header_cells(DATA_OCR), RULES)
|
||
assert cand.rows[0][1] == Cell("1500\n1700", None, 78.0, "row:93")
|
||
|
||
|
||
def test_income_label_and_unknown_label() -> None:
|
||
income = table_candidate("j.json", 1, table("Отчет о финансовых результатах"), header_cells(DATA_OCR), RULES)
|
||
assert income.type_hint is DocumentType.INCOME and income.rows == ()
|
||
blank = table_candidate(
|
||
"j.json", 2, {"label": {"form_name": " ", "confidence": 0.0}, "rows": []}, header_cells(DATA_OCR), RULES
|
||
)
|
||
assert (blank.evidence.title, blank.type_hint) == (None, None)
|
||
|
||
|
||
@pytest.mark.parametrize(
|
||
("raw", "expected"),
|
||
[(25, 25.0), (150.0, 100.0), (-3, 0.0), (math.nan, 0.0), (math.inf, 0.0), ("100", 0.0), (True, 0.0), (None, 0.0)],
|
||
)
|
||
def test_confidence_of(raw: object, expected: float) -> None:
|
||
assert confidence_of(raw) == expected
|
||
|
||
|
||
@pytest.mark.parametrize(
|
||
"broken",
|
||
[
|
||
"не объект",
|
||
{"label": {}, "rows": "x"},
|
||
table("ББ", {"cells": []}),
|
||
table("ББ", row(True, (0, "a", 100.0))),
|
||
table("ББ", row(1, (0, "a", 100.0)), row(1, (0, "b", 100.0))),
|
||
table("ББ", {"row_index": 0, "cells": "x"}),
|
||
table("ББ", row(0, ("1", "a", 100.0))),
|
||
table("ББ", row(0, (-1, "a", 100.0))),
|
||
table("ББ", row(0, (1, "a", 100.0), (1, "b", 100.0))),
|
||
table("ББ", row(0, (True, "a", 100.0))),
|
||
table("ББ", {"row_index": 0, "cells": ["не объект"]}),
|
||
],
|
||
)
|
||
def test_broken_structure_raises(broken: object) -> None:
|
||
with pytest.raises(TableStructureError):
|
||
table_candidate("j.json", 0, broken, header_cells(DATA_OCR), RULES)
|