Introduce background scheduling and migrations for analytics ingest, expand parser management and map toolbar UX, and ignore local data/session files from version control. Co-authored-by: Cursor <cursoragent@cursor.com>
MapMil Platform (ЦП → ЦА → ПИ)
Единая платформа: ЦА (аналитика и карта) + ЦП (парсинг Telegram).
Архитектура
flowchart LR
CA[ЦА Analytics Center]
CP[ЦП Parsing Center]
PI[ПИ External Consumers]
CA -->|jobs via Redis| CP
CP -->|POST /internal/ingest| CA
CA -->|GET /api/v1/events| PI
| Центр | Контейнеры | Назначение |
|---|---|---|
| ЦА | ca-db, ca-api, ca-frontend |
PostgreSQL, ingest API, карта, distribution API |
| ЦП | cp-workers |
Парсинг Telegram: real-time listener (Telethon) + batch-задания из Redis |
| Общее | redis |
Очередь заданий ЦА → ЦП |
Структура monorepo
MapMil/
├── centers/
│ ├── analytics/
│ │ ├── api/ # CA backend (FastAPI + PostgreSQL)
│ │ └── frontend/ # CA admin UI (Vue + Leaflet)
│ └── parsing/
│ └── workers/ # CP workers (Telethon)
├── contracts/ # Shared schemas (ingest, jobs)
├── data/ # telegram.session (локально, не в git)
├── docker-compose.yml
└── .env # TELEGRAM_API_ID, TELEGRAM_API_HASH, …
Быстрый старт
- Создайте
.envиз примера и укажите ключи Telegram:
cp .env.example .env
# отредактируйте TELEGRAM_API_ID и TELEGRAM_API_HASH
- Положите сессию Telegram в
data/telegram.session(файл Telethon SQLite). Если мигрируете со старого SocialParser:
cp ../SocialParser/data/telegram.session data/
- Запуск:
docker compose up --build
- Откройте UI: http://localhost:8080
Admin UI
Веб-интерфейс ЦА доступен по тем же адресу. Навигация в шапке:
| Раздел | Путь | Описание |
|---|---|---|
| Карта | / |
Интерактивная карта событий: навигация по датам (flatpickr), пресеты периода, фильтры региона/темы/источника, подложки Яндекс/OSM/Topo/ESRI, линейка, полноэкранный режим, центрирование по координатам и городам, поиск населённых пунктов (Nominatim). CRUD для ручных объектов (ПКМ). Поддерживает ?eventId= |
| Парсеры | /parsers |
Telegram-парсеры с периодическим запуском, настройка интервала, редактирование и удаление; дубликаты по source_url не записываются |
| События | /events |
Фильтрация, пагинация, просмотр деталей, ссылка «На карте» для событий с координатами |
| Аналитика | /analytics |
KPI-карточки, график динамики ingest за 30 дней, топ населённых пунктов и регионов |
| ПИ | /consumers |
CRUD подписчиков distribution API, ротация ключей, тест среза через /api/v1/events |
Сохранение Telegram-сессии
Важно: существующий файл сессии не удаляется и не пересоздаётся.
- Локально:
data/telegram.session(SQLite Telethon, в.gitignore) - В Docker
cp-workers: каталог./dataмонтируется как/data(read-write) - Путь в контейнере:
/data/telegram.session - Переменные из
.env:TELEGRAM_API_ID,TELEGRAM_API_HASH,TELEGRAM_SESSION_PATH=/data/telegram.session
Файл сессии и .env не коммитятся.
API
Карта
| Метод | Путь | Описание |
|---|---|---|
| GET | /api/health |
Health check |
| GET | /api/map/objects |
Объекты на карте с данными события; фильтры: date_from, date_to, on_date, region, topic, source_type, search, event_id |
| GET | /api/map/filters |
Справочники для фильтров: regions, topics, source_types, available_dates, date_bounds |
| GET | /api/objects |
Объекты (legacy, без фильтров события) |
| POST | /api/objects |
Создать объект вручную |
Инструменты карты (UI)
- Даты: кнопки ❮❮/❮/❯/❯❯, календарь flatpickr, пресеты периода (1 неделя — 1 год / день)
- Фильтры: регион, тема, источник (из
/api/map/filters) - Подложки: Яндекс Карты / Спутник (ru_RU), OpenStreetMap, OpenTopoMap, ESRI Satellite
- Инструменты: линейка (PolylineMeasure), полноэкранный режим, координаты/города, поиск Nominatim
- Точки: маркеры из ingest + popup; ручные объекты — CRUD и drag
- Обновление: polling
/api/map/objectsкаждые 30 с
ЦА Admin
| Метод | Путь | Описание |
|---|---|---|
| POST | /admin/jobs |
Создать парсер (сразу в очередь + периодический запуск) |
| GET | /admin/jobs |
Список парсеров |
| PATCH | /admin/jobs/{id} |
Изменить канал/лимит, interval_seconds, is_active |
| DELETE | /admin/jobs/{id} |
Удалить парсер |
| POST | /admin/jobs/{id}/retry |
Повторить failed-задание |
| GET | /admin/events |
События с фильтрами ({ items, total }) |
| GET | /admin/analytics/summary |
KPI-сводка |
| GET | /admin/analytics/timeline?days= |
Динамика ingest по дням |
| GET | /admin/analytics/top-localities?limit= |
Топ населённых пунктов |
| GET | /admin/analytics/top-regions?limit= |
Топ регионов |
| GET | /admin/consumers |
Список подписчиков ПИ |
| POST | /admin/consumers |
Создать подписчика ПИ |
| PATCH | /admin/consumers/{id} |
Обновить подписчика |
| POST | /admin/consumers/{id}/rotate-key |
Сменить API-ключ |
Пример задания Telegram:
curl -X POST http://localhost:8080/admin/jobs \
-H 'Content-Type: application/json' \
-d '{"source_type":"telegram","source_config":{"channel":"creamy_caprice","limit":50}}'
ЦП → ЦА (internal)
| Метод | Путь | Описание |
|---|---|---|
| POST | /internal/ingest |
Приём batch событий (заголовок X-Internal-Token) |
ПИ Distribution API
| Метод | Путь | Описание |
|---|---|---|
| GET | /api/v1/events |
События с фильтром по API-ключу |
Тестовый consumer test-pi создаётся при старте с ключом из TEST_PI_API_KEY (по умолчанию test-pi-api-key-change-me):
curl http://localhost:8080/api/v1/events \
-H 'Authorization: Bearer test-pi-api-key-change-me'
Парсинг Telegram
cp-workers объединяет два режима в одном процессе (общая сессия telegram.session):
| Режим | Как работает |
|---|---|
| Listener (real-time) | Telethon NewMessage / Album на активных парсерах (is_active=true); новый пост сразу уходит в ingest |
| Batch (по расписанию) | Планировщик в ca-api ставит задание в Redis; воркер забирает последние N постов (iter_messages) |
Дубликаты по source_url при ingest пропускаются. Listener не меняет status парсера (флаг listener: true в ingest).
Переменные cp-workers:
| Переменная | По умолчанию | Описание |
|---|---|---|
TELEGRAM_LISTENER_ENABLED |
true |
Включить real-time listener |
TELEGRAM_LISTENER_REFRESH_SECONDS |
60 |
Как часто обновлять список каналов из БД |
Internal API: GET /internal/listener/subscriptions — список активных каналов для listener.
Поток данных
- Аналитик создаёт парсер:
POST /admin/jobs(канал, лимит, интервал в секундах) - Listener сразу подписывается на канал и ingest-ит новые посты
- Планировщик
ca-apiпериодически ставит batch-задание в Redis (cp:jobs) cp-workersзабирает batch-задание, парсит последние N постов- Результаты →
POST /internal/ingest(дубликаты поsource_urlпропускаются) - Новые события с координатами появляются на карте как
MapObject - Внешние ПИ получают срез через
/api/v1/events
Миграция EventRecord → Event
| SocialParser | CA Event |
|---|---|
event |
description / title |
date (dd.mm.yy) |
event_date |
geolocation |
latitude, longitude |
locality |
locality, region |
source_url |
source_url |
| — | source_type = "telegram" |
Остановка
docker compose down
Данные PostgreSQL сохраняются в volume pgdata.
Legacy
Старые каталоги backend/ и frontend/ в корне оставлены для справки; активная разработка — в centers/.