Files
MapMil/README.md
T
gitrusprusandCursor 3f9dc6643b Add reusable LLM parser profiles with multi-event extract.
Support kind=llm profiles (instruction/schema), optional multi-event posts via #eN URLs, and recover stale running/queued parse jobs after worker crashes.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-13 20:22:38 +03:00

11 KiB
Raw Blame History

MapMil Platform (ЦП → ЦА → ПИ)

Единая платформа: ЦА (аналитика и карта) + ЦП (адаптеры парсинга: Telegram, Crawl4AI, VIINA).

Документация для разработчиков: docs/ — обзор архитектуры, поток данных, локальный запуск.

Архитектура

flowchart LR
  CA[ЦА Analytics Center]
  CP[ЦП Parsing Center]
  PI[ПИ External Consumers]

  CA -->|jobs via Redis families| CP
  CP -->|POST /internal/ingest| CA
  CA -->|GET /api/v1/events| PI
Центр Контейнеры Назначение Документ
ЦА ca-db, ca-api, ca-frontend PostgreSQL, ingest API, карта, distribution API centers/analytics/ARCHITECTURE.md
ЦП cp-workers, cp-workers-web, cp-workers-nlp Адаптеры: Telegram / Crawl4AI / VIINA centers/parsing/ARCHITECTURE.md
ПИ (маршрут в ЦА) GET /api/v1/events centers/analytics/DISTRIBUTION.md
Общее redis Очереди cp:jobs:{telegram|web|nlp} docs/contracts.md

Подробный обзор платформы: docs/architecture-overview.md.

Структура monorepo

MapMil/
├── centers/
│   ├── analytics/
│   │   ├── api/              # CA backend (FastAPI + PostgreSQL)
│   │   ├── frontend/         # CA admin UI (Vue + Leaflet)
│   │   ├── ARCHITECTURE.md
│   │   └── DISTRIBUTION.md   # ПИ
│   └── parsing/
│       ├── ARCHITECTURE.md
│       └── workers/          # CP workers + adapters
├── contracts/                # Shared schemas (ingest, jobs, sources, queues)
├── docs/                     # Документация для разработчиков
├── data/                     # telegram.session (локально, не в git)
├── docker-compose.yml
└── .env

Быстрый старт

  1. Создайте .env из примера и укажите ключи Telegram:
cp .env.example .env
# отредактируйте TELEGRAM_API_ID и TELEGRAM_API_HASH
  1. Положите сессию Telegram в data/telegram.session (файл Telethon SQLite). Если мигрируете со старого SocialParser:
cp ../SocialParser/data/telegram.session data/
  1. Запуск:
docker compose up --build
  1. Откройте UI: http://localhost:8080 — карта публичная; разделы админки — после входа (ADMIN_USER / ADMIN_PASSWORD из .env).

Admin UI

Веб-интерфейс ЦА доступен по тому же адресу. Карта (/) открыта всем. Остальные пункты навигации видны после логина (/login).

Раздел Путь Описание
Карта / Интерактивная карта событий (публичный просмотр). CRUD ручных объектов — только для админа (ПКМ). Поддерживает ?eventId=
Парсеры /parsers Связка канал + профиль (heuristic→extract_mode=profile, llm→extract_mode=llm); дедуп по source_url
Профили /parser-profiles Reusable heuristic (правила) или llm (instruction/schema); целевые поля = Event
События /events Фильтрация, пагинация, просмотр деталей, ссылка «На карте» для событий с координатами
Аналитика /analytics KPI-карточки, график динамики ingest за 30 дней, топ населённых пунктов и регионов
ПИ /consumers CRUD подписчиков distribution API, ротация ключей, тест среза через /api/v1/events

Авторизация админки: POST /admin/auth/login → JWT Bearer. Все /admin/* (кроме login) и мутации /api/objects требуют токен. GET /api/map/* публичен.

Сохранение 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 (нужен JWT после POST /admin/auth/login):

TOKEN=$(curl -s -X POST http://localhost:8080/admin/auth/login \
  -H 'Content-Type: application/json' \
  -d '{"username":"admin","password":"change-me"}' | jq -r .access_token)

curl -X POST http://localhost:8080/admin/jobs \
  -H "Authorization: Bearer $TOKEN" \
  -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.

Поток данных

  1. Аналитик создаёт парсер: POST /admin/jobs (канал, лимит, интервал в секундах)
  2. Listener сразу подписывается на канал и ingest-ит новые посты
  3. Планировщик ca-api периодически ставит batch-задание в Redis (cp:jobs)
  4. cp-workers забирает batch-задание, парсит последние N постов
  5. Результаты → POST /internal/ingest (дубликаты по source_url пропускаются)
  6. Новые события с координатами появляются на карте как MapObject
  7. Внешние ПИ получают срез через /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.

Вход в админку (/login):

Логин: admin Пароль: change-me