itsb/jq/ntsb.jq

674 lines
23 KiB
Plaintext

# National Transportation Safety Board feed generator
# Expects JSON data from a NTSB CAROL investigation data export, outputs xmltodict-compatible JSON
# WARNING: Dates are locale-sensitive; the RSS feed might not generate correctly with another locale.
# Convert an object to an HTML definition list
def to_dl: if length > 0 then "<dl>\([to_entries[]|select(.value != null)|"<dt>\(.key)</dt><dd>\(.value)</dd>"]|join(""))</dl>" else null end;
# Convert an array to a HTML unordered list
def to_ul: if length > 0 then "<ul><li>\(map(values)|join("</li><li>"))</li></ul>" else null end;
{
"AL": "Alabama",
"AK": "Alaska",
"AZ": "Arizona",
"AR": "Arkansas",
"AO": "Atlantic Ocean",
"CA": "California",
"CB": "Caribbean Sea",
"CO": "Colorado",
"CT": "Connecticut",
"DE": "Delaware",
"DC": "District of Columbia",
"FL": "Florida",
"GA": "Georgia",
"GM": "Gulf of Mexico",
"HI": "Hawaii",
"ID": "Idaho",
"IL": "Illinois",
"IN": "Indiana",
"IA": "Iowa",
"KS": "Kansas",
"KY": "Kentucky",
"LA": "Louisiana",
"ME": "Maine",
"MD": "Maryland",
"MA": "Massachusetts",
"MI": "Michigan",
"MN": "Minnesota",
"MS": "Mississippi",
"MO": "Missouri",
"MT": "Montana",
"NE": "Nebraska",
"NV": "Nevada",
"NH": "New Hampshire",
"NJ": "New Jersey",
"NM": "New Mexico",
"NY": "New York",
"NC": "North Carolina",
"ND": "North Dakota",
"OH": "Ohio",
"OK": "Oklahoma",
"OR": "Oregon",
"OF": "Other Foreign",
"PO": "Pacific Ocean",
"PA": "Pennsylvania",
"PR": "Puerto Rico",
"RI": "Rhode Island",
"SC": "South Carolina",
"SD": "South Dakota",
"TN": "Tennessee",
"TX": "Texas",
"UT": "Utah",
"VT": "Vermont",
"VA": "Virginia",
"WA": "Washington",
"WV": "West Virginia",
"WI": "Wisconsin",
"WY": "Wyoming"
} as $states
| {
"AF": "Afghanistan",
"AL": "Albania",
"AG": "Algeria",
"AQ": "American Samoa",
"AN": "Andorra",
"AO": "Angola",
"AV": "Anguilla",
"AY": "Antarctica",
"AC": "Antigua And Barbuda",
"AR": "Argentina",
"AM": "Armenia",
"AA": "Aruba",
"AT": "Ashmore And Cartier Islands",
"AS": "Australia",
"AU": "Austria",
"AJ": "Azerbaijan",
"BF": "Bahamas",
"BA": "Bahrain",
"FQ": "Baker Island",
"BG": "Bangladesh",
"BB": "Barbados",
"BS": "Bassas Da India",
"BO": "Belarus",
"BE": "Belgium",
"BH": "Belize",
"BN": "Benin",
"BD": "Bermuda",
"BT": "Bhutan",
"BL": "Bolivia",
"BK": "Bosnia And Herzegovina",
"BC": "Botswana",
"BV": "Bouvet Island",
"BR": "Brazil",
"IO": "British Indian Ocean Territory",
"VG": "British Virgin Islands",
"BX": "Brunei",
"BU": "Bulgaria",
"UV": "Burkina",
"BM": "Burma",
"BY": "Burundi",
"CB": "Cambodia",
"CM": "Cameroon",
"CA": "Canada",
"CV": "Cape Verde",
"CJ": "Cayman Islands",
"CT": "Central African Republic",
"CD": "Chad",
"CI": "Chile",
"CH": "China",
"KT": "Christmas Island",
"IP": "Clipperton Island",
"CK": "Cocos (keeling) Islands",
"CO": "Colombia",
"CN": "Comoros",
"CF": "Congo",
"CW": "Cook Islands",
"CR": "Coral Sea Islands",
"CS": "Costa Rica",
"IV": "Cote D'ivoire",
"HR": "Croatia",
"CU": "Cuba",
"CUW": "Curacao",
"CY": "Cyprus",
"EZ": "Czech Republic",
"KN": "Democratic People's Republic Of Korea",
"DA": "Denmark",
"DJ": "Djibouti",
"DO": "Dominica",
"DR": "Dominican Republic",
"EC": "Ecuador",
"EG": "Egypt",
"ES": "El Salvador",
"EK": "Equatorial Guinea",
"ER": "Eritrea",
"EN": "Estonia",
"ET": "Ethiopia",
"EU": "Europa Island",
"FK": "Falkland Islands (islas Mal) as)",
"FO": "Faroe Islands",
"FM": "Federated States Of Micronesia",
"FJ": "Fiji",
"FI": "Finland",
"FT": "Fr Terr Of Afars Issas",
"FR": "France",
"FG": "French Guiana",
"FP": "French Polynesia",
"FS": "French Southern And Antarctic Lands",
"GB": "Gabon",
"GA": "Gambia",
"GZ": "Gaza Strip",
"GG": "Georgia",
"GE": "Germany",
"GH": "Ghana",
"GI": "Gibraltar",
"GO": "Glorioso Islands",
"GR": "Greece",
"GL": "Greenland",
"GJ": "Grenada",
"GP": "Guadeloupe",
"GT": "Guatemala",
"GK": "Guernsey",
"GV": "Guinea",
"PU": "Guinea-bissau",
"GY": "Guyana",
"HA": "Haiti",
"HM": "Heard Island And Mcdonald Islands",
"HO": "Honduras",
"HK": "Hong Kong",
"HU": "Hungary",
"IC": "Iceland",
"IN": "India",
"ID": "Indonesia",
"IR": "Iran",
"IZ": "Iraq",
"EI": "Ireland",
"IM": "Isle of Man",
"IS": "Israel",
"IT": "Italy",
"IY": "Ivory Coast",
"JM": "Jamaica",
"JN": "Jan Mayen",
"JA": "Japan",
"JE": "Jersey",
"JO": "Jordan",
"JU": "Juan De Nova Island",
"KZ": "Kazakhstan",
"KE": "Kenya",
"KQ": "Kingman Reef",
"KR": "Kiribati",
"KU": "Kuwait",
"KG": "Kyrgyzstan",
"LA": "Laos",
"LG": "Latvia",
"LE": "Lebanon",
"LT": "Lesotho",
"LI": "Liberia",
"LY": "Libya",
"LS": "Liechtenstein",
"LH": "Lithuania",
"LU": "Luxembourg",
"MC": "Macau",
"MK": "Macedonia",
"MA": "Madagascar",
"MI": "Malawi",
"MY": "Malaysia",
"MV": "Maldives",
"ML": "Mali",
"MT": "Malta",
"MS": "Marshall Islands",
"MB": "Martinique",
"MR": "Mauritania",
"MP": "Mauritius",
"MF": "Mayotte",
"MX": "Mexico",
"MD": "Moldova",
"MN": "Monaco",
"MG": "Mongolia",
"MW": "Montenegro",
"MH": "Montserrat",
"MO": "Morocco",
"MZ": "Mozambique",
"WA": "Namibia",
"NR": "Nauru",
"NP": "Nepal",
"NL": "Netherlands",
"NT": "Netherlands Antilles",
"NC": "New Caledonia",
"NZ": "New Zealand",
"NU": "Nicaragua",
"NG": "Niger",
"NI": "Nigeria",
"NE": "Niue",
"NF": "Norfolk Island",
"CQ": "Northern Mariana Islands",
"NO": "Norway",
"MU": "Oman",
"PK": "Pakistan",
"PW": "Palau",
"PM": "Panama",
"PP": "Papua New Guinea",
"PF": "Paracel Islands",
"PA": "Paraguay",
"PE": "Peru",
"RP": "Philippines",
"PC": "Pitcairn Islands",
"PL": "Poland",
"PO": "Portugal",
"QA": "Qatar",
"KS": "Republic Of Korea",
"RE": "Reunion",
"RO": "Romania",
"RS": "Russia",
"RW": "Rwanda",
"SM": "San Marino",
"TP": "Sao Tome And Principe",
"SA": "Saudi Arabia",
"SG": "Senegal",
"SR": "Serbia",
"SE": "Seychelles",
"SL": "Sierra Leone",
"SN": "Singapore",
"LO": "Slovakia",
"SI": "Slovenia",
"BP": "Solomon Islands",
"SO": "Somalia",
"SF": "South Africa",
"SX": "South Georgia & S Sandwich Isl",
"SP": "Spain",
"PG": "Spratly Islands",
"CE": "Sri Lanka",
"SH": "St Helena",
"SC": "St Kitts And Nevis",
"ST": "St Lucia",
"SB": "St Pierre And Miquelon",
"VC": "St Vincent And The Grenadines",
"SU": "Sudan",
"NS": "Suriname",
"SV": "Svalbard",
"WZ": "Swaziland",
"SW": "Sweden",
"SZ": "Switzerland",
"SY": "Syria",
"TW": "Taiwan",
"TI": "Tajikistan",
"TZ": "Tanzania",
"TH": "Thailand",
"TO": "Togo",
"TL": "Tokelau",
"TN": "Tonga",
"TD": "Trinidad And Tobago",
"TE": "Tromelin Island",
"PS": "Trust Terr Of Pacific Islands",
"TS": "Tunisia",
"TU": "Turkey",
"TX": "Turkmenistan",
"TK": "Turks And Caicos Islands",
"TV": "Tuvalu",
"UG": "Uganda",
"UP": "Ukraine",
"TC": "United Arab Emirates",
"UK": "United Kingdom",
"USA": "United States",
"UN": "Unknown",
"UY": "Uruguay",
"UZ": "Uzbekistan",
"NH": "Vanuatu",
"VT": "Vatican City",
"VE": "Venezuela",
"VM": "Vietnam",
"WQ": "Wake Island",
"WF": "Wallis And Futuna",
"WE": "West Bank",
"WN": "West Indies",
"WI": "Western Sahara",
"WS": "Western Samoa",
"YM": "Yemen",
"CG": "Zaire",
"ZA": "Zambia",
"ZI": "Zimbabwe"
} as $countries
| {
"AIR": "Airplane",
"BALL": "Balloon",
"BLIM": "Blimp",
"CSF": "Commercial space flight",
"GLI": "Glider",
"GYRO": "Gyroplane",
"HELI": "Helicopter",
"PPAR": "Powered parachute",
"PLFT": "Powered-lift",
"RCKT": "Rocket",
"ULTR": "Ultralight",
"UNK": "Unknown",
"UNMANNED": "Unmanned",
"WSFT": "Weight-shift"
} as $aircraft_categories
| {
"ARMF": "Armed Forces",
"NUSC": "Non-U.S., commercial",
"NUSN": "Non-U.S., non-commercial",
"103": "Part 103: Ultralight",
"107": "Part 107: Small UAS",
"121": "Part 121: Air carrier",
"125": "Part 125: 20+ pax,6000+ lbs",
"129": "Part 129: Foreign",
"133": "Part 133: Rotorcraft ext. load",
"135": "Part 135: Air taxi & commuter",
"137": "Part 137: Agricultural",
"415": "Part 415: Commercial space flight",
"431": "Part 431: Commercial space flight",
"435": "Part 435: Commercial space flight",
"437": "Part 437: Commercial space flight",
"091K": "Part 91 subpart k: Fractional",
"091": "Part 91: General aviation",
"091F": "Part 91F: Special flt ops.",
"PUBU": "Public aircraft",
"UNK": "Unknown"
} as $far_parts
| {
"MEDE": "Medical emergency",
"DISC": "Discretionary",
"ORGT": "Organizational"
} as $air_medical_types
| {
"ELEC": "Electric",
"GTFN": "Geared turbofan",
"HR": "Hybrid rocket",
"LR": "Liquid rocket",
"NONE": "None",
"REC": "Reciprocating",
"SR": "Solid rocket",
"TF": "Turbo fan",
"TJ": "Turbo jet",
"TP": "Turbo prop",
"TS": "Turbo shaft",
"UNK": "Unknown"
} as $engine_types
| {
"AAPL": "Aerial application",
"AOBV": "Aerial observation",
"ADRP": "Air drop",
"ASHO": "Air race/show",
"BANT": "Banner tow",
"BUS": "Business",
"EXEC": "Executive/Corporate",
"EXLD": "External load",
"FERY": "Ferry",
"FIRF": "Firefighting",
"FLTS": "Flight test",
"GLDT": "Glider tow",
"INST": "Instructional",
"OWRK": "Other work use",
"PERS": "Personal",
"POSI": "Positioning",
"PUBU": "Public aircraft",
"PUBF": "Public aircraft - federal",
"PUBL": "Public aircraft - local",
"PUBS": "Public aircraft - state",
"SKYD": "Skydiving",
"UNK": "Unknown"
} as $flight_operation_types
| {
"CARG": "Cargo",
"MAIL": "Mail contract only",
"PAX": "Passenger",
"PACA": "Passenger/Cargo",
"UNK": "Unknown"
} as $flight_service_types
| {
"DOM": "Domestic",
"INT": "International"
} as $flight_terminal_types
| {
"0": "Level 0: No Driving Automation",
"1": "Level 1: Driver Assistance",
"2": "Level 2: Partial Driving Automation",
"3": "Level 3: Conditional Driving Automation",
"4": "Level 4: High Driving Automation",
"5": "Level 5: Full Driving Automation"
} as $driving_automation_levels
| {
"ACC": "Accident",
"INC": "Incident",
"OCC": "Occurrence"
} as $event_types
| {
# This list only includes aviation-related event codes, as codes are not officially documented and some
# aviation codes can be found as the available values of the EventSequence.CICTTEventSOEGroup field
# from CAROL's API.
"000": "Unknown or undetermined",
"090": "Abnormal runway contact",
"091": "Tailstrike",
"092": "Hard landing",
"093": "Dragged wing/rotor/float/other",
"094": "Landing gear collapse",
"095": "Landing gear not configured",
"096": "Nose over/nose down",
"097": "Roll over",
"100": "Air traffic event",
"110": "Cabin safety event",
"120": "Controlled flight into terr/obj (CFIT)",
"130": "Emergency descent initiated",
"140": "Engine shutdown",
"150": "Fire/smoke (non-impact)",
"160": "Explosion (non-impact)",
"170": "Fire/smoke (post-impact)",
"180": "Explosion (post-impact)",
"190": "Fuel related",
"191": "Fuel starvation",
"192": "Fuel exhaustion",
"193": "Fuel contamination",
"194": "Wrong fuel",
"200": "Ground collision",
"210": "Structural icing",
"220": "Low altitude operation/event",
"230": "Loss of control on ground",
"231": "Dynamic rollover",
"232": "Ground resonance",
"240": "Loss of control in flight",
"241": "Aerodynamic stall/spin",
"242": "Loss of tail rotor effectiveness",
"243": "Retreating blade stall",
"244": "Settling with power/vortex ring state",
"245": "Mast bumping",
"250": "Midair collision",
"260": "Near midair/TCAS alert/loss of separation",
"270": "Abrupt maneuver",
"271": "Inflight upset",
"280": "Navigation error",
"281": "Course deviation",
"282": "Altitude deviation",
"283": "Airspace incursion",
"284": "Wrong surface or wrong airport",
"300": "Runway excursion",
"310": "Wildlife encounter (non-bird)",
"320": "Runway incursion veh/AC/person",
"330": "Sys/Comp malf/fail (non-power)",
"331": "Pressure/environ sys malf/fail",
"332": "Electrical system malf/failure",
"333": "Flight control sys malf/fail",
"334": "Flight instrument malf/fail",
"335": "Nav system malfunction/failure",
"336": "Comm system malf/failure",
"337": "Aircraft structural failure",
"338": "Part(s) separation from AC",
"340": "Powerplant sys/comp malf/fail",
"341": "Loss of engine power (total)",
"342": "Loss of engine power (partial)",
"343": "Uncontained engine failure",
"350": "Security/criminal event",
"360": "Turbulence encounter",
"361": "Aircraft wake turb encounter",
"362": "Clear air turbulence encounter",
"370": "Landing area undershoot",
"380": "Landing area overshoot",
"390": "Windshear or thunderstorm",
"400": "Other weather encounter",
"401": "VFR encounter with IMC",
"402": "Loss of visual reference",
"410": "Terrain avoidance alert",
"420": "Collision avoidance alert",
"430": "Stall warn/stick-shaker/pusher",
"440": "Off-field or emergency landing",
"441": "Ditching",
"450": "Hazardous material leak/spill",
"460": "Evacuation",
"470": "Collision with terr/obj (non-CFIT)",
"480": "External load event (Rotorcraft)",
"850": "Medical event",
"901": "Birdstrike"
} as $event_codes
| {
"rss": {
"@version": "2.0",
"channel": {
"title": "National Transportation Safety Board",
"description": "Completed investigation reports from the National Transportation Safety Board of the United States.",
"link": "https://www.ntsb.gov/",
"language": "en-US",
"pubDate": (now | strftime("%a, %d %b %Y %T %z")),
"docs": "https://www.rssboard.org/rss-specification",
"ttl": 1440,
"generator": "ITSB",
"item": [.[] | {
"title": .cm_ntsbNum,
"author": .cm_agency,
"description": (({
"Unique identifier": .cm_mkey,
"NTSB case number": .cm_ntsbNum,
"Transportation mode": .cm_mode,
"Investigation agency": .cm_agency,
"Completion status": .cm_completionStatus,
"Occurrence date": .cm_eventDate,
"Publication date": .cm_originalPublishedDate,
"Report type": .cm_mostRecentReportType,
"Last updated": .cm_recentReportPublishDate,
"Event type": (.cm_eventType | try ($event_types[.] // .) catch null),
"Location": (
.cm_city +
if .cm_state != null then ", \(.cm_state | $states[.] // .)"
elif .cm_country != null then ", \(.cm_country | $countries[.] // .)"
else "" end
),
"Airport": (if .airportName != null then "\(.airportName) (\(.airportId))" else null end),
"Weather conditions": (.accidentSiteCondition | if . == "VMC" then "Visual Meteorological Conditions" elif . == "IMC" then "Instrument Meteorological Conditions" else . end),
"Injuries": "\(.cm_fatalInjuryCount) fatal, \(.cm_seriousInjuryCount) serious, \(.cm_minorInjuryCount) minor",
"Pipeline operator": .pipelineOperator,
"Pipeline type": .pipelineType,
"Regulator type": .regulatorType,
"Chemical released": .chemicalReleased,
"Hazard class": .hazardClass,
"Hazardous material operator": .hazmatOperator,
"Hazardous material type": .hazmatType,
"State of material": .stateOfMaterial,
"Container type": .containerType,
"Department of Transportation container specifications": .dotContainerSpec,
"Competent authority": .competentAuthority,
"Preliminary narrative": .prelimNarrative,
"Factual narrative": .factualNarrative,
"Analysis": .analysisNarrative,
"Probable cause": .cm_probableCause,
"Has safety recommendations": .cm_hasSafetyRec,
} | to_dl) + ([
(.cm_vehicles // [])
| to_entries[]
| "<h2>Vehicle \(.key + 1)</h2>" + (.value | {
"Aircraft category": (.aircraftCategory | try ($aircraft_categories[.] // .) catch null),
"Make": .make,
"Model": .model,
"Amateur built": .amateurBuilt,
"Engines": (
[
(.cm_engines // [])
| group_by(.engineType)[]
| "\(length) \(.[0].engineType | (try ($engine_types[.] // .) catch "Unknown"))"
]
| join(", ")
| if . == "" then null else . end
),
"Registration number": .registrationNumber,
"Registered owner": .registerOwner,
"Operator": .operatorName,
"Second pilot present": .secondPilotPresent,
"Flight conducted under": (.regulationFlightConductedUnder | try ($far_parts[.] // .) catch null),
"Flight operation type": (.flightOperationType | try ($flight_operation_types[.] // .) catch null),
"Flight service type": (.flightServiceType | try ($flight_service_types[.] // .) catch null),
"Flight terminal type": (.flightTerminalType | try ($flight_terminal_types[.] // .) catch null),
"Medical flight type": (.airMedicalType | try ($air_medical_types[.] // .) catch null),
"Scheduled flight": (.flightScheduledType | if . == "SCHD" then true elif . == "NSCH" then false else . end),
"Commercial sightseeing flight": .revenueSightseeing,
"Railroad name": .railroadName,
"Equipment type": .equipmentType,
"Train name": .trainName,
"Train number": .trainNum,
"Train type": .trainType,
"Callsign": .callSign,
"Total cars": .totalCars,
"Total locomotive units": .totalLocomotiveUnits,
"Trailing tons": .trailingTons,
"Vessel name": .vesselName,
"Vessel type": .vesselType,
"IMO number": .imoNum,
"Maritime Mobile Service Identity": .mmsi,
"Port of registry": .portOfRegistry,
"Safety Management Certificate number": .smcNum,
"Classification society": .classificationSociety,
"Flag state": .flagState,
"Traffic unit name": .trafficUnitName,
"Traffic unit type": .trafficUnitType,
"Serial number": .SerialNumber,
"Damage level": .DamageLevel,
"Carrying hazardous materials": .carryingHazmat,
"Has released hazardous materials": .releasedHazmat,
"Units": ([
(.vehicles // [])[] | {
"Type": .vehicleType,
"Name": .vehicleName,
"Make": .vehicleMake,
"Model": .vehicleModel,
"Year": .vehicleYear,
"Engine type": .engineType,
"Transmission type": .transmissionType,
"Driving automation level": (.drivingAutomationLevel | try ($driving_automation_levels[.] // .) catch null),
"Gross vehicle weight rating": .gvwr
} | to_dl
] | to_ul),
"Events": ([
(.cm_events // [])[] | (
(
# Try to use the known event codes to map to more specific names
(try $event_codes[.cm_tier2Num] catch null)
// (try $event_codes[.cm_tier1Num] catch null)
# Fallback to the sometimes provided generic event group name
// .cicttEventSOEGroup
# Fallback to an error message
// "Unknown event code \(.cm_eventCode)"
)
+ if .cm_isDefiningEvent then "" else " (non-defining event)" end
)
] | to_ul),
"Findings": ([
(.cm_findings // [])[] | (.cm_findingText + if .cm_inPc then "" else " (not part of the probable cause)" end)
] | to_ul)
} | to_dl)
] | join("")) | gsub("[\\x00-\\x1f]"; "") | gsub("\r"; "<br>")),
"link": "https://data.ntsb.gov/carol-repgen/api/Aviation/ReportMain/GenerateNewestReport/\(.cm_mkey)/pdf",
"guid": {
"@isPermaLink": false,
"#text": (.cm_mkey | tostring)
},
"pubDate": (
# Prefer the most recent report publication date when available.
# Both dates use different formats; the most recent report publication date has milliseconds
# but the original publication date does not.
# strptime does not support milliseconds, so we just cut the part after the seconds.
(.cm_recentReportPublishDate // .cm_originalPublishedDate)[:19]
| strptime("%Y-%m-%dT%H:%M:%S")
| mktime
| strftime("%a, %d %b %Y %T %z")
)
}]
}
}
}