Aircraft enrichment: FlightAware fallback + IATA→ICAO normalization

The user reported 0 enrichment hits. Two root causes:

1. route-explorer's schedule endpoint only covers FUTURE flights —
   verified by curl: WN1942 / 2024-01-27 → no flights; WN7 /
   2026-05-27 → 1 result with equipmentIata=73H. The user's CSV is
   mostly historical, so route-explorer was a dead end for ~all rows.

2. Even when route-explorer DID return data, it ships IATA aircraft
   codes ("73H") while the rest of the app expects ICAO designators
   ("B738"). The saved string wouldn't have matched displayName or
   type-filter tables anyway.

Two fixes:

- FlightAwareLookup actor scrapes flightaware.com/live/flight/
  <CALLSIGN> for the trackpollBootstrap JSON embedded in the page.
  The activityLog.flights[] array contains 8–15 recent operations
  of that flight number, each with a real ICAO aircraftType
  ("B738", "B38M", etc.) and the route IATA. We walk braces to
  extract the JSON literal, then pick the best match:
    1. Same dep/arr route → most common type on that route
    2. Reverse direction (same airframe usually flies the return)
    3. Fallback: most common type across the entire activity log
  Verified by curl that FA isn't Cloudflare-gated and returns
  ICAO codes directly. Per-callsign result cached.

- AircraftDatabase.normalizedICAO(forCode:) converts either input
  form (IATA or ICAO) to canonical ICAO. New iataToICAO map covers
  the common 60+ codes (737 family, A320 family, widebodies,
  regionals, MD-80s). Anything missing falls through unchanged.

- Both enrichers (EnrichAircraftTypesView for existing flights,
  ImportCSVView during import) now run the two-step lookup:
  route-explorer first, FlightAware on miss. Result is normalized
  through normalizedICAO before saving so the Aircraft Stats screen
  recognizes the value.

Expected outcome: the user's 80 historical Southwest flights should
mostly get B737 / B738 / B38M codes assigned via FlightAware's
per-flight-# activity log (Southwest reuses flight numbers reliably
on the same routes).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Trey T
2026-05-29 19:00:02 -05:00
parent e5333ff965
commit 0c9d02f7d4
5 changed files with 268 additions and 14 deletions
+4
View File
@@ -86,6 +86,7 @@
HX1800001800000018000001 /* PassportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = HX1800001800000018000002 /* PassportView.swift */; };
HX1900001900000019000001 /* AircraftStatsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = HX1900001900000019000002 /* AircraftStatsView.swift */; };
HX2000002000000020000001 /* EnrichAircraftTypesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = HX2000002000000020000002 /* EnrichAircraftTypesView.swift */; };
HX2100002100000021000001 /* FlightAwareLookup.swift in Sources */ = {isa = PBXBuildFile; fileRef = HX2100002100000021000002 /* FlightAwareLookup.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -181,6 +182,7 @@
HX1800001800000018000002 /* PassportView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PassportView.swift; sourceTree = "<group>"; };
HX1900001900000019000002 /* AircraftStatsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AircraftStatsView.swift; sourceTree = "<group>"; };
HX2000002000000020000002 /* EnrichAircraftTypesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnrichAircraftTypesView.swift; sourceTree = "<group>"; };
HX2100002100000021000002 /* FlightAwareLookup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlightAwareLookup.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -326,6 +328,7 @@
HX1000001000000010000002 /* AirframeMetadataService.swift */,
HX1100001100000011000002 /* CSVFlightImporter.swift */,
HX1300001300000013000002 /* HistoryFilters.swift */,
HX2100002100000021000002 /* FlightAwareLookup.swift */,
);
path = Services;
sourceTree = "<group>";
@@ -527,6 +530,7 @@
HX1800001800000018000001 /* PassportView.swift in Sources */,
HX1900001900000019000001 /* AircraftStatsView.swift in Sources */,
HX2000002000000020000001 /* EnrichAircraftTypesView.swift in Sources */,
HX2100002100000021000001 /* FlightAwareLookup.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};