Files
wiki/tasks/flightradar24/reports/TZ-schedule-ui-enrichment.md
2026-04-22 00:50:01 +03:00

6.9 KiB
Raw Blame History

ТЗ: Обогащение UI расписания данными FR24

Дата: 2026-04-21
Статус: READY FOR DEV
Файлы: frontend/main.py, frontend/static/schedule.html, frontend/static/schedule.js


Что сейчас есть и что не так

Таблица /schedule уже получает часть FR24-полей (actual_takeoff, delay, fr24_id, flight_category),
но отображение плохое:

  • Трек-ссылка ✈ дублируется в столбцах 2 и 12
  • duration_min есть в API, но не отображается в таблице
  • Runway, дистанция, тип ВС, регистрация — не показываются
  • Колонка "Фактически" показывает actual_at из Яндекса (часто null), а не FR24 actual_takeoff
  • Нет признака источника данных (Яндекс vs FR24)

Изменения в main.py

1. Обогатить /api/schedule/data данными из flight_actual

Добавить LEFT JOIN на fr24_ext.flight_actual в запрос:

SELECT
    s.flight_number, s.airline_name, s.airport_iata, s.direction,
    s.origin_iata, s.destination_iata,
    s.scheduled_at, s.actual_at, s.status,
    s.flight_date, s.duration_min, s.thread_title,
    s.actual_takeoff, s.actual_landed,
    s.delay_takeoff_min, s.delay_landed_min,
    s.fr24_id, s.flight_category, s.source AS sched_source,
    -- из flight_actual:
    fa.runway_takeoff, fa.runway_landed,
    fa.actual_distance,
    fa.flight_time AS fa_flight_time,
    fa.reg AS registration,
    fa.operated_as,
    fa.category AS fa_category,
    -- из mart.flights (тип ВС):
    mf.aircraft_type,
    mf.track_source, mf.track_points
FROM fr24_ext.schedule s
LEFT JOIN fr24_ext.flight_actual fa
    ON fa.fr24_id = s.fr24_id AND s.fr24_id IS NOT NULL
LEFT JOIN fr24_mart.flights mf
    ON mf.flight_number = s.flight_number
    AND mf.flight_date = s.flight_date
WHERE {where}
ORDER BY s.scheduled_at DESC
LIMIT %s OFFSET %s

2. Добавить новые поля в JSON-ответ

В schedule_data() добавить в dict полёта:

"runway_takeoff":   r.get("runway_takeoff"),
"runway_landed":    r.get("runway_landed"),
"actual_distance":  r.get("actual_distance"),   # км
"flight_time_min":  r.get("fa_flight_time"),    # минуты из FA
"registration":     r.get("registration"),
"aircraft_type":    r.get("aircraft_type"),
"track_source":     r.get("track_source"),      # rtlsdr/fr24/fa/null
"track_points":     r.get("track_points"),
"sched_source":     r.get("sched_source"),      # yandex/fr24

Эффективная длительность:

"duration_eff": r.get("fa_flight_time") or r.get("duration_min"),

Изменения в schedule.html

Новые колонки таблицы (вместо текущих 12)

# Колонка Данные
1 Дата flight_date
2 Рейс flight_number + cat badge + source badge
3 Авиакомпания airline + registration (мелко)
4 ↑↓ direction
5 Маршрут thread_title или origin → destination
6 Аэропорт airport_iata
7 По расп. scheduled_at (MSK)
8 Взлёт факт actual_takeoff (MSK) + delay badge
9 Посадка факт actual_landed (MSK)
10 Длит. duration_eff в формате "2ч 35м"
11 ВПП runway_takeoff → runway_landed
12 ВС aircraft_type
13 Трек ✈ ссылка на FR24 + 🛰 если есть RTL-SDR трек

Убрать: дублирующий столбец Трек (#12 старый), убрать Статус (всегда scheduled).

Source badge в колонке Рейс

<!-- если sched_source == 'fr24' -->
<span class="src-badge src-fr24" title="Данные из FR24 API">FR</span>
<!-- если sched_source == 'yandex' -->
<!-- не показываем ничего, это норма -->

CSS для src-badge:

.src-badge { border-radius:3px; font-size:9px; font-weight:700; padding:1px 4px; margin-left:3px; }
.src-fr24  { background:#0d2d5e; color:#58a6ff; }

Track source badge в колонке Трек

// ✈ = ссылка на FR24
// 🛰 = RTL-SDR трек есть в mart
const rtlBadge = f.track_source === 'rtlsdr' 
  ? `<span title="${f.track_points} точек RTL-SDR" style="color:#3fb950;cursor:default">🛰</span>` 
  : '';

Изменения в schedule.js

renderTable() — новые колонки

// Длительность
function fmtDuration(min) {
  if (!min) return "—";
  const h = Math.floor(min / 60);
  const m = min % 60;
  return h > 0 ? `${h}ч ${m}м` : `${m}м`;
}

// ВПП
const runway = (f.runway_takeoff || f.runway_landed)
  ? `${f.runway_takeoff || "?"}${f.runway_landed || "?"}`
  : "—";

// Actual takeoff с задержкой
const actTakeoffCell = f.actual_takeoff
  ? `${fmtTime(f.actual_takeoff)} ${delayCell(f.delay_takeoff_min)}`
  : "—";

// Actual landed
const actLandedCell = f.actual_landed
  ? fmtTime(f.actual_landed)
  : "—";

// Регистрация под авиакомпанией
const airlineCell = `${esc(f.airline || "—")}`
  + (f.registration ? `<br><small style="color:#6e7681">${esc(f.registration)}</small>` : "");

// Source badge
const srcBadge = f.sched_source === 'fr24'
  ? `<span class="src-badge src-fr24" title="Источник: FR24">FR</span>` : "";

// Track column
const trackCol = [
  f.fr24_id ? `<a href="https://www.flightradar24.com/data/flights/${f.fr24_id}" target="_blank" class="track-link" title="Трек FR24">✈</a>` : "",
  f.track_source === 'rtlsdr' ? `<span title="${f.track_points||0} точек RTL-SDR" style="color:#3fb950">🛰</span>` : "",
  f.track_source === 'fa'     ? `<span title="${f.track_points||0} точек FA" style="color:#d29922">FA</span>` : "",
].filter(Boolean).join(" ");

renderCards() — добавить новые поля

В мобильных карточках добавить строки:

  • Взлёт факт + задержка
  • Посадка факт
  • Длительность
  • ВПП
  • Тип ВС / Регистрация
  • Трек (FR24 + RTL-SDR badge)

Деплой

Файлы на VM лежат в контейнере fr24-api, монтируются из:

  • /home/fr24/projects/fr24/frontend/main.py
  • /home/fr24/projects/fr24/frontend/static/schedule.html
  • /home/fr24/projects/fr24/frontend/static/schedule.js

После изменений: docker restart fr24-api


НЕ делать

  • Не трогать другие страницы (index.html, monitoring.html, data_sources)
  • Не менять схему БД
  • Не трогать main.py кроме функции schedule_data() и добавления SQL JOIN