Files
wiki/tasks/flightradar24/reports/TZ-fr24-schedule-supplement.md
2026-04-22 00:10:02 +03:00

166 lines
6.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ТЗ: Дополнение расписания из FR24 flight-summary/full
**Дата:** 2026-04-21
**Статус:** READY FOR DEV
**Приоритет:** Высокий
---
## Контекст
FR24 `/api/flight-summary/full` уже используется в `fr24_worker.py` и возвращает
**все реальные рейсы** через московские аэропорты за сутки (до 20 000 записей).
Текущее поведение:
- `enrich_schedule()` — только **UPDATE** существующих строк в `fr24_ext.schedule`
- Рейсы из `flight_actual`, которых нет в Яндекс.Расписании, **теряются**
Проблема: Яндекс не покрывает 100% рейсов (чартеры, грузовые, технические, задержанные).
FR24 имеет полные данные. Нужно использовать `flight_actual` как дополнительный источник расписания.
---
## Что нужно сделать
### 1. Функция `supplement_schedule()` в `fr24_worker.py`
```python
def supplement_schedule(conn, target_date: date) -> int:
"""
Insert into fr24_ext.schedule flights from flight_actual
that have no matching schedule record.
Source: fr24_ext.flight_actual
Target: fr24_ext.schedule (source='fr24')
Returns: number of rows inserted
"""
```
**Логика:**
1. Найти все рейсы в `flight_actual` за дату, у которых нет соответствия в `schedule`
2. Определить direction: если `origin_icao` в московских аэропортах → departure, иначе arrival
3. INSERT в `schedule` с `source='fr24'`, `status='actual'`
**SQL-скелет для поиска незаматченных:**
```sql
SELECT fa.*
FROM fr24_ext.flight_actual fa
WHERE fa.flight_date = %(date)s
AND NOT EXISTS (
SELECT 1 FROM fr24_ext.schedule s
WHERE UPPER(REPLACE(s.flight_number,' ','')) = UPPER(REPLACE(fa.flight,' ',''))
AND s.flight_date = fa.flight_date
)
AND fa.flight IS NOT NULL
AND fa.flight != ''
```
**Поля для INSERT в schedule:**
```
flight_date = target_date
airport_iata = ICAO_TO_IATA[origin_icao или dest_icao] (московский аэропорт)
direction = 'departure' если origin_icao московский, иначе 'arrival'
flight_number = fa.flight (нормализовать: убрать лишние пробелы)
airline_iata = первые 2 символа IATA (если можно определить)
origin_iata = ICAO_TO_IATA.get(fa.origin_icao)
destination_iata = ICAO_TO_IATA.get(fa.dest_icao)
aircraft_type = NULL (нет в flight_actual)
scheduled_at = fa.datetime_takeoff (или datetime_landed для arrival)
actual_takeoff = fa.datetime_takeoff
actual_landed = fa.datetime_landed
status = 'actual'
source = 'fr24'
fr24_id = fa.fr24_id
```
**ON CONFLICT:** `(flight_number, airport_iata, scheduled_at, direction)` — уже есть.
### 2. Также исправить баги из ревью
#### Баг A: ZBAD/PKX не в словаре ICAO_TO_IATA
Файл: `build_mart.py`
Добавить в словарь `ICAO_TO_IATA`:
```python
"ZBAD": "PKX", # Beijing Daxing
"RKSI": "ICN", # Seoul Incheon (уже есть, проверить)
```
#### Баг B: Soft match для FA/FR24 когда destination не совпадает
Файл: `build_mart.py`, функции `find_fa_track()` и `find_fr24_track()`
Текущая логика:
```python
if origin_iata and not destination_iata: # только если dest == NULL
# soft match by origin only
```
Нужно изменить на:
```python
# Если полный матч не нашёл → fallback на origin only
if origin_iata:
for row in rows:
orig_iata = ICAO_TO_IATA.get(row[2])
if orig_iata == origin_iata:
return (row[0], row[1])
```
(убрать условие `not destination_iata`)
### 3. Вызов `supplement_schedule()` в `run()` fr24_worker.py
После `enrich_schedule()`:
```python
# 4. Supplement schedule with flights from FR24 not in Yandex
try:
supplemented = supplement_schedule(conn, target_date)
conn.commit()
stats["schedule_supplemented"] = supplemented
log.info("FR24 worker: supplemented %d new schedule rows", supplemented)
except Exception as e:
conn.rollback()
log.error("FR24 worker: supplement_schedule failed: %s", e)
stats["errors"] += 1
```
---
## Что НЕ нужно делать
- НЕ менять DDL/схему БД (колонки `fr24_id`, `actual_takeoff`, `actual_landed` в schedule уже есть или добавить как nullable)
- НЕ трогать RTL-SDR матч — он работает корректно
- НЕ запускать backfill — только текущая логика для новых дат
---
## Проверка результата
1. Запустить build для 19.04 или 20.04 (данные уже есть в `flight_actual`)
2. Проверить:
```sql
SELECT source, count(*) FROM fr24_ext.schedule
WHERE flight_date = '2026-04-19' GROUP BY source;
-- Ожидаемо: yandex=~1570, fr24=N (новые)
SELECT count(*) FROM fr24_ext.schedule s
JOIN fr24_ext.flight_actual fa ON UPPER(REPLACE(fa.flight,' ','')) = UPPER(REPLACE(s.flight_number,' ',''))
WHERE s.flight_date = '2026-04-19' AND s.source = 'fr24';
```
3. Перезапустить mart build для даты — посмотреть увеличение `schedule_flights` и `with_track`
---
## Файлы для изменения
| Файл | Изменение |
|------|-----------|
| `ingest/tracks_fr24/fr24_worker.py` | Добавить `supplement_schedule()`, вызвать в `run()` |
| `ingest/mart/build_mart.py` | Добавить `ZBAD→PKX` в словарь, исправить soft match |
---
## Путь к проекту на VM
`/home/fr24/fr24/ingest/`
Деплой: `docker cp <file> fr24-tracks-fr24:/app/<file> && docker restart fr24-tracks-fr24`