Source
Periodic public flight records
BTS publishes monthly On-Time Performance files. Separate archives need repeatable acquisition and consistent interpretation before they can support analysis.
RAW TO RELIABLE / ENGINEERING CASE STUDY / US DOT FLIGHTS
Parameterized US DOT flight-data ingestion with Python, AWS S3, Snowflake, dbt dimensional modeling, and Airflow orchestration for carrier performance and delay attribution.
Monthly batch processing · flight-level models and analytical marts · a recorded manual Airflow run, with source-linked implementation details.
Focus areas
Technology stack
01 / Executive Overview
Turn periodic public flight files into repeatable analytical models.
Source
BTS publishes monthly On-Time Performance files. Separate archives need repeatable acquisition and consistent interpretation before they can support analysis.
Pipeline
Python selects a year and month, extracts the CSV, compresses it to GZIP, and places it in a partitioned S3 location.
Warehouse
Snowflake COPY maps staged CSV fields into RAW_FLIGHTS and adds file provenance and load timestamps.
Modeling
Seven SQL models normalize records, define flight and dimension tables, and aggregate carrier performance and delay causes. Staging selects AA, UA, and DL.
Orchestration
A manual DAG coordinates download, compression and upload, warehouse loading, dbt run, and dbt test. Year/month parameters support historical-month selection.
Analytical output
Two monthly marts expose flight volume, on-time performance, cancellations, and delay-cause shares. These are analytical model definitions; no BI dashboard is claimed.
The implementation supports flight, carrier, cancellation, and delay-cause analysis. Counts above describe checked-in definitions, not processed flights or passed tests.
02 / System Architecture
Airflow coordinates the workflow above the data path; it is not a downstream destination.
Apache Airflow / manual orchestration and control
01 / Airflow task
BTS ZIP → CSV
02 / Airflow task
GZIP → S3
03 / Airflow task
COPY INTO
04 / Airflow task
Staging / Core / Mart
05 / Airflow task
dbt test
Acquisition / monthly BTS file to partitioned S3 object
01 / Source
Year / month selection
02 / Acquisition
Stream / extract
03 / Compression
One CSV stream
04 / Landing
year=YYYY / month=MM
Warehouse loading / continues from S3
01 / Input
Partitioned GZIP files
02 / Access
Storage Integration
03 / Loading
Matching .csv.gz files
04 / Raw layer
Source fields + metadata
dbt modeling / continues from RAW_FLIGHTS
01 / Input
Loaded raw history
02 / Normalize
stg_flights / view
03 / Model
One fact / three dimensions
04 / Aggregate
Two carrier-month models
The first two tasks acquire and upload a monthly file. The third loads Snowflake; the final two invoke dbt run and dbt test. Compression and S3 upload share one task.
Download and upload use year/month parameters. COPY scans matching GZIP files across the configured stage, and dbt rebuilds models from loaded raw history. Neither operation is restricted to the selected month’s XCom key.
03 / Apache Airflow Orchestration
One dependency chain, explicit handoffs, and a recorded manual run.
dot_flights_pipeline / task dependency chain
01 / Task 1
Stream the BTS ZIP and extract its CSV.
02 / Task 2
GZIP the CSV, hash the file, and upload it to S3.
03 / Task 3
Execute COPY INTO and print loading results and a total row count.
04 / Task 4
Invoke dbt run in the configured container project directory.
05 / Task 5
Invoke dbt test after the model build succeeds.
01 / Download
download_dot_dataStream the BTS ZIP and extract its CSV.
02 / Compress & upload
compress_and_upload_s3GZIP the CSV, hash the file, and upload it to S3.
03 / Load Snowflake
copy_into_snowflakeExecute COPY INTO and print loading results and a total row count.
04 / Build models
dbt_runInvoke dbt run in the configured container project directory.
05 / Run tests
dbt_testInvoke dbt test after the model build succeeds.
schedule=None,
start_date=datetime(2024, 1, 1),
catchup=False,task_download >> task_upload_s3 >> task_copy_snowflake >> task_dbt_run >> task_dbt_testRuntime parameters default to year 2024 and month 1. Selecting another month supports a manual historical run. There is no automatic month loop or scheduled backfill; data-model coverage beyond 2024 needs changes described below.
Compose defines LocalExecutor, a PostgreSQL metadata database, initialization, webserver, and scheduler services. The image uses Airflow 2.10.4 with Python 3.11 and installs dbt-snowflake, boto3, requests, and the Snowflake connector.

