mirror of
https://github.com/smittix/intercept.git
synced 2026-04-24 06:40:00 -07:00
fix: address PR #145 review issues
- Escape ac.icao, callsign, typeCode with escapeHtml() in aircraft card (XSS) - Add linking comments between duplicated IATA_TO_ICAO mappings - VDL2 sidebar: single-click selects aircraft, double-click opens modal - Remove stale ICAOs from acarsAircraftIcaos in cleanupOldAircraft() - Add null guard to drawPolarPlot() in weather-satellite.js - Move deferred imports (translate_message, get_flight_correlator) to module level - Check all frequency checkboxes by default on initial load - Remove extra blank lines and uncertain MC/MCO airline code entry - Add TODO comments linking duplicated renderAcarsCard implementations Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -15,21 +15,23 @@ import time
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Any, Generator
|
from typing import Any, Generator
|
||||||
|
|
||||||
from flask import Blueprint, jsonify, request, Response
|
from flask import Blueprint, Response, jsonify, request
|
||||||
|
|
||||||
import app as app_module
|
import app as app_module
|
||||||
from utils.logging import sensor_logger as logger
|
from utils.acars_translator import translate_message
|
||||||
from utils.validation import validate_device_index, validate_gain, validate_ppm
|
|
||||||
from utils.sdr import SDRFactory, SDRType
|
|
||||||
from utils.sse import sse_stream_fanout
|
|
||||||
from utils.event_pipeline import process_event
|
|
||||||
from utils.constants import (
|
from utils.constants import (
|
||||||
|
PROCESS_START_WAIT,
|
||||||
PROCESS_TERMINATE_TIMEOUT,
|
PROCESS_TERMINATE_TIMEOUT,
|
||||||
SSE_KEEPALIVE_INTERVAL,
|
SSE_KEEPALIVE_INTERVAL,
|
||||||
SSE_QUEUE_TIMEOUT,
|
SSE_QUEUE_TIMEOUT,
|
||||||
PROCESS_START_WAIT,
|
|
||||||
)
|
)
|
||||||
|
from utils.event_pipeline import process_event
|
||||||
|
from utils.flight_correlator import get_flight_correlator
|
||||||
|
from utils.logging import sensor_logger as logger
|
||||||
from utils.process import register_process, unregister_process
|
from utils.process import register_process, unregister_process
|
||||||
|
from utils.sdr import SDRFactory, SDRType
|
||||||
|
from utils.sse import sse_stream_fanout
|
||||||
|
from utils.validation import validate_device_index, validate_gain, validate_ppm
|
||||||
|
|
||||||
acars_bp = Blueprint('acars', __name__, url_prefix='/acars')
|
acars_bp = Blueprint('acars', __name__, url_prefix='/acars')
|
||||||
|
|
||||||
@@ -126,7 +128,6 @@ def stream_acars_output(process: subprocess.Popen, is_text_mode: bool = False) -
|
|||||||
|
|
||||||
# Enrich with translated label and parsed fields
|
# Enrich with translated label and parsed fields
|
||||||
try:
|
try:
|
||||||
from utils.acars_translator import translate_message
|
|
||||||
translation = translate_message(data)
|
translation = translate_message(data)
|
||||||
data['label_description'] = translation['label_description']
|
data['label_description'] = translation['label_description']
|
||||||
data['message_type'] = translation['message_type']
|
data['message_type'] = translation['message_type']
|
||||||
@@ -142,7 +143,6 @@ def stream_acars_output(process: subprocess.Popen, is_text_mode: bool = False) -
|
|||||||
|
|
||||||
# Feed flight correlator
|
# Feed flight correlator
|
||||||
try:
|
try:
|
||||||
from utils.flight_correlator import get_flight_correlator
|
|
||||||
get_flight_correlator().add_acars_message(data)
|
get_flight_correlator().add_acars_message(data)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
@@ -452,11 +452,9 @@ def stream_acars() -> Response:
|
|||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@acars_bp.route('/messages')
|
@acars_bp.route('/messages')
|
||||||
def get_acars_messages() -> Response:
|
def get_acars_messages() -> Response:
|
||||||
"""Get recent ACARS messages from correlator (for history reload)."""
|
"""Get recent ACARS messages from correlator (for history reload)."""
|
||||||
from utils.flight_correlator import get_flight_correlator
|
|
||||||
limit = request.args.get('limit', 50, type=int)
|
limit = request.args.get('limit', 50, type=int)
|
||||||
limit = max(1, min(limit, 200))
|
limit = max(1, min(limit, 200))
|
||||||
msgs = get_flight_correlator().get_recent_messages('acars', limit)
|
msgs = get_flight_correlator().get_recent_messages('acars', limit)
|
||||||
@@ -467,7 +465,6 @@ def get_acars_messages() -> Response:
|
|||||||
def clear_acars_messages() -> Response:
|
def clear_acars_messages() -> Response:
|
||||||
"""Clear stored ACARS messages and reset counter."""
|
"""Clear stored ACARS messages and reset counter."""
|
||||||
global acars_message_count, acars_last_message_time
|
global acars_message_count, acars_last_message_time
|
||||||
from utils.flight_correlator import get_flight_correlator
|
|
||||||
get_flight_correlator().clear_acars()
|
get_flight_correlator().clear_acars()
|
||||||
acars_message_count = 0
|
acars_message_count = 0
|
||||||
acars_last_message_time = None
|
acars_last_message_time = None
|
||||||
|
|||||||
@@ -13,8 +13,7 @@ import time
|
|||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from typing import Any, Generator
|
from typing import Any, Generator
|
||||||
|
|
||||||
from flask import Blueprint, jsonify, request, Response, render_template
|
from flask import Blueprint, Response, jsonify, make_response, render_template, request
|
||||||
from flask import make_response
|
|
||||||
|
|
||||||
# psycopg2 is optional - only needed for PostgreSQL history persistence
|
# psycopg2 is optional - only needed for PostgreSQL history persistence
|
||||||
try:
|
try:
|
||||||
@@ -28,39 +27,38 @@ except ImportError:
|
|||||||
|
|
||||||
import app as app_module
|
import app as app_module
|
||||||
from config import (
|
from config import (
|
||||||
|
ADSB_AUTO_START,
|
||||||
ADSB_DB_HOST,
|
ADSB_DB_HOST,
|
||||||
ADSB_DB_NAME,
|
ADSB_DB_NAME,
|
||||||
ADSB_DB_PASSWORD,
|
ADSB_DB_PASSWORD,
|
||||||
ADSB_DB_PORT,
|
ADSB_DB_PORT,
|
||||||
ADSB_DB_USER,
|
ADSB_DB_USER,
|
||||||
ADSB_AUTO_START,
|
|
||||||
ADSB_HISTORY_ENABLED,
|
ADSB_HISTORY_ENABLED,
|
||||||
SHARED_OBSERVER_LOCATION_ENABLED,
|
SHARED_OBSERVER_LOCATION_ENABLED,
|
||||||
)
|
)
|
||||||
from utils.logging import adsb_logger as logger
|
from utils import aircraft_db
|
||||||
from utils.process import write_dump1090_pid, clear_dump1090_pid, cleanup_stale_dump1090
|
from utils.acars_translator import translate_message
|
||||||
from utils.validation import (
|
from utils.adsb_history import _ensure_adsb_schema, adsb_history_writer, adsb_snapshot_writer
|
||||||
validate_device_index, validate_gain,
|
|
||||||
validate_rtl_tcp_host, validate_rtl_tcp_port
|
|
||||||
)
|
|
||||||
from utils.sse import format_sse
|
|
||||||
from utils.event_pipeline import process_event
|
|
||||||
from utils.sdr import SDRFactory, SDRType
|
|
||||||
from utils.constants import (
|
from utils.constants import (
|
||||||
ADSB_SBS_PORT,
|
ADSB_SBS_PORT,
|
||||||
ADSB_TERMINATE_TIMEOUT,
|
ADSB_TERMINATE_TIMEOUT,
|
||||||
PROCESS_TERMINATE_TIMEOUT,
|
|
||||||
SBS_SOCKET_TIMEOUT,
|
|
||||||
SBS_RECONNECT_DELAY,
|
|
||||||
SOCKET_BUFFER_SIZE,
|
|
||||||
SSE_KEEPALIVE_INTERVAL,
|
|
||||||
SSE_QUEUE_TIMEOUT,
|
|
||||||
SOCKET_CONNECT_TIMEOUT,
|
|
||||||
ADSB_UPDATE_INTERVAL,
|
ADSB_UPDATE_INTERVAL,
|
||||||
DUMP1090_START_WAIT,
|
DUMP1090_START_WAIT,
|
||||||
|
PROCESS_TERMINATE_TIMEOUT,
|
||||||
|
SBS_RECONNECT_DELAY,
|
||||||
|
SBS_SOCKET_TIMEOUT,
|
||||||
|
SOCKET_BUFFER_SIZE,
|
||||||
|
SOCKET_CONNECT_TIMEOUT,
|
||||||
|
SSE_KEEPALIVE_INTERVAL,
|
||||||
|
SSE_QUEUE_TIMEOUT,
|
||||||
)
|
)
|
||||||
from utils import aircraft_db
|
from utils.event_pipeline import process_event
|
||||||
from utils.adsb_history import adsb_history_writer, adsb_snapshot_writer, _ensure_adsb_schema
|
from utils.flight_correlator import get_flight_correlator
|
||||||
|
from utils.logging import adsb_logger as logger
|
||||||
|
from utils.process import cleanup_stale_dump1090, clear_dump1090_pid, write_dump1090_pid
|
||||||
|
from utils.sdr import SDRFactory, SDRType
|
||||||
|
from utils.sse import format_sse
|
||||||
|
from utils.validation import validate_device_index, validate_gain, validate_rtl_tcp_host, validate_rtl_tcp_port
|
||||||
|
|
||||||
adsb_bp = Blueprint('adsb', __name__, url_prefix='/adsb')
|
adsb_bp = Blueprint('adsb', __name__, url_prefix='/adsb')
|
||||||
|
|
||||||
@@ -1247,14 +1245,12 @@ def get_aircraft_messages(icao: str):
|
|||||||
callsign = aircraft.get('callsign') if aircraft else None
|
callsign = aircraft.get('callsign') if aircraft else None
|
||||||
registration = aircraft.get('registration') if aircraft else None
|
registration = aircraft.get('registration') if aircraft else None
|
||||||
|
|
||||||
from utils.flight_correlator import get_flight_correlator
|
|
||||||
messages = get_flight_correlator().get_messages_for_aircraft(
|
messages = get_flight_correlator().get_messages_for_aircraft(
|
||||||
icao=icao.upper(), callsign=callsign, registration=registration
|
icao=icao.upper(), callsign=callsign, registration=registration
|
||||||
)
|
)
|
||||||
|
|
||||||
# Backfill translation on messages missing label_description
|
# Backfill translation on messages missing label_description
|
||||||
try:
|
try:
|
||||||
from utils.acars_translator import translate_message
|
|
||||||
for msg in messages.get('acars', []):
|
for msg in messages.get('acars', []):
|
||||||
if not msg.get('label_description'):
|
if not msg.get('label_description'):
|
||||||
translation = translate_message(msg)
|
translation = translate_message(msg)
|
||||||
|
|||||||
@@ -15,21 +15,23 @@ import time
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Any, Generator
|
from typing import Any, Generator
|
||||||
|
|
||||||
from flask import Blueprint, jsonify, request, Response
|
from flask import Blueprint, Response, jsonify, request
|
||||||
|
|
||||||
import app as app_module
|
import app as app_module
|
||||||
from utils.logging import sensor_logger as logger
|
from utils.acars_translator import translate_message
|
||||||
from utils.validation import validate_device_index, validate_gain, validate_ppm
|
|
||||||
from utils.sdr import SDRFactory, SDRType
|
|
||||||
from utils.sse import sse_stream_fanout
|
|
||||||
from utils.event_pipeline import process_event
|
|
||||||
from utils.constants import (
|
from utils.constants import (
|
||||||
|
PROCESS_START_WAIT,
|
||||||
PROCESS_TERMINATE_TIMEOUT,
|
PROCESS_TERMINATE_TIMEOUT,
|
||||||
SSE_KEEPALIVE_INTERVAL,
|
SSE_KEEPALIVE_INTERVAL,
|
||||||
SSE_QUEUE_TIMEOUT,
|
SSE_QUEUE_TIMEOUT,
|
||||||
PROCESS_START_WAIT,
|
|
||||||
)
|
)
|
||||||
|
from utils.event_pipeline import process_event
|
||||||
|
from utils.flight_correlator import get_flight_correlator
|
||||||
|
from utils.logging import sensor_logger as logger
|
||||||
from utils.process import register_process, unregister_process
|
from utils.process import register_process, unregister_process
|
||||||
|
from utils.sdr import SDRFactory, SDRType
|
||||||
|
from utils.sse import sse_stream_fanout
|
||||||
|
from utils.validation import validate_device_index, validate_gain, validate_ppm
|
||||||
|
|
||||||
vdl2_bp = Blueprint('vdl2', __name__, url_prefix='/vdl2')
|
vdl2_bp = Blueprint('vdl2', __name__, url_prefix='/vdl2')
|
||||||
|
|
||||||
@@ -85,7 +87,6 @@ def stream_vdl2_output(process: subprocess.Popen, is_text_mode: bool = False) ->
|
|||||||
vdl2_inner = data.get('vdl2', data)
|
vdl2_inner = data.get('vdl2', data)
|
||||||
acars_payload = (vdl2_inner.get('avlc') or {}).get('acars')
|
acars_payload = (vdl2_inner.get('avlc') or {}).get('acars')
|
||||||
if acars_payload and acars_payload.get('label'):
|
if acars_payload and acars_payload.get('label'):
|
||||||
from utils.acars_translator import translate_message
|
|
||||||
translation = translate_message({
|
translation = translate_message({
|
||||||
'label': acars_payload.get('label'),
|
'label': acars_payload.get('label'),
|
||||||
'text': acars_payload.get('msg_text', ''),
|
'text': acars_payload.get('msg_text', ''),
|
||||||
@@ -104,7 +105,6 @@ def stream_vdl2_output(process: subprocess.Popen, is_text_mode: bool = False) ->
|
|||||||
|
|
||||||
# Feed flight correlator
|
# Feed flight correlator
|
||||||
try:
|
try:
|
||||||
from utils.flight_correlator import get_flight_correlator
|
|
||||||
get_flight_correlator().add_vdl2_message(data)
|
get_flight_correlator().add_vdl2_message(data)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
@@ -396,7 +396,6 @@ def stream_vdl2() -> Response:
|
|||||||
@vdl2_bp.route('/messages')
|
@vdl2_bp.route('/messages')
|
||||||
def get_vdl2_messages() -> Response:
|
def get_vdl2_messages() -> Response:
|
||||||
"""Get recent VDL2 messages from correlator (for history reload)."""
|
"""Get recent VDL2 messages from correlator (for history reload)."""
|
||||||
from utils.flight_correlator import get_flight_correlator
|
|
||||||
limit = request.args.get('limit', 50, type=int)
|
limit = request.args.get('limit', 50, type=int)
|
||||||
limit = max(1, min(limit, 200))
|
limit = max(1, min(limit, 200))
|
||||||
msgs = get_flight_correlator().get_recent_messages('vdl2', limit)
|
msgs = get_flight_correlator().get_recent_messages('vdl2', limit)
|
||||||
@@ -407,7 +406,6 @@ def get_vdl2_messages() -> Response:
|
|||||||
def clear_vdl2_messages() -> Response:
|
def clear_vdl2_messages() -> Response:
|
||||||
"""Clear stored VDL2 messages and reset counter."""
|
"""Clear stored VDL2 messages and reset counter."""
|
||||||
global vdl2_message_count, vdl2_last_message_time
|
global vdl2_message_count, vdl2_last_message_time
|
||||||
from utils.flight_correlator import get_flight_correlator
|
|
||||||
get_flight_correlator().clear_vdl2()
|
get_flight_correlator().clear_vdl2()
|
||||||
vdl2_message_count = 0
|
vdl2_message_count = 0
|
||||||
vdl2_last_message_time = None
|
vdl2_last_message_time = None
|
||||||
|
|||||||
@@ -727,6 +727,7 @@ const WeatherSat = (function() {
|
|||||||
* Draw polar plot for a pass trajectory
|
* Draw polar plot for a pass trajectory
|
||||||
*/
|
*/
|
||||||
function drawPolarPlot(pass) {
|
function drawPolarPlot(pass) {
|
||||||
|
if (!pass) return;
|
||||||
const canvas = document.getElementById('wxsatPolarCanvas');
|
const canvas = document.getElementById('wxsatPolarCanvas');
|
||||||
if (!canvas) return;
|
if (!canvas) return;
|
||||||
|
|
||||||
|
|||||||
@@ -2864,8 +2864,8 @@ sudo make install</code>
|
|||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="aircraft-header">
|
<div class="aircraft-header">
|
||||||
<span class="aircraft-callsign">${callsign}${badge}${acarsIndicator}${agentBadge}</span>
|
<span class="aircraft-callsign">${escapeHtml(callsign)}${badge}${acarsIndicator}${agentBadge}</span>
|
||||||
<span class="aircraft-icao">${typeCode ? typeCode + ' • ' : ''}${ac.icao}</span>
|
<span class="aircraft-icao">${typeCode ? escapeHtml(typeCode) + ' • ' : ''}${escapeHtml(ac.icao)}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="aircraft-details">
|
<div class="aircraft-details">
|
||||||
<div class="aircraft-detail">
|
<div class="aircraft-detail">
|
||||||
@@ -3242,6 +3242,7 @@ sudo make install</code>
|
|||||||
return '<span style="display:inline-block;padding:1px 5px;border-radius:3px;font-size:8px;font-weight:700;color:#000;background:' + color + ';">' + lbl + '</span>';
|
return '<span style="display:inline-block;padding:1px 5px;border-radius:3px;font-size:8px;font-weight:700;color:#000;background:' + color + ';">' + lbl + '</span>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Similar to renderAcarsMainCard in partials/modes/acars.html — consider unifying
|
||||||
function renderAcarsCard(msg) {
|
function renderAcarsCard(msg) {
|
||||||
const type = msg.message_type || 'other';
|
const type = msg.message_type || 'other';
|
||||||
const badge = getAcarsTypeBadge(type);
|
const badge = getAcarsTypeBadge(type);
|
||||||
@@ -3346,6 +3347,7 @@ sudo make install</code>
|
|||||||
cleanupTrail(icao);
|
cleanupTrail(icao);
|
||||||
delete aircraft[icao];
|
delete aircraft[icao];
|
||||||
delete alertedAircraft[icao];
|
delete alertedAircraft[icao];
|
||||||
|
if (typeof acarsAircraftIcaos !== 'undefined') acarsAircraftIcaos.delete(icao);
|
||||||
needsUpdate = true;
|
needsUpdate = true;
|
||||||
|
|
||||||
if (selectedIcao === icao) {
|
if (selectedIcao === icao) {
|
||||||
@@ -3811,8 +3813,8 @@ sudo make install</code>
|
|||||||
});
|
});
|
||||||
|
|
||||||
container.innerHTML = freqs.map((freq, i) => {
|
container.innerHTML = freqs.map((freq, i) => {
|
||||||
// On initial load, only check the first (primary) frequency; otherwise preserve state
|
// On initial load, check all frequencies; otherwise preserve state
|
||||||
const checked = previouslyChecked.size === 0 ? (i === 0 ? 'checked' : '') : (previouslyChecked.has(freq) ? 'checked' : '');
|
const checked = previouslyChecked.size === 0 || previouslyChecked.has(freq) ? 'checked' : '';
|
||||||
return `
|
return `
|
||||||
<label style="display: flex; align-items: center; gap: 3px; padding: 2px 6px; background: var(--bg-secondary); border-radius: 3px; cursor: pointer;">
|
<label style="display: flex; align-items: center; gap: 3px; padding: 2px 6px; background: var(--bg-secondary); border-radius: 3px; cursor: pointer;">
|
||||||
<input type="checkbox" class="acars-freq-cb" value="${freq}" ${checked} style="margin: 0; cursor: pointer;">
|
<input type="checkbox" class="acars-freq-cb" value="${freq}" ${checked} style="margin: 0; cursor: pointer;">
|
||||||
@@ -4040,6 +4042,7 @@ sudo make install</code>
|
|||||||
const acarsAircraftIcaos = new Set();
|
const acarsAircraftIcaos = new Set();
|
||||||
|
|
||||||
// IATA (2-letter) → ICAO (3-letter) airline code mapping
|
// IATA (2-letter) → ICAO (3-letter) airline code mapping
|
||||||
|
// NOTE: Duplicated from utils/airline_codes.py — keep both in sync
|
||||||
const IATA_TO_ICAO = {
|
const IATA_TO_ICAO = {
|
||||||
'AA':'AAL','DL':'DAL','UA':'UAL','WN':'SWA','B6':'JBU','AS':'ASA',
|
'AA':'AAL','DL':'DAL','UA':'UAL','WN':'SWA','B6':'JBU','AS':'ASA',
|
||||||
'NK':'NKS','F9':'FFT','G4':'AAY','HA':'HAL','SY':'SCX','WS':'WJA',
|
'NK':'NKS','F9':'FFT','G4':'AAY','HA':'HAL','SY':'SCX','WS':'WJA',
|
||||||
@@ -4696,8 +4699,9 @@ sudo make install</code>
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
if (matchedIcao) {
|
if (matchedIcao) {
|
||||||
msg.addEventListener('click', (e) => { e.stopPropagation(); selectAircraft(matchedIcao); });
|
msg.addEventListener('click', () => selectAircraft(matchedIcao));
|
||||||
msg.title = 'Click to locate ' + label + ' on map';
|
msg.addEventListener('dblclick', () => showVdl2Modal(data, time));
|
||||||
|
msg.title = 'Click to locate on map, double-click for details';
|
||||||
} else {
|
} else {
|
||||||
msg.addEventListener('click', () => showVdl2Modal(data, time));
|
msg.addEventListener('click', () => showVdl2Modal(data, time));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,7 +111,7 @@
|
|||||||
container.querySelectorAll('input:checked').forEach(cb => previouslyChecked.add(cb.value));
|
container.querySelectorAll('input:checked').forEach(cb => previouslyChecked.add(cb.value));
|
||||||
|
|
||||||
container.innerHTML = freqs.map((freq, i) => {
|
container.innerHTML = freqs.map((freq, i) => {
|
||||||
const checked = previouslyChecked.size === 0 ? (i === 0 ? 'checked' : '') : (previouslyChecked.has(freq) ? 'checked' : '');
|
const checked = previouslyChecked.size === 0 || previouslyChecked.has(freq) ? 'checked' : '';
|
||||||
return `
|
return `
|
||||||
<label style="display: flex; align-items: center; gap: 3px; padding: 2px 6px; background: var(--bg-secondary); border-radius: 3px; cursor: pointer;">
|
<label style="display: flex; align-items: center; gap: 3px; padding: 2px 6px; background: var(--bg-secondary); border-radius: 3px; cursor: pointer;">
|
||||||
<input type="checkbox" class="acars-main-freq-cb" value="${freq}" ${checked} style="margin: 0; cursor: pointer;">
|
<input type="checkbox" class="acars-main-freq-cb" value="${freq}" ${checked} style="margin: 0; cursor: pointer;">
|
||||||
@@ -188,6 +188,7 @@
|
|||||||
return `<span style="display:inline-block;padding:1px 5px;border-radius:3px;font-size:8px;font-weight:700;color:#000;background:${color};">${lbl}</span>`;
|
return `<span style="display:inline-block;padding:1px 5px;border-radius:3px;font-size:8px;font-weight:700;color:#000;background:${color};">${lbl}</span>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Similar to renderAcarsCard in templates/adsb_dashboard.html — consider unifying
|
||||||
function renderAcarsMainCard(data) {
|
function renderAcarsMainCard(data) {
|
||||||
const flight = escapeHtml(data.flight || 'UNKNOWN');
|
const flight = escapeHtml(data.flight || 'UNKNOWN');
|
||||||
const type = data.message_type || 'other';
|
const type = data.message_type || 'other';
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ from __future__ import annotations
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
# IATA (2-letter) → ICAO (3-letter) mapping for common airlines
|
# IATA (2-letter) → ICAO (3-letter) mapping for common airlines
|
||||||
|
# NOTE: Duplicated in templates/adsb_dashboard.html (JS IATA_TO_ICAO) — keep both in sync
|
||||||
IATA_TO_ICAO: dict[str, str] = {
|
IATA_TO_ICAO: dict[str, str] = {
|
||||||
# North America — Major
|
# North America — Major
|
||||||
"AA": "AAL", # American Airlines
|
"AA": "AAL", # American Airlines
|
||||||
@@ -112,8 +113,6 @@ IATA_TO_ICAO: dict[str, str] = {
|
|||||||
"AV": "AVA", # Avianca
|
"AV": "AVA", # Avianca
|
||||||
"CM": "CMP", # Copa Airlines
|
"CM": "CMP", # Copa Airlines
|
||||||
"AR": "ARG", # Aerolíneas Argentinas
|
"AR": "ARG", # Aerolíneas Argentinas
|
||||||
# ACARS-specific addressing codes
|
|
||||||
"MC": "MCO", # Possible: some ACARS systems use MC
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Build reverse mapping (ICAO → IATA)
|
# Build reverse mapping (ICAO → IATA)
|
||||||
|
|||||||
Reference in New Issue
Block a user