feat: align source data and nightly exports
This commit is contained in:
@@ -21,13 +21,13 @@
|
||||
| ЕИС закупки: HTTP fallback | `https://zakupki.gov.ru/opendata/download/notifications/{region}/{year}/...` | ZIP-архивы с XML-файлами закупок, если SOAP-токен не используется или передана прямая ссылка. |
|
||||
| ЕИС/FAS generic-источники | `https://zakupki.gov.ru/epz/order/extendedsearch/results.html`, `https://zakupki.gov.ru/epz/orderclause/search/results.html`, `https://zakupki.gov.ru/epz/contract/search/results.html`, `https://zakupki.gov.ru/epz/dishonestsupplier/search/results.html`, `https://fas.gov.ru/pages/activity/reestr-uridicheskih-lic` | HTML-страницы официальных реестров. Парсер извлекает карточки/таблицы: закупки 44-ФЗ, закупки 223-ФЗ, контракты, недобросовестные поставщики, сведения ФАС по ГОЗ. |
|
||||
| ФНС: бухгалтерская отчетность | Автоматического HTTP-скачивания с ФНС в текущем коде не найдено. В каталоге источников указан справочный URL `https://bo.nalog.gov.ru/advanced-search/organizations/search?...` | Обрабатываются локально загруженные или положенные в папку `input/fns` файлы `fin_{id}_{ogrn}.xlsx`, а также ZIP-архивы с такими файлами. Из Excel берутся строки форм N 1, 2, 3, 4, 6 бухгалтерской отчетности. |
|
||||
| КАД Арбитр через Checko | Официальный источник в каталоге: `https://kad.arbitr.ru/`; фактический lookup в коде: `https://api.checko.ru/v2/legal-cases` | JSON-ответы по арбитражным делам для активных организаций из внутренних реестров. В запрос передаются ИНН/ОГРН. В payload сохраняются номер дела, суд, тип, статус, даты, суммы, стороны и ссылка на карточку. |
|
||||
| Федресурс/ЕФРСБ | `https://bankrot.fedresurs.ru/`; fallback: `https://api.checko.ru/v2/company` | Официальный источник обрабатывается как HTML/структурированная выгрузка. При недоступности портала используется Checko: по ИНН/ОГРН организации берутся сведения о банкротных сообщениях из JSON. |
|
||||
| КАД Арбитр через внешний сервис данных | Официальный источник в каталоге: `https://kad.arbitr.ru/`; фактический lookup выполняется через служебный API внешнего сервиса | JSON-ответы по арбитражным делам для активных организаций из внутренних реестров. В запрос передаются ИНН/ОГРН. В payload сохраняются номер дела, суд, тип, статус, даты, суммы, стороны и ссылка на карточку. |
|
||||
| Федресурс/ЕФРСБ | `https://bankrot.fedresurs.ru/`; fallback выполняется через служебный API внешнего сервиса | Официальный источник обрабатывается как HTML/структурированная выгрузка. При недоступности портала по ИНН/ОГРН организации запрашиваются сведения о банкротных сообщениях из JSON. |
|
||||
| ФСТЭК | `https://reestr.fstec.ru/reg3` и найденные на странице ссылки вида `module=rfiles` или `/uploads/reg...` | HTML-страница реестра, затем CSV/файловая выгрузка, если ссылка найдена. Для этого источника в коде отключена SSL-верификация. |
|
||||
| Вакансии: Работа России | Клиент использует `http://opendata.trudvsem.ru/api/v1/vacancies`, `http://opendata.trudvsem.ru/api/v1/vacancies/company/inn/{inn}`; в каталоге источников указан `https://opendata.trudvsem.ru/api/v1/vacancies` | JSON-список вакансий, включая работодателя, ИНН/ОГРН при наличии, название вакансии, дату, зарплату, статус, ссылку. |
|
||||
| Вакансии: HeadHunter | `https://api.hh.ru/vacancies` | JSON-список вакансий. Поиск выполняется по региону и/или тексту, для организаций без поиска по ИНН используется нормализованное название. |
|
||||
| Вакансии: SuperJob | `https://api.superjob.ru/2.0/vacancies/` | JSON-список вакансий. Используется только если задан `SUPERJOB_APP_ID`; ключ передается в заголовке `X-Api-App-Id`. |
|
||||
| Checko: контракты и проверки по организациям | `https://api.checko.ru/v2/contracts`, `https://api.checko.ru/v2/inspections` | JSON-данные по контрактам и проверкам для активных организаций из внутренних реестров. В запрос передаются ИНН/ОГРН, API-ключ передается параметром `key`. |
|
||||
| Внешний сервис данных: контракты и проверки по организациям | Служебные API контрактов и проверок | JSON-данные по контрактам и проверкам для активных организаций из внутренних реестров. В запрос передаются ИНН/ОГРН, API-ключ передается параметром `key`. |
|
||||
| Proxy-Tools | `https://proxy-tools.com/api/v1/proxies` | Служебная загрузка списка RU-прокси для парсеров. Используется только при заданном `PROXY_TOOLS_API_KEY`; запрос идет с Bearer-токеном. |
|
||||
|
||||
## Форматы загружаемых данных
|
||||
@@ -51,7 +51,7 @@
|
||||
- коды регионов;
|
||||
- ИНН/ОГРН организаций из внутренних активных реестров;
|
||||
- поисковая строка по названию организации для вакансий;
|
||||
- служебные ключи API из окружения: `ZAKUPKI_TOKEN`, `CHECKO_API_KEY`, `SUPERJOB_APP_ID`, `PROXY_TOOLS_API_KEY`.
|
||||
- служебные ключи API из окружения для ЕИС, внешнего сервиса данных, SuperJob и Proxy-Tools.
|
||||
|
||||
Ключи в коде не захардкожены, берутся из переменных окружения.
|
||||
|
||||
|
||||
95
docs/source-record-export-matrix-ru.md
Normal file
95
docs/source-record-export-matrix-ru.md
Normal file
@@ -0,0 +1,95 @@
|
||||
# Матрица файловых выгрузок источников
|
||||
|
||||
## Пользовательский контракт
|
||||
|
||||
Frontend отправляет администраторский
|
||||
`POST /api/v2/organization-source-records/export-ticket/` с массивом `sources`
|
||||
и выбранным `format`. В ответ он получает короткоживущий одноразовый ticket и
|
||||
передаёт его обычной HTML-формой в
|
||||
`POST /api/v2/organization-source-records/export-download/`. Поэтому браузер
|
||||
сохраняет потоковый ZIP напрямую на диск, не удерживая весь архив как Blob в
|
||||
JavaScript. Ticket передаётся в теле формы, не попадает в URL и после первого
|
||||
запроса становится недействительным.
|
||||
|
||||
Совместимый администраторский
|
||||
`POST /api/v2/organization-source-records/export/` по-прежнему сразу возвращает
|
||||
тот же ZIP API-клиентам. Крупный XLSX может состоять из нескольких файлов
|
||||
`*-part-001.xlsx`, `*-part-002.xlsx` и далее.
|
||||
|
||||
При скачивании endpoint не читает таблицы записей источников и не строит CSV,
|
||||
XLSX или JSON заново. Он упаковывает файлы последнего полностью опубликованного
|
||||
ночного поколения и сразу потоково отправляет ZIP без временной копии всего
|
||||
архива. Поэтому `Content-Length` у ответа отсутствует. Если ни одного поколения
|
||||
ещё нет, API отвечает `503` с кодом `source_export_not_ready`.
|
||||
|
||||
## Матрица
|
||||
|
||||
| Группа API | Файл | CSV | XLSX | JSON |
|
||||
|---|---|:---:|:---:|:---:|
|
||||
| `financial_indicators` | `financial-indicators` | — | — | да |
|
||||
| `government_procurements` | `public-procurements` | да | да | да |
|
||||
| `industrial_production` | `manufacturers-and-products` | да | да | да |
|
||||
| `planned_inspections` | `planned-inspections` | да | да | да |
|
||||
| `bankruptcy` | `bankruptcy-procedures` | да | да | да |
|
||||
| `defense_suppliers` | `defense-unreliable-suppliers` | да | да | да |
|
||||
| `arbitration` | `arbitration-cases` | да | да | да |
|
||||
| `security_registries` | `information-security-registries` | да | да | да |
|
||||
| `vacancies` | `labor-vacancies` | да | да | да |
|
||||
|
||||
Итого формируется 25 логических артефактов: один JSON для финансовых показателей
|
||||
и по три формата для остальных восьми групп. Физических файлов может быть
|
||||
больше из-за разбиения крупных XLSX. Если финансовые показатели выбраны вместе
|
||||
с другим форматом, в ZIP для них всё равно включается JSON.
|
||||
|
||||
## Ночная генерация
|
||||
|
||||
Celery Beat запускает
|
||||
`organizations.tasks.refresh_source_record_export_artifacts` ежедневно в
|
||||
`05:30 Europe/Moscow`, после ежедневного обновления organization sources в
|
||||
`04:30`.
|
||||
|
||||
Генератор:
|
||||
|
||||
1. читает каждую группу из БД один раз без глобальной сортировки миллионов строк;
|
||||
2. пишет compact JSON-массив на диск и использует его как готовый JSON без второй копии;
|
||||
3. потоково создаёт CSV и XLSX без накопления всех строк в памяти;
|
||||
4. разбивает XLSX по умолчанию по 100 000 строк на отдельные файлы, ограничивая временный XML и не превышая лимит Excel;
|
||||
5. записывает размеры файлов и номера частей в manifest;
|
||||
6. атомарно переключает `current.json` только после готовности всей матрицы;
|
||||
7. сохраняет текущее и предыдущее поколения по умолчанию.
|
||||
|
||||
При ошибке незавершённое поколение удаляется, а download endpoint продолжает
|
||||
отдавать предыдущую успешную версию.
|
||||
|
||||
### Расчёт диска
|
||||
|
||||
Атомарная публикация требует одновременно хранить уже опубликованные поколения
|
||||
и одно новое поколение в staging. Минимальный запас под артефакты рассчитывается
|
||||
как `(SOURCE_RECORD_EXPORT_GENERATIONS_TO_KEEP + 1) * размер поколения`, плюс
|
||||
рабочий запас файловой системы. На снимке dev от 2026-08-03 одно поколение
|
||||
заняло 16,55 ГБ (54 физических файла), поэтому при значении `2` следует
|
||||
выделить не менее 55 ГБ свободного места под каталог выгрузок. Временная копия
|
||||
целого ZIP при скачивании не создаётся.
|
||||
|
||||
## Хранение и первый запуск
|
||||
|
||||
Каталог задаётся через `SOURCE_RECORD_EXPORT_DIRECTORY`, по умолчанию —
|
||||
`media/source-record-exports`. Он должен быть общим read-write volume для web и
|
||||
Celery worker. В Docker Compose используется `./media:/app/media`.
|
||||
|
||||
После первого развёртывания готовое поколение можно создать сразу, не ожидая
|
||||
ночного расписания:
|
||||
|
||||
```bash
|
||||
PYTHONPATH=src uv run python src/manage.py build_source_record_exports
|
||||
```
|
||||
|
||||
Доступные настройки:
|
||||
|
||||
| Настройка | Значение по умолчанию | Назначение |
|
||||
|---|---:|---|
|
||||
| `SOURCE_RECORD_EXPORT_DIRECTORY` | `media/source-record-exports` | Общий каталог артефактов |
|
||||
| `SOURCE_RECORD_EXPORT_GENERATIONS_TO_KEEP` | `2` | Число сохраняемых успешных поколений |
|
||||
| `SOURCE_RECORD_EXPORT_LOCK_TTL_SECONDS` | `21600` | TTL распределённой блокировки Celery |
|
||||
| `SOURCE_RECORD_EXPORT_XLSX_ROWS_PER_FILE` | `100000` | Максимум строк данных в одной XLSX-части |
|
||||
| `SOURCE_RECORD_EXPORT_DOWNLOAD_TICKET_TTL_SECONDS` | `300` | Срок действия одноразового browser-download ticket |
|
||||
Reference in New Issue
Block a user