Исправлена строка 26 в fix_lookalikes: текст.replace("\xa0", " ") для
корректной замены неразрывного пробела (U+00A0) на обычный пробел (U+0020),
как в источнике date_utils._normalize.
Добавлен тест test_nbsp_replacement для проверки замены NBSP с таблицей
двойников и без неё.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
105 lines
3.4 KiB
Python
105 lines
3.4 KiB
Python
"""Очистка OCR-текста (§11.1 п. 1, 6; D-4 `text.py`): перенос функций старого ядра, поведение — как в источнике."""
|
||
|
||
import pytest
|
||
|
||
from src.normalizer.ocr.text import (
|
||
clean_value,
|
||
collapse_whitespace,
|
||
digits_only,
|
||
fix_lookalikes,
|
||
is_code_header,
|
||
is_value_header,
|
||
normalize_header,
|
||
)
|
||
|
||
LOOKALIKES = {
|
||
"О": "0",
|
||
"о": "0",
|
||
"O": "0",
|
||
"o": "0",
|
||
"З": "3",
|
||
"з": "3",
|
||
"l": "1",
|
||
"I": "1",
|
||
"|": "1",
|
||
"і": "1",
|
||
"б": "6",
|
||
"S": "5",
|
||
"s": "5",
|
||
"Ч": "4",
|
||
}
|
||
|
||
|
||
class TestFixLookalikes:
|
||
@pytest.mark.parametrize(
|
||
("raw", "expected"),
|
||
[
|
||
("2ОО5", "2005"), # два прохода: О после 2, затем О после 0
|
||
("3l.12.2О24", "31.12.2024"),
|
||
("Общество", "Общество"), # буквы без соседней цифры не трогаются
|
||
("2|0|2|4|", "2 0 2 4 "), # первая строка — только цифры и |: это разделитель клеток
|
||
("3l", "31"), # | не разделитель, если есть буквы
|
||
("", ""),
|
||
],
|
||
)
|
||
def test_replaces_only_next_to_digits(self, raw: str, expected: str) -> None:
|
||
assert fix_lookalikes(raw, LOOKALIKES) == expected
|
||
|
||
def test_separator_rule_looks_at_first_line_only(self) -> None:
|
||
assert fix_lookalikes("2|0|2|4|\nмусор", LOOKALIKES) == "2 0 2 4 \nмусор"
|
||
|
||
def test_empty_table_keeps_text(self) -> None:
|
||
assert fix_lookalikes("2ОО5", {}) == "2ОО5"
|
||
|
||
def test_nbsp_replacement(self) -> None:
|
||
# неразрывный пробел (U+00A0) → обычный пробел (U+0020)
|
||
assert fix_lookalikes("31\xa012\xa02024", LOOKALIKES) == "31 12 2024"
|
||
assert fix_lookalikes("31\xa012\xa02024", {}) == "31 12 2024"
|
||
|
||
|
||
@pytest.mark.parametrize(
|
||
("raw", "expected"),
|
||
[
|
||
("13 070", "13 070"),
|
||
("(7744536}", "7744536"),
|
||
("изо :", "изо"),
|
||
("На 30 июня\n2025 г", "На 30 июня 2025 г"),
|
||
("", ""),
|
||
],
|
||
)
|
||
def test_clean_value(raw: str, expected: str) -> None:
|
||
assert clean_value(raw) == expected
|
||
|
||
|
||
def test_digits_only() -> None:
|
||
assert digits_only("3|1 21 2| о! О' 0 0; 3: 51") == "3121200351"
|
||
assert digits_only("+") == ""
|
||
|
||
|
||
def test_collapse_whitespace() -> None:
|
||
assert collapse_whitespace(" На 31 декабря\n2024 г.\t") == "На 31 декабря 2024 г."
|
||
|
||
|
||
class TestHeaders:
|
||
def test_normalize_header(self) -> None:
|
||
assert normalize_header("Наименование\nпоказателя:") == "наименование показателя"
|
||
assert normalize_header(None) == ""
|
||
|
||
def test_code_header(self) -> None:
|
||
assert is_code_header(normalize_header("Код"))
|
||
assert is_code_header(normalize_header("Код строки"))
|
||
assert not is_code_header(normalize_header("Наименование"))
|
||
|
||
@pytest.mark.parametrize(
|
||
("text", "expected"),
|
||
[
|
||
("на 30 июня 2025 г", True),
|
||
("за январь июнь 2025 г", True),
|
||
("2024", True),
|
||
("пояснения", False),
|
||
("", False),
|
||
],
|
||
)
|
||
def test_value_header(self, text: str, expected: bool) -> None:
|
||
assert is_value_header(text) is expected
|