18 Commits

Author SHA1 Message Date
Andrey Epifancev
9dd568ce44 fix(normalizer): правило тысяч только при скобках на всю строку, спека и README (AURORA-1405, #35)
I-1: «(4,200) руб», «1,234(5,678)» и т. п. разбираются как прежде; I-2/I-3: спека и README;
M-1..M-3: docstring, комментарий регулярки, тесты крайних случаев.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 16:59:32 +04:00
Andrey Epifancev
1f308ddfe3 feat(normalizer): запятая-разделитель тысяч в суммах OCR (AURORA-1405, #35)
«4,200» в PDF — это 4200: запятые вида [+-]?[1-9]\d{0,2}(,\d{3})+ (полное совпадение, с учётом скобок)
удаляются до замены запятой на точку, verified сохраняется. Решение владельца 15.10.2026.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 16:56:58 +04:00
Andrey Epifancev
0847e6786c fix(normalizer): при конфликте label с единодушными кодами строк тип документа — по кодам (#30)
ИС Сканирование вернула ОФР с label «Бухгалтерский баланс» при кодах 2110…2910;
по прежнему правилу побеждал label, и документ уходил в ядро как BALANCE.
Теперь: все коды данных одной группы и противоречат label — тип по кодам,
предупреждение «тип по label (…) не совпал с кодами: взят …», needs_check;
смешанные коды — по-прежнему label. header.form_name не меняется.
Спека image-recognition-gateway, README и CLAUDE.md обновлены.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-09 20:07:25 +04:00
Andrey Epifancev
c81ec9a66c feat(normalizer): разлеплять склеенные строки ИС «1500\n1700» (#16, OQ-156)
ИС возвращает соседние строки формы одной строкой: в ячейке кода несколько
целых кодов через перевод строки, суммы — тоже. Раньше строка пропускалась как
нечисловой код, и в ББ РБ и Счастливочной за 1 кв. 2025 не было 1500 и 1700.

- rows.split_merged_row: N ≥ 2 целых кодов и ровно N частей в каждой непустой
  ячейке значений → N строк до классификации; наименование делится так же или
  остаётся целым; уверенность — исходной ячейки. Иначе — поведение прежнее.
- Предупреждение таблицы «строк с несколькими кодами разлеплено: N».
- Решение №36 в design.md Среза 3a (отклонение от источника), README.

Эталоны нормализатора — отдельный PR в arm-ka-fixtures; правило — §11.1 п. 4.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 12:59:22 +04:00
Andrey Epifancev
c1288d08fb fix(normalizer): итоги финального ревью — Money, статус при ошибочном файле, пропущенные строки, длина дат, дубли yaml
- C-1 (№30): сумма с более чем двумя знаками после запятой не принимается (amount=null, string_value, is_recognized=false);
  confidence ячеек и шапки приводится к [0, 100], NaN и бесконечность дают 0
- F-A (№28): ошибочный файл набора при успешном понижает статус до needs_check
- F-B: «ИНН не распознан» только когда ИНН не извлечён, иначе «ИНН распознан неуверенно»
- №29: предупреждения «строк с нечисловым кодом пропущено: N» и «строк без кода с суммами пропущено: N»
- I-3 (№31): тексты дат длиннее 200 символов не разбираются и не проходят fix_lookalikes
- T1.3 (№33): загрузчик yaml отклоняет повторяющиеся ключи; тавтологичный тест дублей заменён настоящим
- гигиена: докстринг amounts.py описывает фактическое поведение (все точки удаляются), мёртвые noqa в ocr/__init__.py

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-07 03:35:10 +04:00
Andrey Epifancev
ebbeff58bd test(normalizer): golden на реальных ответах ИС, make golden-update (D-8)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-07 00:47:42 +04:00
Andrey Epifancev
2a7ceca334 feat(normalizer): OcrNormalizer — документы из успешных файлов, статусы done/needs_check/error (D-6)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-07 00:42:44 +04:00
Andrey Epifancev
8ebf572a22 fix(normalizer): values по числу колонок, предупреждения редакций и колонок (T8.1–T8.6)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-07 00:37:30 +04:00
Andrey Epifancev
80622ed331 feat(normalizer): сборка statementDocument из таблицы OCR — тип, колонки, строки, редакции (D-6, D-7)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-07 00:30:22 +04:00
Andrey Epifancev
6882493d98 feat(normalizer): шапка документа из data_ocr — ИНН, единицы, дата отчёта (§11.1 п. 8)
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-07 00:25:48 +04:00
Andrey Epifancev
462ea9ee32 feat(normalizer): строки таблицы OCR — шапки, классы строк, тип по кодам (§11.1 п. 3–5)
Перенос _classify_row, _parse_table_rows, _synthetic_header_cells, _resolve_table_type
из reference-old-arm с адаптацией к новой архитектуре:
- Cell dataclass вместо dict
- ParsedTable вместо list возврата
- Fallback для row_index если не int
- Безопасная обработка пустых кодов в table_type_by_codes

14 тестов пройдено (2 на реальных данных fixtures).

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-07 00:21:20 +04:00
Andrey Epifancev
0123dc0067 feat(normalizer): раскладка колонок OCR — шапка «Код» и вывод по данным (§11.1 п. 3)
Перенос `_build_layout`, `_infer_layout`, `_cells_by_index`, `_cell_value` из старого ядра.
- Cell dataclass (frozen): значение и уверенность из ячейки OCR
- build_layout: раскладка из строки с ячейкой «Код…» (заголовок)
- infer_layout: раскладка из строк данных по кодам (когда заголовка нет)
- Справочник кодов приходит параметром, ячейки — Cell вместо словарей

R-1 (design #18): тест test_aep3_like_rows_without_header исправлен:
- столбец 5 содержит только мусор (пусто, ':', '+ :') → не входит в value_idxs
- столбец 4 содержит суммы ('3', '5 678') не реже мусора ('--') → входит
- замена assertion на layout.value_idxs == (2, 4) вместо ошибочного условия

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-07 00:15:13 +04:00
Andrey Epifancev
60092b8293 feat(normalizer): разбор сумм OCR — перенос _parse_amount (§11.1 п. 7)
Переносим функции парсинга сумм и проверки пустоты из старого ядра:
замена запятой, удаление пробелов, извлечение круглых скобок (отрицательное),
множественные точки как разделители тысяч. Отличие: verified вычисляется
ДО постановки минуса (решение №13 design.md), чтобы не отмечать суммы
в скобках как неверифицированные.
2026-10-07 00:10:20 +04:00
Andrey Epifancev
5f56f62dbb feat(normalizer): разбор дат из OCR — дословный перенос date_utils старого ядра (§11.1 п. 2)
Портирован модуль date_utils.py из DKB_ARMKA с перечисленными в §11.1 правками:
- удалены _LOOKALIKES, _LOOKALIKE_CHARS, _NEAR_DIGIT, _normalize (перенесены в text.fix_lookalikes)
- переименована _parse_date в parse_date с новой сигнатурой (value: object -> date | None)
- обновлены аннотации типов: Optional[...] -> ... | None
- добавлена аннотация для _PATTERNS: list[tuple[re.Pattern[str], Callable[[re.Match[str]], date | None]]]
- добавлены noqa/type: ignore комментарии для прохода линтеров

Тесты: 35 тестов пройдены (парсинг дат в текстовых и числовых формах, отклонение названий организаций).

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-07 00:06:14 +04:00
Andrey Epifancev
746d993077 fix(normalizer): замена неразрывного пробела в fix_lookalikes, тест
Исправлена строка 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
2026-10-07 00:00:31 +04:00
Andrey Epifancev
cea8dfe485 feat(normalizer): очистка OCR-текста — двойники, разделитель клеток, clean_value (§11.1 п. 1, 6)
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-06 23:56:16 +04:00
Andrey Epifancev
ecdf9a4e13 fix(normalizer): опечатка кода 1220 в справочнике 66n, тест данных редакции
I-1: Исправлена опечатка в api/rules/statement_lines.yaml код 1220:
- было: 'ценностями' (неправильный падеж)
- стало: 'ценностям' (правильный падеж, соответствует брифу)

I-2: Добавлен тест test_66n_edition_data_integrity() в TestRealRules:
- проверяет точное количество кодов в редакции 66n (63)
- валидирует специфические коды из брифа: 1220 (опечатка), 1105, 2411, 2412
- тест фиксирует данные редакции 66n, предотвращает проскальзывание опечаток

Результат: 760 тестов пройдено (749 + 11 новых), lint чист.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-06 23:44:44 +04:00
Andrey Epifancev
393d65208f feat(normalizer): справочники yaml с редакциями форм, загрузчик правил, SCAN_CLEANUP/SCAN_RULES_DIR (D-7)
Справочники нормализатора OCR:
- api/rules/statement_lines.yaml: редакции форм ББ/ОФР (66n до 2024-12-31, fsbu4 с 2025-01-01)
- api/rules/ocr_lookalikes.yaml: замены букв на цифры (из date_utils._LOOKALIKES)
- api/rules/statement_labels.yaml: подстроки label → тип документа (BALANCE/INCOME)

Модуль rules.py:
- Edition: редакция с интервалом дат и таблицей кодов → наименований
- OcrRules: все справочники + методы edition_for(), edition_by_codes(), known_codes(), editions_with()
- load_rules(): загрузчик с валидацией (коды 4–5 цифр, двойники 1→1, файлы обязательны)

Настройки:
- SCAN_CLEANUP: удаление файлов из горячей папки (по умолчанию false)
- SCAN_RULES_DIR: путь к справочникам (по умолчанию 'rules')

TDD: 10 тестов (TestRealRules, TestEditionChoice, TestValidation, test_settings_defaults)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-06 23:33:54 +04:00