Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
9.7 KiB
9.7 KiB
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.jsonv1.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(xlrdformatting_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, для Счастливочной xlsG/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), собранный тестовым писателем BIFF12tests/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.