Original project screenshot of one successful manual Airflow run. The five task states include dbt_test success; individual dbt test results, loaded row counts, processed months, and the executed source revision are not shown.
Source: image linked in the audited DOT repository README. Full image preserved; the supplied image already obscures date fields.This recorded output supports one manual run’s successful task states. It does not identify the executed source revision or prove that each of the 17 current test declarations ran and passed. Reproduction gaps in the public checkout are listed in Evidence.
04 / Ingestion & Snowflake Loading
Separate source acquisition, object identity, and warehouse loading contracts.
Stream the parameterized BTS URL, extract the archive, and select its CSV. The standalone download script prints a lightweight row/header inspection.
Stream compression instead of loading the whole file in memory. Compute file size and SHA256 for the generated metadata log.
Use a deterministic monthly key. The standalone uploader also performs a post-upload existence check.
Map positional CSV fields and inject METADATA$FILENAME and CURRENT_TIMESTAMP for provenance.
s3_key = (
f"{S3_PROJECT_PREFIX}/raw/flights/"
f"year={year}/month={month:02d}/{gz_filename}"
)dot_flights/raw/flights/year=2024/month=01/on_time_2024_01.csv.gz is an illustrative expansion of the verified key template, not a recorded object listing.
The integration template associates external-stage access with an IAM role and allowed S3 locations. The stage references that integration. Owner configuration and the trust relationship must be established before loading; no credentials or private storage identifiers are reproduced here.
COPY overrides the stage’s uncompressed default with GZIP, skips the CSV header, and uses ON_ERROR = CONTINUE with FORCE = FALSE. The DAG prints COPY results and a total raw row count, but does not fail on rejected-record counts or validate a monthly expected total.
05 / dbt Data Modeling
Normalize first, retain a flight-level fact, then aggregate at carrier-month grain.
stg_flights is a view over RAW_FLIGHTS, filtered to AA, UA, and DL. It casts dates, numeric measures, and status flags; handles the 2400 time sentinel; and carries file/load metadata forward. Four core tables separate flight measures from carrier, calendar, and airport attributes.
Fact / Declared scheduled flight leg
flight_key / MD5 identifiercarrier_key · date_keyorigin_airport_key · dest_airport_keyflight_date · flight_number · tail_numberdelay minutes · distance_milesis_cancelled · is_divertedsource_file · loaded_atcarrier_key
Carrier code and metadata; AA, UA, DL names are explicitly mapped.
carrier_code · carrier_nameiata_code_carrier · dot_id_carrierdate_key
YYYYMMDD key; a bounded 2024 date spine with a source-derived upper filter.
full_date · year · month_numquarter · day_of_week · is_weekendorigin_airport_key → airport_key
Origin role; airport attributes are derived from both origin and destination records.
airport_code · airport_idcity · state · regiondest_airport_key → airport_key
Destination role; the same airport dimension serves this second relationship.
airport_code · airport_idcity · state · region MD5(
COALESCE(carrier_code, '') || '-' ||
COALESCE(CAST(flight_number AS VARCHAR), '') || '-' ||
COALESCE(CAST(flight_date AS VARCHAR), '') || '-' ||
COALESCE(origin, '') || '-' ||
COALESCE(dest, '') || '-' ||
COALESCE(tail_number, '')
) AS flight_key,The fact declares one scheduled leg per carrier, flight number, date, origin, and destination. Its hash also includes tail_number. It neither deduplicates rows nor filters out cancellations. MD5 does not establish uniqueness; repeated inputs and NULL-to-empty substitution retain row-identity assumptions.
Carrier rows use DISTINCT across carrier attributes; airports UNION distinct origin/destination attribute rows. Neither operation proves one row per key. Airport states map to Northeast, Midwest, South, West, or Territory/Other using the source’s Census-style grouping.
DATEADD(DAY, seq4(), '2024-01-01'::DATE) AS full_date
FROM TABLE(GENERATOR(ROWCOUNT => 366))dim_date filters this fixed range using MAX(flight_date) from staging. It does not derive a dynamic lower bound or expand beyond 366 dates. All three dbt layers are explicit: Staging / Core / Mart.
06 / Data Quality & Validation
Show what is declared, what the recorded run shows, and which checks remain absent.
not_null
Staging identifiers, flags, distance, year/month, and provenance. The distance test is unconditional, despite its description referring to operated flights.
accepted_values
Carrier AA/UA/DL; cancellation and diversion booleans; flight_year = 2024; flight_month from 1 to 12.
Core-key coverage
No unique or relationships tests are defined for the flight identifier, dimensions, or foreign keys. Their presence in SQL does not prove stable join cardinality.
The YAML defines 12 not_null and five accepted_values checks, all on stg_flights. These protect essential fields and selected categorical domains. It does not declare tests on cancellation codes or delay flags, despite the broader README description.
The Airflow image shows a successful dbt_test task. No manifest, run_results, or test log is published to establish how many checks were executed or passed. A successful command in that recorded environment does not verify current core joins.
Historical inputs beyond 2024 conflict with the accepted-year test and date coverage. The actual definitions determine the presentation; README claims about unique and relationship tests are excluded.
07 / Analytical Data Marts
Two implemented SQL marts answer performance and delay-cause questions without invented KPI results.
Analytical table / carrier-month
mart_ carrier_ performance_ monthlyCompare carrier reliability month by month.
carrier_key / carrier_name / year / month_num / month_nametotal_flightscancelled_count / cancelled_pctdiverted_count / diverted_pcton_time_count / on_time_pctavg_dep_delay / avg_arr_delayp75_arr_delay / p95_arr_delayAnalytical table / carrier-month
mart_ delay_ cause_ breakdown_ monthlySeparate reported delay minutes by cause.
carrier_key / carrier_name / year / month_num / month_namedelayed_flights_counttotal_carrier_delay_min / pct_carriertotal_weather_delay_min / pct_weathertotal_nas_delay_min / pct_nastotal_security_delay_min / pct_securitytotal_late_aircraft_delay_min / pct_late_aircrafttotal_delay_min SUM(CASE WHEN arr_delayed_15 = 0 AND NOT is_cancelled AND NOT is_diverted THEN 1 ELSE 0 END) AS on_time_count,
ROUND(on_time_count * 100.0 / total_flights, 2) AS on_time_pct,The delay model’s actual filename is mart_delay_cause_breakdown_monthly, rather than the README’s attribution name. These measures describe model logic, not recorded results, causal findings, or a dashboard. Unverified dimension cardinality can affect joined aggregates.
08 / Engineering Decisions & Reliability
Explain the processing contracts and tradeoffs behind a repeatable monthly workflow.
Source + documentation
Source evidence
Source + documentation
Source evidence
Source + documentation
Source evidence
Source + documentation
Source evidence
Source + documentation
Source evidence
Skip ZIP or GZIP creation if the path exists.
Existence does not validate completeness or freshness; interrupted downloads are not written atomically.
head_object checks the expected monthly object key.
Any ClientError is treated as absence. The recorded SHA256 is not compared with an existing object; new metadata logs are written on reruns.
FORCE = FALSE applies file-load-history protection.
No row-level MERGE or business-key deduplication is implemented. ON_ERROR = CONTINUE requires reconciliation of skipped records.
Staging is a view; core and marts materialize as tables.
This rebuilds selected raw history rather than incrementally merging one month. Repeatability still depends on raw records and join grain.
The setup SQL configures an XSMALL warehouse, AUTO_SUSPEND = 60, AUTO_RESUME = TRUE, and an initially suspended state. These are cost-aware settings; billing outcomes or measured savings are not established.
Tasks inherit one retry with a two-minute delay. Reruns rely on local artifacts, object existence, and COPY history. Inspect rejected COPY rows and validate model grain before interpreting a successful task chain as complete data processing.
09 / Evidence, Limitations & Source Code
Source definitions and one recorded manual run support the walkthrough, with their boundaries visible.
Parameterized acquisition, GZIP compression, partitioned S3 keys, loading templates and COPY logic, five dependent tasks, seven dbt SQL models, and 17 staging test declarations. No source services were executed during this presentation audit.
One recorded manual run of dot_flights_pipeline with five successful task states and no configured schedule. It does not establish continuously scheduled production operation, a completed backfill inventory, individual passed-test counts, or analytical KPI values.
The actual DAG is Airflow/dot_flights_pipeline.py, outside the absent dags/ mount. Compose references an absent dbt_dot_flights directory; the checked-in dbt project is under dbt/ and expects models/, while SQL is under Models/. There is no requirements.txt; dependencies are installed in the Dockerfile. The recorded environment cannot be reproduced directly from this layout.
Storage SQL contains placeholder role/location values and uses ENABLE where the integration property is ENABLED. The stage location needs owner configuration. Date coverage is bounded to 2024, and staging accepts only that year. No published run logs resolve these snapshot discrepancies.
Shared limits: no flight-key uniqueness or relationship tests; no reconciliation results for skipped COPY rows; no verified row counts, historical-month completion, compression savings, or cloud billing. Staging time expressions also mix departure-time conversion with arrival-time strings; typed runtime output is not published.
Reviewed source revision e3628dc. Evidence links target actual files at that revision. Source credentials and private identifiers were excluded from copied content and excerpts.