
OUTDATED: please visit api-gateway for up to date documentation and information (July 2026).
The program is built on a metadata-drive architecture. Instead of hardcoding API endpoints or database schemas, the systesm read from a sources.json file. This allows us to add new Census products (or products from other sources) simply by updating the json file.
The interface is divided into five functional "Focus Zones" to manage the complexity of geographic data:
-
Data Source (Top Bar 1): Horizontal selection of the Census product (e.g., ecnbasic).
-
Geo Scope (Top Bar 2): Horizontal selection of granularity (National, State, or County).
-
Actions (Middle Left): Vertical menu for Sync, Backup, and Restore.
-
State List (Middle Center): Scrollable list of US States with status icons.
-
County Status (Middle Right): Dynamic drill-down showing residency status for counties in the selected state.
The core logic follows a strict three-step process to ensure data integrity and performance.
The program utilizes the requests library with a retry strategy to fetch data from the Census API.
url = f"{base_url}?get={variables}&for={location}&key={your_key_here}"
Before data hits the database, it is "cleaned" into a Tab-Separated Values (TSV) format.
-
Null Handling: Census "None" or empty values are explicitly converted to the string NULL.
-
Normalization: Geographic FIPS codes are padded (e.g., 1 becomes 01) to match the reference schema.
To handle millions of rows, the program avoids standard INSERT statements, which are slow.
INSERT INTO production_table SELECT * FROM temporary_table
ON CONFLICT (unique_hash) DO NOTHING;
The Backup and Restore system acts as a manual version control for your datasets.
-
Snapshots: The Backup action uses a "Shadow Table" approach (table_name_bk). It performs a server-side clone of the production data.
-
Restoration: The Restore action drops the production table and reinstates the backup. Crucial: The program manually reconstructs the UNIQUE index on the unique_hash column after a restore, as cloning data alone does not copy table constraints.
| Key |
Action |
TAB |
Cycle focus between the 5 UI Zones |
Arrows |
Navigate within the active zone (horizontal (left/right) for bars, vertical (up/down) for lists) |
SPACE |
Queue a state or county for batch processing |
C |
Select / deselect ALL counties in the highlighted state |
ENTER |
Execute the highlighted action (sync, backup or restore) |
Q |
Gracefully exit the program |
-
.env: Must contain DB_NAME, DB_USER, DB_PASS, DB_HOST, and CENSUS_API_KEY.
-
sources.json: The metadata configuration file.
-
PostgreSQL Tables:
- Raw Schema: The raw schema must contain the tables defined in the JSON, each with a temporary counterpart and a backup version (e.g.,
ecn_2022_st and ecn_2022_st_tmp and ecn_2022_st_bk)
- Reference Schema: Tables with FIPS codes for the states and counties.
- Initialization
- Load environmental variables
- Set up logging file:
data_downloader_county.log
- Global Data Loading
-
Load json file sources.json
Loads metadata configuration from json; drives entire dynamic ETL logic
-
chunk_list(lst, n)
Yield successive n-sized chunks from a list
-
load_states_from_db()
Fetches the master list of 50 states (plus territories) from the reference.states_fips table. This populates the central UI panel.
-
load_counties_from_db()
Fetches the complete national directory of counties (~3,200 records) from reference.county_fips. This is stored in memory to allow for instant filtering when browsing states.
- Database Operations
-
get_synced_states(geo_meta)
Queries the active production table to see which states already have data. It returns a set of FIPS codes used to turn UI icons Green (the "Green Code" logic).
-
get_synced_counties(table, state_fips)
Performs a targeted query for a specific state to identify which of its counties are already in the database. This is used for the "drill-down" status panel.
-
backup(stdscr, geo_meta)
Clones the current production table into a shadow table (suffixed with _bk). This uses server-side cloning for near-instant snapshots of millions of rows.
-
restore_backup(stdscr, geo_meta)
The "Undo" function. It drops the current production table and replaces it with the backup. It also manually restores the UNIQUE constraints that are lost during table cloning.
-
extract_column_label(label_string)
Extracts the column (or cohort) name in the ACS data.
- Decoupled Stage 1: Extract (API Downloads)
-
extract_bls_timeseries(stdscr, source-meta, active_dataset_idx, start_year='2015', end_year="2024")
Parses structural metadata layout tables to dynamically chunk and fetch large Series ID arrays from the BLS API.
-
extract_data_acs5(stdscr, s_fips, c_fips, source_meta, geo_meta, s_abbr)
The core engine for ACS (American Community Survey) data. This data is significantly different from the other datasets.
-
extract_data_standard(stdscr, s_fips, c_fips, source_meta, geo_meta, s_abbr)
The core engine. It handles:
- Decoupled Stage 2: Upload (Transform and Postgres Bulk Load)
-
upload_bls_timeseries(stdscr, source_meta, active_dataset_idx)
Transforms raw multi-chunk JSON arrays into relational structures and updates the core database tables.
-
upload_data_acs5(stdscr, s_abbr, source_meta, geo_meta)
-
upload_data_standard(stdscr, s_abbr, source_meta, geo_meta)
- Action Handler
-
confirm_action(stdscr, message)
Displays a modal confirmation window (for backup and restoring backup tables).
-
run_action(stdscr, selections, selected_states, selected_counties, geo_meta)
The "Switchboard." It interprets which action is highlighted in the menu and builds the work queue (determining if it should process one county, one state, or a batch of states). Determines which ETL pipeline to run (ACS or main).
- UI Rendering & Feedback
-
draw_ui(stdscr, status, details)
A "blocking" screen overlay. It hides the dashboard and shows a progress bar or status message (e.g., "SYNCING 5/50") during heavy network or database tasks.
-
draw_interface(stdscr, focus_idx, sel, scroll, c_scroll, synced_st, selected_st, selected_co, synced_co)
The primary rendering engine. It draws all 5 zones of the dashboard, calculates scroll positions, and applies color pairs based on the sync status of the data.
-
show_summary_popup(stdscr, targets_synced, record_count)
Displays a modal box summarizing the ETL results.
- Main Loop
main(stdscr)
The entry point. It initializes the curses environment, manages the focus_idx (which zone is active), and listens for keyboard interrupts to update the program state.
Selection Logic Hierarchy
When you hit ENTER to sync, the program decides what to download based on this priority:
-
County Queue: If any counties were selected via the C key.
-
State Queue: If any states were checked via SPACE.
-
Active Highlight: If nothing is checked, it syncs only the state/level currently under the cursor.
flowchart TD
start(("start"))
--> dotenv["Load environmental variables"]
--> raw_data_file["make `raw_data` file if it doesn't exist"]
--> logging_file["set up logging"]
--> source["read `sources.json` file"]
--> load_states[["state_info = load_states_from_db()"]]
--> load_counties[["county_master = load_counties_from_db()"]]
--> main_loop{main_loop}
-- true --> main_loop
-- false --> stop(("stop"))
Yield successive n-sized chunks from a list
Returns: a n-sized chunks
flowchart TD
start(("start"))
--> for_loop{"for i in range(0, len(lst), every nth value)"}
-- true
--> yield["yield lst[i:i+n]"]
--> another_i{another i?}
-- true
--> for_loop
another_i -- false
--> stop(("stop"))
Indentifies all states in reference schema: reference.states_fips
Returns: a dataframe with columns ['fips_codes', 'state_name', 'state_abbr']
flowchart TD
start(("start"))
--> try{try-except}
-- try
--> connect[("database connect")]
--> if{"if national level"}
-- true
--> national["return national geo_id"]
--> stop(("stop"))
if -- false --> state_codes[("get set of distinct state codes in table")] --> stop
try -- except --> empty["return empty set"] --> stop
Identifies all counties from reference schema: reference.county_fips
Returns: a dataframe with columns ['state_fips', 'county_fips', 'county_name'].
flowchart TD
start(("start"))
--> try{try-except}
-- try --> connect[(connect to database)]
--> query[query database]
--> df[save result of query as a dataframe]
--> close_connection
--> stop((return dataframe))
try -- except --> log_error>"log error"]
--> except_stop((return empty dataframe))
Identifies states already in database
Returns: a set of 2 digit fips strings of the states present in the database
flowchart TD
start((start))
--> connect[(connect to database)]
--> table
--> if_national{if national level table}
-- false --> query["QUERY distinct state_fips in table"]
--> res[convert to strings]
--> close_connection
--> return_res((return res))
if_national -- true -->
query_us["QUERY if any entries in table"]
--> if_results{if results exist}
-- true --> return_us(("return set {'00'}"))
if_results -- false --> return_empty((return empty set))
Identifies which counties within a specific state are already present in database
Returns: a set of 3-digit county fips strings
flowchart TD
start(("start"))
--> try{try/except}
-- try --> connect[("connect to database")]
--> query["QUERY distinct county_fips for specific state, pad results to 3 characters"]
--> result["convert results to strings"]
--> close_connection
--> return(("result"))
try -- except --> return_empty(("return empty set"))
Creates a backup of the production table
flowchart TD
start(("start"))
--> try{"try / except"}
-- try --> define_var["table name, backup"]
--> ui_back[/"inform user: BACKING UP"/]
--> connect_db[("connect to database")]
--> drop_table["DROP old backup table if it exists"]
--> create_bk["CREATE backup table by cloning production table"]
--> close_connection
--> stop(("stop"))
try -- except --> log_error>"log: BACKUP FAILURE"]
--> stop
Replaces production table with data from backup table
flowchart TD
start(("start"))
--> define["table, backup table"]
--> ui_restore[/"inform user: RESTORING"/]
--> connect_db[("connect to database")]
--> drop_table
--> copy_backup
--> add_constraint["add constraint to table that unique_hash must be unique"]
--> close_connection
--> stop(("stop"))
¶ extract_column_label(label_string)
Used in the ACS5 ETL pipeline to extract the main column category label
Returns: column_label
flowchart TD
start(("start"))
--> if{"label_string exists and is NOT 'Unknown'"}
-- true --> parts["split label_string at the !!"]
--> if_length{"there are more than 2 parts"}
-- true --> return(("return the second part"))
if_length -- false --> return_false(("return 'Unknown'"))
if -- false --> return_false
Parses structural metadata layout tables to dynamically chunk and fetch large Series ID arrays from the BLS API.
Returns:
flowchart TD
start(("start"))
--> setup
--> if_not_series_file{"if not series_file or path doesn't exist"}
-- true
--> log_fetch_fail>"ERROR: fetch failure"]
--> return_false1(("return FALSE"))
if_not_series_file -- false
--> draw_ui_bls["parsing BLS metadata"]
--> try_series_file{try/except series_file} -- try
--> read_metadata
--> all_series_ids
--> if_not_all_series_ids -- true
--> log_no_series_ids>"ERROR: NO valid series ids"]
--> return_false2(("return FALSE"))
if_not_all_series_ids -- false
--> chunk_setup
try_series_file -- except
--> log_read_fail>"ERROR: Metadata read failure"]
--> return_false3(("return FALSE"))
chunk_setup
--> chunk_list[["chunk_list(all_series_ids, chunk_size)"]]
--> output_setup
--> success_true
--> for_chunk_loop["for each chunk"]
-- chunk
--> draw_ui_download[["draw_ui"]]
--> log_fetch_start>"INFO: fetch start"]
--> payload
--> start_time
--> try_download{"try/except"} -- try
--> download_request
--> json
--> if_succeeded{"if status is succeeded"} -- true
--> append_responses
--> calculate_duration
--> log_download>"INFO: fetch success, dataset, chunk, duration"]
--> sleep
if_succeeded -- false
--> log_fetch_fail2>"Fetch failure, dataset, chunk, error message"]
--> if_daily_threshold{"if daily threshold in message"} -- true
--> succes_false
--> break(("return"))
try_download -- except
--> log_error_fetch>"ERROR: fetch failure"]
--> success_false
--> sleep
--> output_path
--> save_raw_data
--> return_success(("return success"))
Processes the data extraction, transformation, and loading processing for the ACS data (since it's different from the other data sets).
Returns: number of rows merged
flowchart TD
start(("start"))
--> success_true
--> for_loop["each subject_table in the list of subject tables"]
-- item_in_list
--> name_data_files
--> if_geo_us{"if geo is National"}
-- true --> api_us[url = ]
--> draw_ui_downloading("display: DOWNLOADING ACS5")
if_geo_us -- false
--> if_geo_state{"if geo is State"}
-- true --> api_state[url]
--> draw_ui_downloading
if_geo_state -- false
--> api_county[url]
--> draw_ui_downloading
--> log_start>LOG: fetch start]
--> start_time
--> try_api{"try/except"}
-- try --> request
--> write_raw_data[/write raw data in chunks/]
--> calucate_duration
--> calcuate_speed
--> log_success>"INFO: fetch success, table, size, speed"]
--> another_table{"another table?"} -- true
--> for_loop
another_table -- false
--> return_success(("return success status"))
try_api -- except
--> log_error>"ERROR: table, error"]
--> success_false
--> another_table
Donwloads standard (census) data.
Returns: number of rows merged into database
flowchart TD
start(("start"))
--> name_data_files
--> base_vars
--> if_geo_us{if National}
-- true
--> api_us[url]
--> draw_ui_download[["draw_ui(DOWNLOADING)"]]
if_geo_us -- false
--> if_geo_state -- true
--> api_state[url]
--> draw_ui_download
if_geo_state -- false
--> api_county[url]
--> draw_ui_download
--> log_start_etl>LOG: fetch start]
--> start_time
--> try_except{try/except}
-- try --> request_url
--> raise_status
--> write_raw_data[/Write raw data/]
--> log_download>LOG: download success]
--> read_raw_data[\read raw data\]
--> write_tsv[force string type, join with tab character, forces NULLS, new line]
--> calculate_duration
--> calculate_speed
--> log_success>"INFO: fetch success, sources, size, speed"]
--> return_true(("return TRUE"))
try_except -- except
--> log_error>"ERROR: fetch failure"]
--> reutnr_false(("return FALSE"))
Transforms raw multi-chunk JSON arrays into relational structures and updates the core database tables.
Returns: numer of rows added to database
flowchart TD
start(("start"))
--> setup
--> check_raw_data_path
--> start_time
--> draw_ui_transform_load
--> try_except{try/except} -- try
--> read_data
--> outpu_tsv
--> connect_db
--> copy_tmp
--> count_rows
--> insert_master
--> merge_count
--> close_connection
--> calculate_duration
--> log_load>"INFO: load complete: bls-table, merge count, copy count, duration"]
--> return_merge_count(("return merge_count"))
try_except -- except
--> log_error>"ERROR: pipline load failure"]
--> return0(("return 0"))
Returns:
flowchart TD
start(("start"))
Returns:
flowchart TD
start(("start"))
Displays a confirmation window
Returns: True if 'y' or 'Y' is pressed
flowchart TD
start(("start"))
--> get_screen_size
--> create_box
--> display_message
--> capture_character
--> if_yY{If chacter is 'y' or 'Y'}
-- true --> return_true(("Return TRUE"))
Orchestrates user interface actions
Returns: news synced states and counties
flowchart TD
start(("start"))
--> src[use selections to retrive source details]
--> geo
--> set_total_records_to_zero
--> if_selections_action_zero{download data}
-- true
--> if_selected_counties_exist{if specific counties are selected}
-- true
--> queue_list_selected_counties
--> start_time
if_selected_counties_exist -- false
--> if_selected_states_exists{if specific states are selected} -- true
--> queue_list_all_counties_in_selected_states
--> start_time
if_selected_states_exists -- false
--> queue_list_states_selected
--> start_time
--> log_session_start>LOG: new session started]
--> for_loop
-- item in queue
--> try_except_abbr{try/except} -- try
--> abbr_state[look up state abbr from fips code]
--> draw_ui_sync(display: SYNCING i/length of queue)
try_except_abbr -- except
--> abbr_us
--> draw_ui_sync
--> if_acs5{if source is ACS5} -- true
--> acs5_pipeline[[acs_etl_pipeline, returns number of merged records]]
--> if_count{if count} -- true
--> total_new_records[add number of records added to the total_new_records for session]
--> backup_1[[backup]]
--> log_session_complete>LOG: Session complete]
--> log_new_records_added>LOG: number of new records added]
--> summary_popup[[show_summary_popup]]
--> new_synced_states[[get_synced_states]]
if_acs5 -- false
--> std_pipeline[[run_etl_pipeline, returns number of merged records]]
--> if_count
if_count -- false --> log_session_complete
if_selections_action_zero -- false
--> if_selections_action_one{backup database} -- true
--> backup_run[[backup]]
--> new_synced_states
if_selections_action_one -- false
--> if_selections_action_two{restore from backup} -- true
--> restore[[restore]]
--> new_synced_states
--> new_synced_counties[[get_synced_counties]]
--> clear_work_queues
--> return((return new_synced_states, new_synced_counties))
¶ main(stdscr)
Primary control loop initialized by curses.wrapper. Manages keyboard input, zone-based focus, and internal state.
flowchart TD
start(("start"))
--> if_state_info_empty{if state_info is empty}
-- true --> display_error(display error to user)
--> wait_getch[\wait for user to enter any key/]
--> exit((exit main function, end program run))
if_state_info_empty -- false
--> setup["set up colors, initial selections, scrolling, etc"]
--> while_loop
-- true
--> active_metadata["for current source and geography, update states present in database"]
--> update_counties["update county list, if applicable"]
--> draw_interface[["draw_interface(...)"]]
--> key[capture input key]
--> if_q{input is q}
-- true --> stop(("stop"))
if_q -- false
--> if_tab{input is tab}
-- true --> change_focus["cycle through the focus zones"]
--> horizontal_nav{{"if in the top bars (data source or georaphic scope)"}}
if_tab -- false --> horizontal_nav
-- true --> left_right_keys
--> if_space{"space for selection, different for state/county zones"}
horizontal_nav -- false --> up_down_keys
--> if_space
-- true --> space_actions --> while_loop
if_space -- else
--> if_cC{"C/c to select all counties"}
-- true --> c_actions --> while_loop
if_cC -- else
--> if_enter{"enter"}
-- true --> run_action[["run_action(...)"]]
--> redraw[["draw_interface(...)"]]
--> while_loop
Data Validations
All raw data files are saved in the ./raw_data directory.
| Database |
Number of files |
Size |
acs5 |
7801 |
1.4 GB |
cbp |
104 |
1.1 GB |
ecnbasic |
104 |
336 MB |
nes |
104 |
177 MB |
| TOTAL |
|
2.8 GB |