Skip to content

CDCWonderNatality : Automation - #2233

Merged
kartik-s21 merged 77 commits into
datacommonsorg:masterfrom
kartik-s21:cdcwondernatality-automation
Oct 23, 2026
Merged

kartik-s21 merged 77 commits into
datacommonsorg:masterfrom
kartik-s21:cdcwondernatality-automation

Conversation

@kartik-s21

@kartik-s21 kartik-s21 commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

Summary

This PR implements automated pipeline support for CDC WONDER Natality data across Country, State, and County levels, addressing all code review findings.

Key Changes

  • download.py:
    • Automates live scraping of CDC WONDER natality exports (2016–2024) via headless Chrome (--headless=new, --no-sandbox).
    • Queries both un-sliced totals (B_3 = *None*) and all 6 active 2016+ demographic Group-By dimensions (Age of Mother 13 = D149.V39, Mother's Single Race 15 = D149.V49, Mother's Education = D149.V5, Mother's Hispanic Origin = D149.V43, Marital Status = D149.V27, and Mother's Nativity = D149.V48) for State and County levels (selecting post-2022 years on demographic slices to stay below CDC WONDER's 75,000-row export limit).
    • Fails fast with RuntimeError if the year selection listbox (V_D149.V20) or any optional measure checkbox (_OPTIONAL_MEASURES) is missing from the CDC WONDER form, and uses exponential backoff (5 * (2 ** (attempt - 1)), guarded by if attempt < max_retries) across retry attempts.
    • Dynamically discovers and retrieves the immutable historical baseline (1995–2022) from GCS (gs://unresolved_mcf/cdc/wonder/natality/) with demographic breakdowns to ensure zero historical data loss.
  • process.py:
    • Consolidates historical brackets with live exports using chronological (start_year, end_year) sorting and keep='last' precedence for overlapping years.
    • Maps all 6 demographic code columns (_DEMOGRAPHIC_CODE_MAP) and validates all 9 expected _MEASURE_MAP columns exist in the TSV header.
    • Normalizes units to canonical Data Commons schema: Years -> Year, Weeks -> Week, Grams -> Gram, Months -> Month.
    • Null-safely filters out unmapped/invalid place DCIDs (geoId/99, geoId/00, geoId/99999, geoId/00000, geoId/*999).
    • Filters out 0.0 placeholder rows on unknown/unstated "Not Available" slices (_MotherMaritalStatusUnknownOrNotStated and _MotherRaceUnknownOrNotStated for Year >= 2016) and computes country.csv directly via aggregate_state_to_country(final_state_df).
  • manifest.json:
    • Registers table mappings for country, state, and county, "validation_config_file": "validation_config.json", annual schedule ("0 0 1 6 *"), and container resource_limits (cpu: 2, memory: 8).
  • validation_config.json:
    • Configures check_latest_date_freshness (lag_years <= 3), check_scoped_date_consistency_active_statvars (distinct_dates = 1 AND active_statvar_count IN (1, 9, 46, 401) across all MaxDate >= 2022 StatVars), and check_deleted_records_percent (threshold: 2.5).
  • process_test.py & download_test.py:
    • Comprehensive unit test suite (19/19 passing) verifying demographic slice queries and parsing, missing measure/column error handling, retry backoff guard, zero-placeholder filtering, chronological bracket sorting, unit normalization, invalid/null geo filtering, integer quantity formatting, and validation configuration.

Test Environment Verification (2026_10_21T23_41_40_528726_07_00)

  • Batch Job: cdcwondernatality-samnotra-20261022-061014 (ImportStatus.STAGING)
  • Country (input0): 1,425 rows (46 active 2016+ StatVars at MaxDate = 2024, 0 at 2022), 1.04% deleted records (14 fabricated 0 rows removed), 0 missing refs, 0 lint errors, all 6 validation checks PASSED.
  • State (input1): 363,912 rows (401 active 2016+ StatVars at MaxDate = 2024, 0 at 2022), 1.93% deleted records (6,426 0.0 placeholder rows removed), 0 missing refs, 0 lint errors, all 6 validation checks PASSED.
  • County (input2): 3,316,862 rows (401 active 2016+ StatVars at MaxDate = 2024, 0 at 2022), 2.36% deleted records (72,828 0.0 placeholder rows removed), 0 missing refs, 0 lint errors, all 6 validation checks PASSED.

Artifacts (2026_10_21T23_41_40_528726_07_00):

Buganizer: b/555057010

…nd pipeline scripts

- Implement automated download script download.sh from GCS repository
- Implement unified process.py pipeline for country, state, and county tables
- Add unit tests in process_test.py and ensure deterministic sorting in preprocess.py
- Add manifest.json for Cloud Run/Batch automation (annual cadence, 3 import inputs, no node_mcf)
- Add validation_config.json with empty import, lint error, and missing refs checks
- Update README.md with comprehensive documentation and usage instructions
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Warning

Gemini encountered an error creating the review. You can try again by commenting /gemini review.

@kartik-s21

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces an automated end-to-end data processing pipeline for CDC Wonder Natality data, including new scripts for downloading, processing, and testing, along with a manifest and validation configuration. The review feedback highlights several improvement opportunities: making the geographic classification logic in process.py more robust, casting aggregated count statistics to integers (Int64) to prevent float representation, correcting a fallback test data copy that mixes state and county resolutions, ensuring output verification checks for non-empty files, and replacing the file command in download.sh with a more portable unzip -t check.

Comment thread scripts/us_cdc/natality/process.py Outdated
Comment thread scripts/us_cdc/natality/process.py Outdated
Comment thread scripts/us_cdc/natality/process.py Outdated
Comment thread scripts/us_cdc/natality/process.py
Comment thread scripts/us_cdc/natality/download.sh Outdated

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces automation scripts, configurations, and tests for the CDC Wonder Natality import, including an end-to-end processing pipeline (process.py) and a download script (download.sh). Feedback on the new pipeline highlights critical issues in process.py: the geographic classification logic for sample_geo contains flaws regarding DCID lengths and empty values; converting the Quantity column to numeric can result in float representations instead of integers; and copying state-level data to county-level output as a fallback introduces mismatched geographic associations.

Comment thread scripts/us_cdc/natality/process.py Outdated
Comment thread scripts/us_cdc/natality/process.py Outdated
Comment thread scripts/us_cdc/natality/process.py Outdated
- Use distinct output TMCF paths (country.tmcf, state.tmcf, county.tmcf) in manifest.json to prevent Cloud Batch GCS basename collision.
- Remove dangling validation_config_file from manifest.json and README.md.
- Ensure chronological sorting and filename-based routing in process.py.
- Use regex Geo validation (geoId/XX vs geoId/XXXXX) and cast aggregated quantities to Int64.
- Tighten error handling in download.sh: require gcloud, remove silent failure suppression, and verify all geographic resolution levels.
- Track Buganizer issue b/555054352.
- Fix off-by-one check in --use_test_data county DCID synthesis using regex
- Ensure Int64 cast and keep='last' in aggregate.py and process.py
- Hardened download.sh to clean input dir and select latest dated version folder
- Restore validation_config.json with SQL_VALIDATOR date freshness check
- Add multi-bracket sorting, deduplication, and DCID assertions in process_test.py
- Replace RuntimeError with logging.fatal in process.py
- Update README.md with manual preprocessing/staging workflow and clean dependencies
- Track Buganizer issue b/555057010.
- Fix yapf code formatting across scripts/us_cdc/natality to pass CI checks
- Remove .astype(str) on nullable Int64 in process.py to prevent <NA> serialization
- Handle pipefail with || true in download.sh and explicitly check dated snapshot dirs
- Add 1995-2002 chronological bracket overlap test in process_test.py
- Remove redundant sys.exit(1) calls after logging.fatal() in process.py
- Track Buganizer issue b/555057010.
- Vectorize numeric parsing and integer formatting on non-null rows
- Avoid setting Int64 dtype directly into string[pyarrow] columns
- Add assertions in process_test.py ensuring no <NA> strings or float notations
- b/555057010
@kartik-s21

Copy link
Copy Markdown
Contributor Author

All 5 findings have been resolved in CRA :

  1. [P2] Code formatting: Formatted all Python files using yapf -r -i -p --style=google scripts/us_cdc/natality. yapf -r --diff passes with 0 violations.
  2. [P3] Data serialization in process.py: Refactored Quantity normalization to vectorize non-null numeric parsing (valid_mask = count_mask & nums.notna()) and format integers as clean strings. This avoids assigning Int64 directly into Arrow string columns (which caused a TypeError in Python 3.12 / pandas 2.2), guarantees no <NA> literals, and ensures zero .0 float notations.
  3. [P3] Pipefail and fallback in download.sh: Appended || true to the gcloud storage ls ... | grep pipeline command substitutions and replaced the unsafe */ wildcard fallback with an explicit check that outputs a clear diagnostic error and exits 1 if any snapshot folder is empty.
  4. [P3] Bracket overlap test coverage in process_test.py: Added an overlapping record for Year 2002 between state_cleaned_95-02.csv (50000) and state_cleaned_03-06.csv (52000), verifying that 95-02 sorts before 03-06 chronologically (1995 < 2003) rather than alphabetically.
  5. [P3] Redundant sys.exit(1): Removed unreachable sys.exit(1) statements after logging.fatal().

Additionally, the Cloud Batch automation job was re-run and succeeded end-to-end:

  • Version: 2026_09_27T13_32_02_840220_07_00
  • Rows Processed: 3,403,356 (Country: 1,347, State: 332,447, County: 3,069,562)
  • Validation: Passed (True)
  • Corresponding CL 986267877 description updated.

@SandeepTuniki SandeepTuniki left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the changes! I left a few comments and a clarifying question below.

Comment thread scripts/us_cdc/natality/download.sh Outdated
Comment thread scripts/us_cdc/natality/aggregate.py Outdated
Comment thread scripts/us_cdc/natality/validation_config.json Outdated
Comment thread scripts/us_cdc/natality/process.py Outdated
Comment thread scripts/us_cdc/natality/process.py Outdated
…WONDER

- Add download.py with headless Chrome to scrape live CDC WONDER exports and retrieve historical baseline from GCS.
- Refactor process.py to merge historical 1995-2022 baseline with live 2016-2024 data, preventing historical data loss.
- Normalize gestational age, weight, and interval units to match Data Commons schema (Week, Gram, Month).
- Clean up redundant legacy scripts, zip files, and bracket JSON configs.
- Update manifest.json, validation_config.json, README.md, and unit test suite.
- Resolves b/555057010.
- Fix silent data loss on TSV rows with notes/footnotes by retaining header and columns directly.
- Sort historical brackets chronologically by year range and remove unused _merge_csv_files.
- Terminate download pipeline via logging.fatal on baseline failure to prevent historical data loss.
- Always aggregate national totals from final_state_df to ensure multi-decade coverage in country.csv.
- Fix canonical unit mapping from Years to Year for Average Mother's Age.
- Filter invalid place DCIDs (geoId/99, geoId/99999).
- Update validation_config.json with lag_years <= 2 and scoped consistency rule across 9 active StatVars.
- Normalize Count_* quantities to clean integer strings across all geographic levels in memory.
- Increase download timeout to 180s and robustly filter candidate download files.
- Add manifest.json to source_files and document prerequisites in README.md.
- Expand unit test suite in process_test.py and download_test.py.
@kartik-s21

Copy link
Copy Markdown
Contributor Author

Thanks for the changes! I left a few comments and a clarifying question below.

Hi @SandeepTuniki,

All findings and review comments have been addressed in the latest commits:

  1. Automated Live Scraping & Baseline Ingestion:

    • Replaced download.sh with download.py using headless Chrome to automate live CDC WONDER downloads (2016–2024).
    • Historical baseline (1995–2022) with full demographic breakdowns is automatically retrieved from GCS, and merged in memory with chronological precedence.
    • Removed legacy offline scripts (clean_cdc_data.py, preprocess.py, aggregate.py).
  2. Missing Count Aggregation Fix:

    • aggregate_state_to_country() explicitly drops NaN rows post-coercion (dropna(subset=['Quantity'])), preventing fabricated 0 values for unobserved groups.
  3. Dynamic Freshness & Scoped Consistency Validation:

    • Updated check_latest_date_freshness to dynamically assert lag_years <= 2.
    • Added check_scoped_date_consistency_active_statvars to enforce uniform observation dates across all active maternal/infant health StatVars (distinct_dates = 1).
  4. Deterministic Chronological Sorting:

    • _extract_year_bracket() returns (start_year, end_year) tuples, ensuring brackets with identical start years (e.g. 16-20 vs 16-22) sort deterministically.
  5. TMCF Generation & Verification:

    • write_tmcf_files() writes all template MCF files directly, and required_outputs checks both .csv and .tmcf files for existence and non-zero file size.
  6. Schema Alignment & Place Cleaning:

    • Normalized units to canonical Data Commons schema (Year, Week, Gram, Month).
    • Filtered unmapped/invalid DCIDs (geoId/99, geoId/00, geoId/99999, etc.).

Verification

  • Unit Tests: All 15 unit tests pass (pytest scripts/us_cdc/natality/).
  • Cloud Batch Job: Verified end-to-end in test environment (cdcwondernatality-samnotra-20261012-043633, Version 2026_10_11T21_39_34_467304_07_00):
    • Country (input0): 1,349 rows, 0% deleted records, 0 lint errors (PASSED)
    • State (input1): 333,365 rows, 0% deleted records, 0 lint errors (PASSED)
    • County (input2): 3,079,966 rows, 0% deleted records, 0 lint errors (PASSED)
    • All validation rules PASSED.

Ready for re-review!

- download.py: Raise RuntimeError on missing year listbox; add exponential backoff in retry loop.
- manifest.json: Add explicit container resource limits (2 CPU, 8 GB RAM).
- process.py: Make county Geo filtering null-safe.
- validation_config.json: Bound latest date freshness query with MAX, IS NOT NULL, and lag between 0 and 3 years.
- process_test.py: Add unit tests verifying freshness validation fails on missing StatVar and future dates.

@SandeepTuniki SandeepTuniki left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for making the updates—the year-bracket tuple sorting (_extract_year_bracket) and .tmcf output generation/verification look good!

However, after checking the latest commits and Cloud Batch run (2026_10_12T01_56_58_821447_07_00), a few critical items are still missing or were reverted in the final commits (refreshing the demographic StatVars, restoring validation_config.json in manifest.json along with the date freshness/consistency checks, and avoiding the fabricated 0 counts from the historical country_cleaned_*.csv files).

Could you please review and address the inline comments? Thanks!

Comment thread scripts/us_cdc/natality/download.py
Comment thread scripts/us_cdc/natality/download.py Outdated
Comment thread scripts/us_cdc/natality/download.py Outdated
Comment thread scripts/us_cdc/natality/manifest.json
Comment thread scripts/us_cdc/natality/process.py Outdated
Comment thread scripts/us_cdc/natality/validation_config.json
…on config & SQL rules, filter zero placeholders
@kartik-s21

kartik-s21 commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor Author

Hi @SandeepTuniki,

Thank you for the thorough review! All 6 inline comments have been addressed in commit 34eabee3 and verified in the latest Cloud Batch test run (2026_10_21T23_41_40_528726_07_00, Job ID cdcwondernatality-samnotra-20261022-061014):

1. Summary of Fixes

  1. [P1] Refreshed All 6 Demographic Breakdowns (B_3) to 2024 (download.py, process.py):
    • Expanded download.py (_DEMOGRAPHIC_SLICES) to query both un-sliced totals (B_3 = *None*) and all 6 active 2016+ demographic Group-By dimensions (Age of Mother 13 = D149.V39, Mother's Single Race 15 = D149.V49, Mother's Education = D149.V5, Mother's Hispanic Origin = D149.V43, Marital Status = D149.V27, Mother's Nativity = D149.V48) for State and County (querying post-2022 years on demographic slices to stay well under CDC WONDER's 75,000-row export limit).
    • Added _DEMOGRAPHIC_CODE_MAP in process.py to map all demographic slices to their canonical StatVars.
    • Across country.csv (46 active 2016+ StatVars), state.csv (401 active 2016+ StatVars), and county.csv (401 active 2016+ StatVars), 100% of active StatVars now reach MaxDate = 2024 and 0 StatVars remain stuck at 2022.
  2. [P1] Restored validation_config_file in manifest.json:
    • Restored "validation_config_file": "validation_config.json" in scripts/us_cdc/natality/manifest.json.
  3. [P1] Restored check_latest_date_freshness and check_scoped_date_consistency_active_statvars in validation_config.json:
    • Restored both SQL_VALIDATOR rules and expanded check_scoped_date_consistency_active_statvars (WHERE CAST(MaxDate AS INTEGER) >= 2022 ... with distinct_dates = 1 AND active_statvar_count IN (1, 9, 46, 401)) to enforce date consistency across all 46 Country and 401 State/County active StatVars.
  4. [P2] Removed Fabricated 0 National Birth Counts & 0.0 Unknown/Unstated Placeholders (process.py):
    • Stopped loading country_cleaned_*.csv (pre_country_csvs) so country.csv is computed directly via aggregate_state_to_country(final_state_df).
    • Added _filter_zero_unknown_placeholders() to strip 0.0 placeholder rows on _MotherMaritalStatusUnknownOrNotStated and _MotherRaceUnknownOrNotStated (Year >= 2016). Removing these 18 all-zero placeholder StatVars accounts for 419 - 18 = 401 active State/County StatVars (46 Country StatVars) and intentionally deletes 14 Country rows (1.04%), 6,426 State rows (1.93%), and 72,828 County rows (2.36%), all within the 2.5% deletion threshold.
  5. [P2] Fail Fast on Missing Optional Measure Checkboxes & TSV Columns (download.py, process.py):
    • download_natality_dataset() now raises RuntimeError if any _OPTIONAL_MEASURES checkbox is missing/unselectable, and parse_wonder_tsv() raises ValueError if any expected _MEASURE_MAP column is absent from the TSV header.
  6. [P3] Guarded Retry Backoff Sleep (download.py):
    • Guarded time.sleep(backoff_delay) with if attempt < max_retries:.

2. Cloud Batch Verification (2026_10_21T23_41_40_528726_07_00)

  • Job ID: cdcwondernatality-samnotra-20261022-061014 (ImportStatus.STAGING)
  • Total Rows Processed: 3,682,199
    • Country (input0): 1,425 rows (+90 added, -14 fabricated 0 rows removed, 0 modified), all 6 validation checks PASSED.
    • State (input1): 363,912 rows (+36,973 added, -6,426 0.0 placeholder rows removed, 0 modified), all 6 validation checks PASSED.
    • County (input2): 3,316,862 rows (+309,724 added, -72,828 0.0 placeholder rows removed, 3 modified), all 6 validation checks PASSED.
  • Artifacts:

3. Production CNS (20231215) vs. Automated GCS Differ & Deletion Analysis

We also ran tools/import_differ/import_differ.py directly against the production CNS MCF baseline (/cns/jv-d/home/datcom/v3_mcf/cdc/wonder/natality/{country,states,county}/20231215/nodes.mcf-00000-of-00001) and verified that 0 genuine observations are lost (all reported CNS-vs-GCS deletions are caused by unit canonicalization from literal strings "Gram"/"Years" to dcid:Gram/dcid:Year and deduplication of repeated rows in the legacy 2023 CNS MCFs):

@SandeepTuniki SandeepTuniki left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for addressing all the review comments. LGTM!

@kartik-s21
kartik-s21 merged commit ccfabc4 into datacommonsorg:master Oct 23, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants