9.7 KiB
Raw Blame History

Tasks: Срез 2a — парсер ОСВ (tabular-parser)

Каждая задача ≤ половины дня, начинается с тестов, заканчивается зелёным make check. Пути — от корня репозитория. Фикстуры — ../fixtures (worktree .wt/slice-2/fixtures, ветка feat/4-slice-2a-osv-parser); контракт v1.1 — ../docs/contracts (ветка feat/9-slice-2a-osv-parser).

1. Основание

  • 1.1 Зависимости и фикстуры: openpyxl==3.1.5, xlrd==2.0.2, pyxlsb==1.0.10 в api/requirements.txt; make venv; tests/fixtures_helpers.py (ARM_KA_FIXTURES, как в arm-core) и conftest.py с фикстурами osv_file (все файлы ../fixtures/osv/** кроме synthetic) и synthetic_file (../fixtures/osv/synthetic/*), параметризация по относительному пути; маркер fixtures в деле; test_layout не ломается. Проверка: pytest -m fixtures --collect-only показывает 66 параметров.
  • 1.2 Контракт v1.1: скопировать ../docs/contracts/recognition-result.schema.json v1.1 в api/contracts/, OsvRow.extra: dict[str, str | None] | None, CONTRACT_VERSION = "1.1"; примеры docs проходят; тест идентичности зелёный; воркер пишет result_schema_version = "1.1" (test_worker обновлён). Проверка: make check.
  • 1.3 grid.py и читатели: Cell, Sheet, MergedRange, column_letter; readers/xlsx.py (openpyxl, полное чтение, outline_level, alignment.indent, bold), readers/xls.py (xlrd formatting_info, rowinfo_map, XF bold/indent), readers/xlsb.py (pyxlsb, без outline), readers/__init__.py:read_book(data, file_type) -> (sheets, warnings) с определением по сигнатуре и предупреждением при несоответствии; ошибки → UnreadableError/UnsupportedFormatError. Тесты: по читателю на собранной книге, реальные xls/xlsx читаются с outline и merged, синтетический xlsb (писателей xls/xlsb для проверки «три читателя дают одинаковые ячейки» нет — решение R-11, design №21); fixtures-тест: все 66 файлов читаются < 5 с каждый.
  • 1.4 Словари: api/rules/{osv_headers,osv_groups,periods,data_types}.yaml (содержимое по §1.1/§1.5 и синонимам из §1.0), src/parser/rules.py:load_rules(path) -> Rules (pydantic, компиляция регулярок с нормализацией ё/е и пробелов), тест на падение при битом словаре. Покрытие словарей — задача 4.3.

2. Обнаружение структуры

  • 2.1 detect.py: classify_sheet (вид листа: ОСВ / форма ББ-ОФР / прочее), find_table_header (группы по merged-тексту в 1–4 строках, Дебет/Кредит под группой, обязательны «обороты» и «конец» — R-12, свёрнутое сальдо, отсутствие «начала», right_edge, text_col, inn_col по заголовку и по доле 10/12 цифр, extra-колонки). Unit-тесты на сетках: заголовок 1/2/3/4 строки, merged, без «начала», свёрнутое сальдо, КПП между ИНН и суммами, мусор правее. fixtures-тест: columns для РБ E/H/J/L/O/Q, для Счастливочной xls G/J/K/N/Q/S + inn=E.
  • 2.2 header.py: шапка документа (организация, счёт, период, data_type, валюта), group_schema вариант 1 и 2, has_inn, период → period_year, period_quarter, is_cumulative по periods.yaml (ASSUMPTION(OQ-02)). Unit-тесты: обе шапки, отсутствие полей → null + warning, субсчёт 58.03, все строки таблицы §1.5. fixtures-тест: у всех 66 файлов account_code_raw совпадает с кодом в имени каталога/файла по регулярке и период распознан.
  • 2.3 hierarchy.py: стратегии outline, indent, account_pattern, sums, каскад с проверкой непротиворечивости, parent_row_number. Unit-тесты на сетках для каждой стратегии и для отказа стратегии (одинаковые уровни); fixtures-тест: hierarchy=outline у всех файлов со строками данных, trivial у пустых ОСВ (R-13: меньше двух строк данных; в фикстурах таких две — schastl 58 и 66 за 2 кв. 2025).
  • 2.4 classify.py + numbers.py: классификация (ACCOUNT по регулярке, позиция в group_schema, OTHER при выходе, TOTAL, COUNTERPARTY с ИНН, Вал./Кол./код валюты → data_type), числа (Decimal, текстовые форматы, скобки, прочерки, экспонента), extra. Unit-тесты по §1.3 и §1.2; fixtures-тест: в schastl …62 за 3 кв 2024 «БП Аренда» → DEPARTMENT, «МАЛЕЧКИНСКАЯ ПТИЦЕФАБРИКА ООО» с ИНН, физлицо без ИНН.

3. Сборка и статусы

  • 3.1 checks.py + osv.py: инварианты §1.4 (±0.01, null = 0, CURRENCY/QUANTITY исключены), checks, статусы D-6, Deadline и PARSE_MAX_BYTES/PARSE_TIMEOUT_SECONDS в Settings (+ .env.example), OsvParser.parse(data, file_type) -> ParseOutcome, get_parser() возвращает его, NullParser остаётся только для тестов воркера. Unit-тесты: карта статусов (нет листов, нет заголовка, лимит, таймаут через монкипатч часов, исключение → RECOGNITION_FAILED), испорченная сумма → needs_check с номерами строк. fixtures-тест (property): все 66 — done, инварианты, корень = счёт шапки.
  • 3.2 Интеграция с воркером: test_worker на реальном OsvParser с байтами ОСВ_66 за 2024.xls (unit, без NATS) и интеграционный roundtrip (NATS + MinIO): конверт done, result_schema_version = "1.1", файл результата проходит схему. README: раздел «Запуск на файле» (python -m src.parser.cli <файл> — печать результата JSON; добавить CLI).

4. Синтетика, golden, линтер

  • 4.1 Генератор синтетики tests/synthetic/generate.py (детерминированный openpyxl) → ../fixtures/osv/synthetic/: вариант 2 шапки; без outline (отступы); без outline и отступов; Вал./Кол.; текстовые числа и скобки; два листа ОСВ + посторонний; без «начала» + свёрнутое сальдо; КПП-колонка; название субсчёта в строке; испорченная сумма; плюс xlsb (s13), собранный тестовым писателем BIFF12 tests/synthetic/xlsb_writer.py (LibreOffice нет, R-9): не из Excel, совместимость с настоящим xlsb не доказана (O-7), пометка и инструкция замены — в README fixtures; s14–s16 — записи словарей, которых нет в реальных файлах (R-22). Тесты сценариев спеки на этих файлах.
  • 4.2 Golden: tests/golden_helpers.py (нормализация результата без volatile-полей), тест сравнения для всех fixtures/osv/** включая синтетику, make golden-update (перезапись + diff), первичная запись снимков в ../fixtures/golden/expected_osv_parse/ после ручной сверки «Итого» трёх файлов (РБ 60 1 кв 2025, Счастливочная 62 3 кв 2024 xls, 58.03) — сверка описывается в отчёте задачи.
  • 4.3 Линтер координат tests/unit/parser/test_no_coordinates.py (AST по src/parser/** кроме grid.py, readers/) и тест покрытия словарей (каждая запись сработала хотя бы раз на фикстурах+синтетике; список несработавших — в сообщении).

5. Завершение

  • 5.1 Документы: README (архитектура парсера, статусы и коды, словари, golden-процесс, лимиты), CLAUDE.md (модули, правила расширения), rules/README.md; make check-full (lint, unit, fixtures, integration, docker) зелёный, вывод — в PR. Проверка: сценарии всех требований osv-parsing имеют тест (таблица в отчёте задачи).
  • 5.2 После merge: openspec archive slice-2a-osv-parser.