14 KiB
14 KiB
Фаза 2, Шаг 1: Внешние источники данных — FR24 API + онлайн табло
Статус
🔲 Не начат
Цель
Развернуть отдельный контейнер для сбора данных из внешних источников:
- FR24 API — исторические и live треки (как в прототипе noisemap)
- Онлайн табло — расписание рейсов по аэропортам Москвы (SVO, DME, VKO, ZIA)
Данные сохранять в PostgreSQL в отдельной схеме fr24_ext — не пересекаться со схемой fr24 (RTL-SDR ingest).
Бизнес-требования
BR-1: Сбор данных онлайн табло (приоритет)
- Аэропорты: SVO (Шереметьево), DME (Домодедово), VKO (Внуково), ZIA (Жуковский)
- Режим: T-1 (загрузка на следующий день за предыдущие сутки)
- Данные: номер рейса, авиакомпания, направление (прилёт/вылет), запланированное время, фактическое время (с учётом задержек/отмен), статус, ICAO24 борта (если доступен)
- Источники:
- Яндекс.Расписания API (основной) — расписание, статусы, маршруты
- OpenSky Network API (дополнительный) — фактические времена, ICAO24 бортов
- Глубина хранения: 3 года (параметр, изменяемый)
- Старт загрузки: с 01.04.2026
- Догрузка: возможность загружать старые периоды с учётом rate limits
BR-2: UI для просмотра табло
- Расположение: в существующем фронтенде (
http://192.168.2.67:8080/schedule) - Формат: таблица с фильтрами
- Фильтры:
- Дата (диапазон)
- Тип (прилёт/вылет/все)
- Аэропорт (SVO/DME/VKO/ZIA/все)
- Номер рейса (поиск)
- Часовой интервал (например, 06:00-12:00)
- Экспорт: CSV
- Адаптация: мобильная версия обязательна
BR-3: Сбор данных FR24 API (отложен до Шага 2)
- Загрузка треков рейсов над Московской областью через FR24 API
- Покрытие: bbox ~54.5–57.0°N, 35.5–40.5°E
- Стратегия: будет определена в Шаге 2 (витрина данных)
- На Шаге 1 — только табло, FR24 треки позже
BR-4: Хранение
- Схема
fr24_extв существующей PostgreSQL БД - Не влиять на схему
fr24(RTL-SDR данные) - Retention: табло — 3 года (параметр)
Технические требования
Контейнер fr24-schedule
- Python 3.11-slim
- Отдельный сервис в docker-compose.yml
- Переменные окружения: YANDEX_RASP_API_KEY, OPENSKY_USERNAME, OPENSKY_PASSWORD (опционально)
- Два независимых воркера: yandex_worker и opensky_worker
- Логирование в stdout (Docker logs)
Схема БД fr24_ext
-- Табло аэропортов (объединённые данные из источников)
fr24_ext.schedule (
schedule_id BIGSERIAL PRIMARY KEY,
flight_date DATE NOT NULL,
airport_iata CHAR(3) NOT NULL, -- SVO, DME, VKO, ZIA
direction VARCHAR(10) NOT NULL, -- 'arrival' | 'departure'
flight_number VARCHAR(10) NOT NULL,
airline_iata CHAR(2),
airline_name VARCHAR(100),
origin_iata CHAR(3),
destination_iata CHAR(3),
aircraft_type VARCHAR(10),
scheduled_at TIMESTAMPTZ NOT NULL,
estimated_at TIMESTAMPTZ,
actual_at TIMESTAMPTZ,
status VARCHAR(20), -- 'scheduled', 'delayed', 'cancelled', 'departed', 'arrived'
icao24 CHAR(6), -- из OpenSky, если доступен
source VARCHAR(20) NOT NULL, -- 'yandex' | 'opensky' | 'merged'
fetched_at TIMESTAMPTZ NOT NULL DEFAULT now(),
UNIQUE(flight_number, airport_iata, scheduled_at, direction)
);
CREATE INDEX idx_schedule_date ON fr24_ext.schedule(flight_date);
CREATE INDEX idx_schedule_airport ON fr24_ext.schedule(airport_iata);
CREATE INDEX idx_schedule_flight ON fr24_ext.schedule(flight_number);
CREATE INDEX idx_schedule_time ON fr24_ext.schedule(scheduled_at);
-- Состояние загрузки (курсор для догрузки)
fr24_ext.load_state (
state_key VARCHAR(50) PRIMARY KEY,
state_value JSONB NOT NULL,
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- Пример state_value для догрузки:
-- {"last_loaded_date": "2026-04-01", "airports": ["SVO", "DME", "VKO", "ZIA"]}
Воркеры
yandex_worker.py:
- Загрузка расписания через Яндекс.Расписания API
- Endpoint:
https://api.rasp.yandex.net/v3.0/schedule/ - Параметры:
station={код станции}&date={YYYY-MM-DD} - Коды станций: SVO=s9600213, DME=s9600366, VKO=s9600215, ZIA=s9881291
- Upsert в
fr24_ext.scheduleпо(flight_number, airport_iata, scheduled_at, direction) - Rate limit: ~100 запросов/мин (не документирован, консервативная оценка)
opensky_worker.py:
- Загрузка arrivals/departures через OpenSky Network API
- Endpoint:
https://opensky-network.org/api/flights/arrivalи/departure - Параметры:
airport={ICAO}&begin={unix_ts}&end={unix_ts} - ICAO коды: SVO=UUEE, DME=UUDD, VKO=UUWW, ZIA=UUBW
- Обогащение существующих записей в
fr24_ext.schedule(добавление icao24) - Rate limit: 4000 запросов/день для зарегистрированных, 400/день для анонимов
- Батчинг: 1 день = 1 запрос на аэропорт × 2 направления = 8 запросов/день
Режимы работы
T-1 режим (основной):
- Запуск: ежедневно в 02:00 UTC (05:00 MSK)
- Загружает данные за вчерашний день (T-1)
- Последовательность: сначала Яндекс (расписание), потом OpenSky (обогащение icao24)
Догрузка исторических данных:
- Скрипт
backfill.pyс параметрами--start-date 2026-04-01 --end-date 2026-04-19 - Учитывает rate limits: пауза между запросами, батчинг
- Сохраняет прогресс в
fr24_ext.load_state - При ошибке — возобновление с последней успешной даты
ТЗ для Dev-агента
📋 Детальная спецификация: docs/PHASE2_STEP1_DETAILED_SPEC.md
Детальная спецификация содержит:
- Архитектуру контейнера и структуру процессов
- Полный код модулей с обработкой ошибок
- Конфигурацию и переменные окружения
- Retry логику и rate limiting
- Docker setup и docker-compose интеграцию
- Retention и автоочистку
Файлы для создания
tasks/flightradar24/ingest/schedule/
Dockerfile
main.py # точка входа, запускает оба воркера + cron для T-1
yandex_worker.py # загрузка из Яндекс.Расписания
opensky_worker.py # загрузка из OpenSky Network
backfill.py # скрипт догрузки исторических данных
requirements.txt
tasks/flightradar24/db/init/003_schema_ext.sql # DDL схемы fr24_ext
tasks/flightradar24/frontend/schedule.html # UI для просмотра табло
tasks/flightradar24/frontend/schedule.js # логика фильтров и таблицы
Обновить
tasks/flightradar24/compose/docker-compose.yml— добавить сервисscheduletasks/flightradar24/frontend/main.py— добавить endpoints:GET /schedule— HTML страницаGET /api/schedule/data— JSON данные с фильтрамиGET /api/schedule/export— CSV экспорт
API endpoints (новые)
GET /api/schedule/data
Query params:
- date_from: YYYY-MM-DD
- date_to: YYYY-MM-DD
- airport: SVO|DME|VKO|ZIA|all (default: all)
- direction: arrival|departure|all (default: all)
- flight_number: string (поиск, опционально)
- time_from: HH:MM (опционально)
- time_to: HH:MM (опционально)
- limit: int (default: 100)
- offset: int (default: 0)
Response:
{
"total": 1234,
"flights": [
{
"flight_number": "SU1234",
"airline": "Аэрофлот",
"airport": "SVO",
"direction": "departure",
"origin": "SVO",
"destination": "LED",
"scheduled_at": "2026-04-19T10:30:00Z",
"actual_at": "2026-04-19T10:45:00Z",
"delay_min": 15,
"status": "departed",
"icao24": "151ABC"
},
...
]
}
GET /api/schedule/export
Query params: те же что у /data
Response: CSV файл
UI требования
Макет:
- Шапка с фильтрами (компактная, сворачиваемая на мобиле)
- Таблица с пагинацией (100 строк/страница)
- Кнопка экспорта CSV
Колонки таблицы:
- Дата
- Рейс (номер)
- Авиакомпания
- Аэропорт
- Направление (иконка ↑ вылет / ↓ прилёт)
- Маршрут (откуда → куда)
- Запланировано
- Фактически
- Задержка (мин)
- Статус (цветовая индикация)
Мобильная адаптация:
- Фильтры в выдвижной панели
- Таблица → карточки на экранах <768px
- Свайп для прокрутки таблицы на планшетах
Цветовая индикация статусов:
scheduled— серыйdeparted/arrived— зелёныйdelayed— жёлтыйcancelled— красный
Тест-кейсы
TC-1: Яндекс.Расписания подключение
- Контейнер стартует без ошибок
- Яндекс API возвращает данные для SVO за вчерашний день
- Данные сохраняются в
fr24_ext.schedule - Дубликаты не создаются (upsert работает)
TC-2: OpenSky Network подключение
- OpenSky API возвращает arrivals/departures для UUEE (SVO)
- icao24 добавляется к существующим записям в
fr24_ext.schedule - Rate limit соблюдается (не более 4000 запросов/день)
TC-3: T-1 режим
- Ежедневный запуск в 02:00 UTC работает
- Загружаются данные за вчерашний день (T-1)
- Все 4 аэропорта обрабатываются
TC-4: Догрузка исторических данных
backfill.py --start-date 2026-04-01 --end-date 2026-04-19завершается успешно- Прогресс сохраняется в
fr24_ext.load_state - При прерывании — возобновление с последней успешной даты
- Rate limits соблюдаются (паузы между запросами)
TC-5: UI табло
- Страница
/scheduleоткрывается - Фильтры работают (дата, аэропорт, направление, номер рейса, время)
- Пагинация работает (100 строк/страница)
- Экспорт CSV работает
- Мобильная версия адаптирована (карточки вместо таблицы на <768px)
TC-6: Изоляция схем
- Запросы к
fr24_extне влияют наfr24 - При падении schedule контейнера остальные работают
TC-7: Retention
- Данные старше 3 лет удаляются автоматически (cron job)
Зависимости
- Существующая PostgreSQL (fr24-postgres)
- YANDEX_RASP_API_KEY в ~/.openclaw/.env
- OPENSKY_USERNAME, OPENSKY_PASSWORD в ~/.openclaw/.env (опционально, для повышенного лимита)
Оценка объёма
Данные
- ~800 рейсов/день (4 аэропорта × ~200 рейсов)
- 3 года = ~876K записей
- Размер записи: ~200 байт
- Объём БД: ~175 MB за 3 года
Догрузка с 01.04.2026
- 20 дней × 4 аэропорта = 80 дней данных
- Яндекс: 80 запросов (1 запрос/аэропорт/день)
- OpenSky: 160 запросов (2 направления × 4 аэропорта × 20 дней)
- Укладывается в лимиты при батчинге
Rate limits стратегия
- Яндекс: пауза 1 сек между запросами (консервативно)
- OpenSky: пауза 30 сек между запросами (4000/день = ~1 запрос/22 сек)
- Догрузка 20 дней: ~1.5 часа при последовательной обработке