feat: limit source exports to current year
All checks were successful
CI/CD Pipeline / Code Quality Checks (push) Successful in 3m16s
CI/CD Pipeline / Run Tests (push) Successful in 4m58s
CI/CD Pipeline / Build and Push Dev Images (push) Successful in 32s
CI/CD Pipeline / Deploy Dev via Compose (push) Successful in 33s

This commit is contained in:
2026-08-04 23:25:36 +02:00
parent c2a09a0403
commit 2b9ecc2681
8 changed files with 316 additions and 30 deletions

View File

@@ -9,6 +9,11 @@
теле обычной HTML-формы на
`POST /api/v2/organization-source-records/export-download/`.
Имя скачиваемого архива формируется по выбранным источникам и времени создания
запроса: `<source-stem>_YYYYMMDD_HHMMSS.zip`, а для нескольких источников их
имена соединяются через `__`. Значение `file_name` из ticket и итоговый
`Content-Disposition` совпадают.
Браузер получает потоковый ZIP напрямую, без многогигабайтного `Blob` в
JavaScript. Ticket не попадает в URL и после первого запроса становится
недействительным. Совместимый администраторский endpoint
@@ -19,6 +24,13 @@ JavaScript. Ticket не попадает в URL и после первого з
готовые файлы последнего ночного поколения. При отсутствии поколения API
возвращает `503` с кодом `source_export_not_ready`.
Каждое поколение содержит только текущий календарный год в timezone сервиса.
Для моделей с предметной датой год определяется по ней, для моделей без такой
даты — по `created_at`, для финансовых отчётов — по году строк отчёта. Строки
других лет из финансового отчёта не выгружаются. После смены года старое
поколение не раздаётся: до успешной сборки нового года API отвечает
`503 source_export_not_ready`.
## Матрица
| Группа API | Таблицы State Corp | Файл | CSV | XLSX | JSON |
@@ -39,9 +51,9 @@ JavaScript. Ticket не попадает в URL и после первого з
используют тот же контракт, что и Mostovik: реквизиты организации, включая ОКПО,
общие поля записи источника и специфичные поля в `payload.*`.
Все записи включаются в публичные файлы, а техническое наименование внешнего
поставщика нейтрализуется. Исходные значения в БД сохраняются для работы
интеграции и дедупликации.
Все записи текущего года включаются в публичные файлы, а техническое
наименование внешнего поставщика нейтрализуется. Исходные значения в БД
сохраняются для работы интеграции и дедупликации.
Физических XLSX-файлов может быть больше: по умолчанию один файл содержит не
более 100 000 строк данных и получает суффикс `-part-001`, `-part-002` и далее.
@@ -55,11 +67,13 @@ Celery Beat запускает
Генератор:
1. читает каждую нормализованную таблицу один раз без model-level сортировки;
предметные даты и fallback по `created_at` индексированы;
2. создаёт компактный JSON-массив и переиспользует его как готовый JSON;
3. потоково формирует CSV и write-only XLSX;
4. атомарно публикует `current.json` только после готовности всей матрицы;
5. при ошибке удаляет staging и продолжает отдавать предыдущее поколение;
6. сохраняет текущее и предыдущее поколения по умолчанию.
4. записывает календарный `export_year` в manifest;
5. атомарно публикует `current.json` только после готовности всей матрицы;
6. при ошибке удаляет staging и продолжает отдавать предыдущее поколение того же года;
7. сохраняет текущее и предыдущее поколения по умолчанию.
Web и Celery worker должны использовать общий read-write volume `/app/media`.
@@ -81,3 +95,8 @@ PYTHONPATH=src uv run python src/manage.py build_source_record_exports
Для атомарной генерации требуется свободное место не меньше
`(GENERATIONS_TO_KEEP + 1) * размер поколения` плюс запас файловой системы.
На локальном снимке от 2026-08-04 поколение за 2026 год содержит 1 962 записи
и занимает 4 738 872 байта (4,52 MiB) для всей матрицы. Архив со всеми
источниками оценивается в 2,49 MiB для JSON, 1,59 MiB для CSV и 0,44 MiB для
XLSX; к сумме добавляется небольшой служебный overhead ZIP.