// data_sources.js — logic for /data-sources page
function getDateRange() {
const to = document.getElementById('date_to').value;
const from = document.getElementById('date_from').value;
return { date_from: from, date_to: to };
}
function qs(params) {
return Object.entries(params).filter(([, v]) => v).map(([k, v]) => `${k}=${encodeURIComponent(v)}`).join('&');
}
async function fetchJSON(url) {
const r = await fetch(url);
if (!r.ok) throw new Error(`HTTP ${r.status}`);
return r.json();
}
function pct(n, total) {
if (!total) return '—';
return (100 * n / total).toFixed(1) + '%';
}
// ── Coverage cards ────────────────────────────────────────────
async function loadCoverage() {
const el = document.getElementById('coverage-cards');
const chart = document.getElementById('coverage-chart');
try {
const data = await fetchJSON('/api/data-sources/coverage?' + qs(getDateRange()));
const rows = data.rows || [];
const totals = data.totals || {};
// Summary cards
const total = totals.total_schedule || 0;
el.innerHTML = `
Расписание Яндекс
Всего рейсов${total.toLocaleString()}
Дней${totals.days || 0}
RTL-SDR Локальный
Рейсов с треком${(totals.with_rtlsdr||0).toLocaleString()}${pct(totals.with_rtlsdr, total)}
FR24 API Платный
Рейсов с треком${(totals.with_fr24||0).toLocaleString()}${pct(totals.with_fr24, total)}
FlightAware AeroAPI
Рейсов с треком${(totals.with_fa||0).toLocaleString()}${pct(totals.with_fa, total)}
`;
// Stacked bar chart by day
if (!rows.length) { chart.innerHTML = 'Нет данных
'; return; }
const maxTotal = Math.max(...rows.map(r => r.total_schedule || 0), 1);
chart.innerHTML = rows.map(r => {
const t = r.total_schedule || 1;
const wRtl = ((r.with_rtlsdr || 0) / t * 100).toFixed(1);
const wFr = ((r.with_fr24 || 0) / t * 100).toFixed(1);
const wFa = ((r.with_fa || 0) / t * 100).toFixed(1);
const wSch = (100 - parseFloat(wRtl) - parseFloat(wFr) - parseFloat(wFa)).toFixed(1);
return ``;
}).join('');
} catch (e) {
el.innerHTML = `Ошибка: ${e.message}
`;
}
}
// ── Quality ───────────────────────────────────────────────────
async function loadQuality() {
const el = document.getElementById('quality-table');
try {
const data = await fetchJSON('/api/data-sources/quality?' + qs(getDateRange()));
const q = data.quality || {};
el.innerHTML = `
| Метрика | Значение |
| Рейсов с маршрутом | ${pct(q.with_route, q.total)} |
| Рейсов с треком | ${pct(q.with_track, q.total)} |
| Рейсов с факт. временем | ${pct(q.with_actual_time, q.total)} |
| Рейсов с типом ВС | ${pct(q.with_aircraft_type, q.total)} |
| Медиана точек (RTL-SDR) | ${q.median_points_rtlsdr || '—'} |
| Медиана точек (FR24) | ${q.median_points_fr24 || '—'} |
| Медиана точек (FA) | ${q.median_points_fa || '—'} |
`;
} catch (e) {
el.innerHTML = `Ошибка: ${e.message}
`;
}
}
// ── Airport load ──────────────────────────────────────────────
async function loadAirportLoad() {
const el = document.getElementById('airport-load');
try {
const data = await fetchJSON('/api/data-sources/airport-load?' + qs(getDateRange()));
const rows = data.rows || [];
if (!rows.length) { el.innerHTML = 'Нет данных
'; return; }
const maxCount = Math.max(...rows.map(r => r.flight_count || 0), 1);
el.innerHTML = `
| Аэропорт | Час (UTC) | Рейсов | |
${rows.map(r => `
| ${r.airport_iata} |
${String(r.hour).padStart(2,'0')}:00 |
${r.flight_count} |
|
`).join('')}
`;
} catch (e) {
el.innerHTML = `Ошибка: ${e.message}
`;
}
}
// ── Top airlines ──────────────────────────────────────────────
async function loadTopAirlines() {
const el = document.getElementById('top-airlines');
try {
const data = await fetchJSON('/api/data-sources/top-airlines?' + qs(getDateRange()));
const rows = data.rows || [];
if (!rows.length) { el.innerHTML = 'Нет данных
'; return; }
const max = rows[0].flight_count || 1;
el.innerHTML = `
| # | Авиакомпания | Рейсов | |
${rows.map((r, i) => `
| ${i+1} |
${r.airline_iata || '—'} |
${r.flight_count} |
|
`).join('')}
`;
} catch (e) {
el.innerHTML = `Ошибка: ${e.message}
`;
}
}
// ── Top routes ────────────────────────────────────────────────
async function loadTopRoutes() {
const el = document.getElementById('top-routes');
try {
const data = await fetchJSON('/api/data-sources/top-routes?' + qs(getDateRange()));
const rows = data.rows || [];
if (!rows.length) { el.innerHTML = 'Нет данных
'; return; }
const max = rows[0].flight_count || 1;
el.innerHTML = `
| # | Маршрут | Рейсов | |
${rows.map((r, i) => `
| ${i+1} |
${r.origin_iata || '?'} → ${r.destination_iata || '?'} |
${r.flight_count} |
|
`).join('')}
`;
} catch (e) {
el.innerHTML = `Ошибка: ${e.message}
`;
}
}
// ── Init ──────────────────────────────────────────────────────
function loadAll() {
loadCoverage();
loadQuality();
loadAirportLoad();
loadTopAirlines();
loadTopRoutes();
}
// Set default date range: last 7 days
(function initDates() {
const today = new Date();
const to = today.toISOString().slice(0, 10);
today.setDate(today.getDate() - 7);
const from = today.toISOString().slice(0, 10);
document.getElementById('date_from').value = from;
document.getElementById('date_to').value = to;
})();
loadAll();