Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
7.9 KiB
7.9 KiB
Tasks: Срез 2c — arm-core
Каждая задача ≤ половины дня, начинается с тестов, заканчивается зелёным make check и интеграционными тестами своей
области. Допущения в коде — # ASSUMPTION(OQ-nn). DBML v5.15 — ../docs/model/data-model-v5.15.dbml (ветка
feat/16-slice-2c-osv-ingest в .wt/slice-2c/docs).
1. Модель и инфраструктура
- 1.1 DBML v5.15, миграция 0003.
OsvDataType.NU(после BU), миграция0003_dbml_v5_15(ALTER TYPE … ADD VALUE 'NU' AFTER 'BU',UPDATE app_user SET email = lower(email); downgrade пересоздаёт enum);DBML_VERSION = "5.15",Makefile,dbml_parity.DBML_FILE_NAME,HEADвtest_migrations,test_enums,test_check_dbml_script(фиктивная 5.16); нормализация email в нижний регистр в seed (_apply_users) и вusers_admin.create_user/update_user; тесты seed/admin на регистр. Проверка:make dbml-checkпуст;make migration-cycle. - 1.2 S3 и фабрики.
S3Storage.get_bytes(key) -> bytes(+ закрытие response),ObjectStorage-протокол сget_bytes;FakeStorage.get_bytes;tests/factories.py:make_stored_file(session, *, entity, status, content_hash, job_status),make_document,make_result_object;make_entries_from_result(session, file, job, result_json)создан в Task 9 (R-2);CommittedJobs.cleanup()удаляетosv_entry,document. НастройкиUPLOAD_MAX_BYTES(50 МиБ),MAPPING_POLL_SECONDS(5),MAPPING_MAX_ATTEMPTS(3) +.env.example/.env.test.
2. Загрузка и статусы
- 2.1 Загрузка файлов.
domain/file_types.py(расширение →FileType/pipeline, проверка сигнатуры),services/primary_files.py(upload_one, цикл по файлам — в эндпоинте; дедуп, S3,stored_file/recognition_job/current_recognition_job_id, аудитFILE_UPLOAD),schemas/files.py(UploadResult{accepted,rejected},FileOut,JobBrief,DocumentBrief), эндпоинтPOST /legal-entities/{id}/files, роутер. Тесты: сценарии спекиprimary-files(принята, смесь, сигнатура, чужое ЮЛ, дубль);fixtures-тест: реальный xls принят иpublish_pendingнаFakeNatsпубликует запрос с ключомuploads/{file_id}/{job_id}/…. - 2.2 Статусы файла в обмене.
recognition_exchange.py: ack →PROCESSING;error→ERROR; вахтёрtimeout→ERROR; публикаторdlq→ERROR(все — только приcurrent_recognition_job_id = job);ResultMapper/NoopMapperудалены,_map_resultубран, тесты inbox обновлены (RecordingMapper→ проверка статуса файла). Тесты: сценарии спекиrecognition-job-exchange(ack → PROCESSING, error → ERROR, timeout → ERROR, done → файл остаётся PROCESSING).
3. Маппинг
- 3.1 Домен маппинга.
domain/osv_mapping.py:normalize_account_code,account_root,EntryDraft,build_entries(doc, chart)без resolve (R-3:EntryDraft.counterparty_idпроставляет цикл маппинга; пропуск TOTAL, parent по row_number,account_id),counterparty_check(header, owner_inn, owner_names_normalized),document_status(...);repositories/counterparties.py:CounterpartyIndex+resolve(inn, name) -> uuid | None(ИНН → имя → алиас, >1 → None). Unit-тесты: нормализация (60.1,76.7.1,76.АВ,МЦ.04→ root None), дерево и TOTAL наresult-osv-minimal.jsonи goldenОСВ_66 за 2024, резолв (ИНН/имя/алиас/неоднозначно), проверка шапки (ИНН, имя, алиас, пусто), статус документа. - 3.2 Цикл маппинга.
services/result_mapping.py:pick_candidate(FOR UPDATE SKIP LOCKED), проверка ключа (OQ-70), чтение S3 вне транзакции,map_file(одна транзакция: очистка прежних строк файла,document×N с обработкой повтора счёта, пакетная вставкаosv_entry,group_schema,is_cumulativeпо Q1 (D-9),metrics.mapping_warnings,PROCESSED), повторыmapping_attempts→ERROR,run_mapping_loopв lifespan (ExchangeTasks). Интеграционные тесты: сценарии спекиosv-result-mappingнаFakeStorageс golden JSON (ОСВ_66), ключ вне папки, сбой с повтором (monkeypatch), идемпотентность, два листа одного счёта, MISMATCH шапки, нарастающая по Q1, RECOGNIZED. - 3.3 Автозамена. В
map_fileпослеPROCESSED: поиск прежних файлов по документам,SUPERSEDED,version_no, аудитFILE_REPLACE(системное событие). Тесты: сценарий спеки (CONFIRMED заменяется,confirmed=trueв description), два разных счёта не заменяют друг друга,include_superseded. - 3.4 Golden-прогон.
tests/test_mapping_golden.py(fixtures): все../fixtures/golden/expected_osv_parse/**/*.json→ файл + заданиеdone+ маппинг черезmap_file; инварианты спеки (строки без TOTAL, родители, суммы,account_id). Время: ≤ 60 с на все 82.
4. API и удаление
- 4.1 API чтения и подтверждение.
repositories/files.py,services/documents.py,schemas/documents.py, эндпоинтыGET /legal-entities/{id}/files,GET /files/{id},GET /documents/{id},GET /documents/{id}/entries,POST /documents/{id}/confirm; policy через ЮЛ-владельца. Тесты: сценарии спекиdocuments-api;test_openapi_routes. - 4.2 Удаление файла.
DELETE /files/{id}: проверкиCONFIRMED/superseded_by, удаление строк/документов/заданий/файла,s3_object.expires_at, аудитFILE_DELETE. Тесты: сценарии спеки.
5. Сквозной прогон и документы
- 5.1 E2E через compose.
tests/e2e/test_osv_ingest_e2e.py(маркерыintegration,e2e): при поднятом профилеservices(реальныйtabular-parserпротив общего NATS/MinIO) — загрузкаrb/2024-FY/ОСВ_66 за 2024.xlsчерез API ядра, ожиданиеPROCESSED(≤ 60 с), проверка документа и 9 строк (10 строк снимка минус TOTAL); без профиля — skip с причиной.make e2eцель;check-fullего включает (skip допустим). - 5.2 Документы. README (загрузка: curl-пример, статусы файла и документа, цикл маппинга и настройки, API, удаление и замена,
ограничения), CLAUDE.md (DBML v5.15,
domain/osv_mapping.py,services/primary_files.py,services/result_mapping.py, три фоновых цикла),qa-guideв docs — сценарии загрузки (в docs-change);make check-fullзелёный;tasks.md[x]. - 5.3 После merge:
openspec archive slice-2c-osv-ingest.