Files
wiki/tasks/flightradar24/docs/PHASE2_STEP1_EXTERNAL_DATA.md
2026-04-20 17:20:01 +03:00

15 KiB
Raw Blame History

Фаза 2, Шаг 1: Внешние источники данных — FR24 API + онлайн табло

Статус

Завершён (20.04.2026)

Итоги деплоя

  • Контейнер fr24-schedule запущен и healthy
  • Схема fr24_ext создана (таблицы schedule, load_state)
  • Backfill выполнен: 28 690 рейсов за 01.0419.04.2026
  • Cron: ежедневно в 02:00 UTC, режим T-1
  • UI: http://192.168.2.67:8080/schedule
  • API: GET /api/schedule/data, GET /api/schedule/export

Отклонения от плана

  • OpenSky отключён — исторические данные требуют платный аккаунт (403 Forbidden). Обогащение icao24 не работает.
  • DDL правки в процессе деплоя: flight_number VARCHAR(50), aircraft_type VARCHAR(100) — Яндекс возвращает полные названия самолётов ("Сухой Суперджет 100")
  • ZIA код станции: исправлен с s9881291 на s9850865
  • Файл миграции: 004_schema_ext.sql (вместо 003 — конфликт с существующим файлом)

Цель

Развернуть отдельный контейнер для сбора данных из внешних источников:

  1. FR24 API — исторические и live треки (как в прототипе noisemap)
  2. Онлайн табло — расписание рейсов по аэропортам Москвы (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.557.0°N, 35.540.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 — добавить сервис schedule
  • tasks/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 часа при последовательной обработке