auto-sync: 2026-04-20 12:50:01
This commit is contained in:
@@ -14,107 +14,281 @@
|
|||||||
|
|
||||||
## Бизнес-требования
|
## Бизнес-требования
|
||||||
|
|
||||||
### BR-1: Сбор данных FR24 API
|
### BR-1: Сбор данных онлайн табло (приоритет)
|
||||||
- Загружать треки рейсов над Московской областью через FR24 API
|
- **Аэропорты:** SVO (Шереметьево), DME (Домодедово), VKO (Внуково), ZIA (Жуковский)
|
||||||
- Покрытие: bbox ~54.5–57.0°N, 35.5–40.5°E (Московская область)
|
- **Режим:** T-1 (загрузка на следующий день за предыдущие сутки)
|
||||||
- Исторические данные: последние 7 дней (при наличии кредитов)
|
- **Данные:** номер рейса, авиакомпания, направление (прилёт/вылет), запланированное время, фактическое время (с учётом задержек/отмен), статус, ICAO24 борта (если доступен)
|
||||||
- Live данные: обновление каждые 5 минут
|
- **Источники:**
|
||||||
- Хранить: icao24, callsign, координаты трека, высота, скорость, время
|
- Яндекс.Расписания API (основной) — расписание, статусы, маршруты
|
||||||
|
- OpenSky Network API (дополнительный) — фактические времена, ICAO24 бортов
|
||||||
|
- **Глубина хранения:** 3 года (параметр, изменяемый)
|
||||||
|
- **Старт загрузки:** с 01.04.2026
|
||||||
|
- **Догрузка:** возможность загружать старые периоды с учётом rate limits
|
||||||
|
|
||||||
### BR-2: Сбор данных онлайн табло
|
### BR-2: UI для просмотра табло
|
||||||
- Аэропорты: SVO (Шереметьево), DME (Домодедово), VKO (Внуково), ZIA (Жуковский)
|
- **Расположение:** в существующем фронтенде (`http://192.168.2.67:8080/schedule`)
|
||||||
- Данные: номер рейса, авиакомпания, направление, время вылета/прилёта, статус
|
- **Формат:** таблица с фильтрами
|
||||||
- Источник: Яндекс.Расписания API или открытые табло аэропортов
|
- **Фильтры:**
|
||||||
- Обновление: каждые 15 минут
|
- Дата (диапазон)
|
||||||
- Хранить историю за 30 дней
|
- Тип (прилёт/вылет/все)
|
||||||
|
- Аэропорт (SVO/DME/VKO/ZIA/все)
|
||||||
|
- Номер рейса (поиск)
|
||||||
|
- Часовой интервал (например, 06:00-12:00)
|
||||||
|
- **Экспорт:** CSV
|
||||||
|
- **Адаптация:** мобильная версия обязательна
|
||||||
|
|
||||||
### BR-3: Хранение
|
### 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_ext` в существующей PostgreSQL БД
|
||||||
- Не влиять на схему `fr24` (RTL-SDR данные)
|
- Не влиять на схему `fr24` (RTL-SDR данные)
|
||||||
- Retention: треки FR24 — 30 дней, табло — 30 дней
|
- Retention: табло — 3 года (параметр)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Технические требования
|
## Технические требования
|
||||||
|
|
||||||
### Контейнер `fr24-external`
|
### Контейнер `fr24-schedule`
|
||||||
- Python 3.11-slim
|
- Python 3.11-slim
|
||||||
- Отдельный сервис в docker-compose.yml
|
- Отдельный сервис в docker-compose.yml
|
||||||
- Переменные окружения: FR24_API_KEY, YANDEX_RASP_API_KEY
|
- Переменные окружения: YANDEX_RASP_API_KEY, OPENSKY_USERNAME, OPENSKY_PASSWORD (опционально)
|
||||||
- Два независимых воркера: fr24_worker и rasp_worker
|
- Два независимых воркера: yandex_worker и opensky_worker
|
||||||
- Логирование в /var/log/fr24/external.log
|
- Логирование в stdout (Docker logs)
|
||||||
|
|
||||||
### Схема БД `fr24_ext`
|
### Схема БД `fr24_ext`
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- Рейсы из FR24 API
|
-- Табло аэропортов (объединённые данные из источников)
|
||||||
fr24_ext.flights_ext (
|
|
||||||
flight_ext_id, source, fr24_id, icao24, callsign,
|
|
||||||
origin_iata, destination_iata, aircraft_type,
|
|
||||||
fetched_at, flight_date
|
|
||||||
)
|
|
||||||
|
|
||||||
-- Треки из FR24 API
|
|
||||||
fr24_ext.track_points_ext (
|
|
||||||
point_id, flight_ext_id, observed_at,
|
|
||||||
lat, lon, altitude_ft, speed_kt, heading_deg
|
|
||||||
)
|
|
||||||
|
|
||||||
-- Табло аэропортов
|
|
||||||
fr24_ext.schedule (
|
fr24_ext.schedule (
|
||||||
schedule_id, airport_iata, direction,
|
schedule_id BIGSERIAL PRIMARY KEY,
|
||||||
flight_number, airline_iata, aircraft_type,
|
flight_date DATE NOT NULL,
|
||||||
scheduled_at, estimated_at, actual_at,
|
airport_iata CHAR(3) NOT NULL, -- SVO, DME, VKO, ZIA
|
||||||
status, fetched_at
|
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-агента
|
## ТЗ для Dev-агента
|
||||||
|
|
||||||
### Файлы для создания
|
### Файлы для создания
|
||||||
```
|
```
|
||||||
tasks/flightradar24/ingest/external/
|
tasks/flightradar24/ingest/schedule/
|
||||||
Dockerfile
|
Dockerfile
|
||||||
main.py # точка входа, запускает оба воркера
|
main.py # точка входа, запускает оба воркера + cron для T-1
|
||||||
fr24_worker.py # загрузка из FR24 API
|
yandex_worker.py # загрузка из Яндекс.Расписания
|
||||||
rasp_worker.py # загрузка из Яндекс.Расписания
|
opensky_worker.py # загрузка из OpenSky Network
|
||||||
|
backfill.py # скрипт догрузки исторических данных
|
||||||
requirements.txt
|
requirements.txt
|
||||||
tasks/flightradar24/db/init/003_schema_ext.sql # DDL схемы fr24_ext
|
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` — добавить сервис `external`
|
- `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: FR24 API подключение
|
### TC-1: Яндекс.Расписания подключение
|
||||||
- [ ] Контейнер стартует без ошибок
|
- [ ] Контейнер стартует без ошибок
|
||||||
- [ ] FR24 API возвращает данные для bbox Москвы
|
- [ ] Яндекс API возвращает данные для SVO за вчерашний день
|
||||||
- [ ] Треки сохраняются в `fr24_ext.track_points_ext`
|
- [ ] Данные сохраняются в `fr24_ext.schedule`
|
||||||
|
- [ ] Дубликаты не создаются (upsert работает)
|
||||||
|
|
||||||
### TC-2: Табло аэропортов
|
### TC-2: OpenSky Network подключение
|
||||||
- [ ] Данные по SVO загружаются корректно
|
- [ ] OpenSky API возвращает arrivals/departures для UUEE (SVO)
|
||||||
- [ ] Статусы рейсов обновляются при повторном запросе
|
- [ ] icao24 добавляется к существующим записям в `fr24_ext.schedule`
|
||||||
- [ ] Дубликаты не создаются (upsert по flight_number + scheduled_at)
|
- [ ] Rate limit соблюдается (не более 4000 запросов/день)
|
||||||
|
|
||||||
### TC-3: Изоляция схем
|
### 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`
|
- [ ] Запросы к `fr24_ext` не влияют на `fr24`
|
||||||
- [ ] При падении external контейнера остальные работают
|
- [ ] При падении schedule контейнера остальные работают
|
||||||
|
|
||||||
### TC-4: Retention
|
### TC-7: Retention
|
||||||
- [ ] Данные старше 30 дней удаляются автоматически
|
- [ ] Данные старше 3 лет удаляются автоматически (cron job)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Зависимости
|
## Зависимости
|
||||||
- Существующая PostgreSQL (fr24-postgres)
|
- Существующая PostgreSQL (fr24-postgres)
|
||||||
- FR24_API_KEY в ~/.openclaw/.env
|
- YANDEX_RASP_API_KEY в ~/.openclaw/.env
|
||||||
- YANDEX_RASP_API_KEY в ~/.openclaw/.env (или альтернативный источник табло)
|
- OPENSKY_USERNAME, OPENSKY_PASSWORD в ~/.openclaw/.env (опционально, для повышенного лимита)
|
||||||
|
|
||||||
## Оценка объёма
|
## Оценка объёма
|
||||||
- ~500 рейсов/день над Москвой из FR24
|
|
||||||
- ~800 записей/день в табло (4 аэропорта × ~200 рейсов)
|
### Данные
|
||||||
- Объём БД: ~50 MB/месяц
|
- ~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 часа при последовательной обработке
|
||||||
|
|||||||
Reference in New Issue
Block a user