Fix epacriteriaairpollutants - #2206
shourya116 wants to merge 14 commits into
Conversation
- Pre-declare 47 unindexed EPA Air Quality monitoring stations in EPA_AirQuality.mcf - Standardize units and add retry logic in air_quality.py - Add node_mcf, validation_config_file, and resource limits to manifest.json - Add validation_config.json with strict zero lint error threshold
…ality.mcf to eliminate remote DC API calls
|
Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). View this failed invocation of the CLA check for more information. For the most up to date status, view the checks section at the bottom of the pull request. |
There was a problem hiding this comment.
Code Review
This pull request introduces a diagnostic runbook, configures validation rules, and updates the air_quality.py script to handle unit mapping, filter out cross-border monitors, and use a robust HTTP session with retries for downloading data. Feedback on the changes includes addressing a critical runtime AttributeError when configuring retries, relaxing the lint error threshold in validation_config.json to prevent pipeline failures from new stations, and resolving a potential KeyError and eager evaluation issue when parsing observation units.
…and clean up validation config
…s-border monitor filtering
…ource_files, add date validation rules, and add unit tests
…force strict zero validation thresholds - Restore all 8,408 EPA monitoring station declarations in EPA_AirQuality.mcf to eliminate remote DC API calls, missing observationAbout errors, and location reference warnings - Restore node_mcf in manifest.json and keep differ disabled - Set strict zero threshold for check_missing_refs_count and check_lint_error_count in validation_config.json - Format air_quality_test.py with yapf
abhishekjaisw
left a comment
There was a problem hiding this comment.
Review scope
- Target: PR #2206 (
7cdda42598c5cce772ee173325c655b016f6d083) - Reviewed:
EPA_AirQuality.mcf,air_quality.py,air_quality_test.py,manifest.json,test_data/test_import.csv,validation_config.json - Skipped: None
Summary of Progression
Thank you for updating the PR description with the detailed incident summaries and staging verification (epacriteriaairpollutants-shouryasingh-20260914-075748 -> gs://datcom-import-test/scripts/us_epa/airdata/EPACriteriaAirPollutants/2026_09_14T01_01_32_581690_07_00, achieving ImportStatus.STAGING with 0 missing refs and 0 lint errors), and for adding unit tests (7/7 passing).
Three remaining schema/automation items need attention before merging (see inline comments):
- [P1] Remove duplicate canonical StatVars & Units from
EPA_AirQuality.mcf(lines 1–158): Inspectinginput0/genmcf/instance_mcf_nodes.mcffrom the Sep 14 run confirms thatgenmcfingests all 18 canonical StatVars and 2 units intoinstance_mcf_nodes.mcf(lines 97460 and 114284). Becausegenmcf's built-in schema cache already resolves canonical Google3 StatVars (manual_epa_stat_vars.mcf) and units (unit.mcf) locally withoutnode_mcf, lines 1–158 should be removed so only the 21MeasurementMethodEnumnodes remain inEPA_AirQuality.mcf. - [P1] Dynamically generate
AirQualitySitenodes (EPA_AirQuality_sites.mcf) instead of hardcoding 8,404 stations in Git: Pre-populatingAirQualitySitenodes vianode_mcfis necessary sogenmcfpopulateslocal_nodesbefore validatingtable_mcfrows—preventing remote DC API lookups and achieving0missing refs and0lint errors. However, hardcoding 8,404 static stations in Git paired withthreshold: 0means future automated cron refreshes (15 22 10 1,7 *) will fail whenever EPA adds a single new monitoring station. Havingair_quality.pydynamically write uniqueAirQualitySitenodes toEPA_AirQuality_sites.mcfat runtime and setting"node_mcf": "EPA_AirQuality*.mcf"inmanifest.jsonpreservesthreshold: 0automatically on every future refresh without 50,000+ lines of static Git bloat. - [P2] Use dynamic year expression in
check_date_freshness: Replace static'2024'withCAST(SUBSTR(latest_date, 1, 4) AS INT) >= YEAR(CURRENT_DATE) - 1.
Positive findings
scripts/us_epa/airdata/air_quality.py:221-236- Resilient HTTP Session with Exponential Backoff and Retries ✓- Finding: Good - Configuring
requests.Sessionwithurllib3.util.Retry(10 retries, backoff factor 2, status forcelist[429, 500, 502, 503, 504]) and 120s timeout provides strong fault tolerance across 270+ annual archive downloads.
- Finding: Good - Configuring
scripts/us_epa/airdata/air_quality.py:138-140&air_quality_test.py:63-151- Foreign Monitor Filtering and Unit Mapping with Unit Tests ✓- Finding: Good - Filtering out cross-border monitors (
State Codein('80', 'CC')) and normalizing unit strings (UNIT_MAP) cleanly resolves 875K fatalobservationAbouterrors, verified by unit tests (7/7passing).
- Finding: Good - Filtering out cross-border monitors (
scripts/us_epa/airdata/manifest.json:25-33- Appropriate Resource Sizing, Differ Bypass, and PR Documentation ✓- Finding: Good - Allocating 32 vCPUs / 128 GiB RAM / 512 GB disk, setting
invoke_differ_tool: false, and documenting the RCA and staging proof in the PR description prevents the 121.83 GB MCF in-memory diffing OOM kernel freeze.
- Finding: Good - Allocating 32 vCPUs / 128 GiB RAM / 512 GB disk, setting
Coverage
| File | Status | Result |
|---|---|---|
| scripts/us_epa/airdata/EPA_AirQuality.mcf | Reviewed | Two P1 findings |
| scripts/us_epa/airdata/manifest.json | Reviewed | Two P1 findings |
| scripts/us_epa/airdata/validation_config.json | Reviewed | One P1 finding, One P2 finding |
| scripts/us_epa/airdata/air_quality.py | Reviewed | Positive findings |
| scripts/us_epa/airdata/air_quality_test.py | Reviewed | Positive findings |
| scripts/us_epa/airdata/test_data/test_import.csv | Reviewed | No findings |
Verification and limitations
- Checks run: Executed all 7 unit tests in
scripts/us_epa/airdata(7/7 OK) and verifiedyapfformatting (0 diffs) in detached worktree at7cdda42598c5cce772ee173325c655b016f6d083; audited GCS staging artifacts (instance_mcf_nodes.mcf,report.json,import_summary.json) for2026_09_10T13_01_42_117377_07_00and2026_09_14T01_01_32_581690_07_00; verified Google3 schema declarations via Code Search. - Checks not run: Live download of 1980–2025 EPA AQS archives (~3.7h runtime).
- Limitations: Note that inline comments for
EPA_AirQuality.mcfare attached tomanifest.jsonandvalidation_config.jsonbecauseEPA_AirQuality.mcf(+50,525 lines) exceeds GitHub's diff size limit for inline comments.
…e duplicate schema, and use dynamic date freshness - Remove duplicate canonical StatVars and units from EPA_AirQuality.mcf, retaining only 21 MeasurementMethodEnum nodes - Remove 8,404 hardcoded station nodes from EPA_AirQuality.mcf - Dynamically generate unique AirQualitySite nodes to EPA_AirQuality_sites.mcf in air_quality.py - Use wildcard pattern 'EPA_AirQuality*.mcf' for node_mcf in manifest.json - Use dynamic DuckDB date expression in validation_config.json check_date_freshness - Update unit tests in air_quality_test.py to verify dynamic site MCF generation, uniqueness, and filtering
There was a problem hiding this comment.
All three review findings have been resolved and verified with a full Cloud Batch staging run on datcom-infosys-dev.
Summary of Changes (Commit 573d49a8 on shourya116:fix-epacriteriaairpollutants):
- [P1] Removed Duplicate Canonical StatVars & Units: Stripped lines 1–158 from
EPA_AirQuality.mcf, retaining only the 21MeasurementMethodEnumnodes. Canonical StatVars and units now resolve purely throughgenmcf's schema cache without duplicate instance node ingestion. - [P1] Dynamically Generated Station MCF (
EPA_AirQuality_sites.mcf): Updatedair_quality.py(write_csv) to dynamically write uniqueAirQualitySitenodes at runtime, removed the 8,404 hardcoded station nodes (lines 246–50671) fromEPA_AirQuality.mcf(-1.37 MB), and updatedmanifest.jsonto"node_mcf": "EPA_AirQuality*.mcf". Future cron runs will automatically capture any new upstream EPA stations with zero manual maintenance. - [P2] Dynamic Date Freshness: Updated
check_date_freshnessinvalidation_config.jsontoCAST(SUBSTR(latest_date, 1, 4) AS INT) >= YEAR(CURRENT_DATE) - 1. - Unit Tests: Updated
air_quality_test.pyto verify dynamic site MCF emission, uniqueness, and cross-border monitor filtering (7/7 OK).
Staging Verification
- Cloud Batch Job:
epacriteriaairpollutants-shouryasingh-20260916-091708(UID:epacriteriaairpoll-9beed9ad-4c41-487c0,datcom-infosys-dev,us-west4) - GCS Staging Version:
gs://datcom-import-test/scripts/us_epa/airdata/EPACriteriaAirPollutants/2026_09_16T02_19_58_109207_07_00 - Status:
ImportStatus.STAGING(Exit Code 0, 64,802,212 rows processed, 305,269,418 MCF nodes generated in 3.88 hours). - Double-Ingestion Check: Audited
input0/genmcf/instance_mcf_nodes.mcf: 0 StatVars, 0 Units (only 8,404AirQualitySite, 8,337GeoCoordinates, and 21MeasurementMethodEnum). - Validation Output (
validation_output.csv):check_empty_import: PASSED (305,269,418 nodes, 64,802,212 rows)check_missing_refs_count: PASSED (0 missing refs,threshold: 0)check_lint_error_count: PASSED (0 lint errors,threshold: 0)check_max_date_consistent: PASSED (18/18 StatVars consistent atMaxDate = 2025-12-31)check_date_freshness: PASSED (CAST(SUBSTR(latest_date, 1, 4) AS INT) >= YEAR(CURRENT_DATE) - 1)
Summary
Resolves execution failures and structural automation issues for the
EPACriteriaAirPollutantsimport pipeline across 1980–2025 historical data (64.8M rows, 305.2M nodes, 123.27 GB MCF):ImportStatus.VALIDATIONdue to 18.1M missing measurement method warnings (NO2_1hour_2010), ~875K fatalobservationAboutreference errors from unregistered cross-border monitors (epa/800060001), and differ summaryDATA_ERROR.mcf_file_util.load_mcf_nodes) attempting to load 121+ GB of MCF text into Python dictionaries on a 128 GiB RAM machine.573d49a8):EPA_AirQuality.mcfto eliminate duplicate instance node ingestion.EPA_AirQuality_sites.mcf) and wildcard"node_mcf": "EPA_AirQuality*.mcf", ensuring future automated refreshes never break againstthreshold: 0when upstream EPA commissions new monitors.'2024'date cutoff with dynamic DuckDB date expression.Key Changes
1. Data Extraction & Dynamic Site Generation (
scripts/us_epa/airdata/air_quality.py)'80'for Mexico and'CC'for Canada) that lack Data Commons place entities, eliminating ~875K fatal lint errors.write_csvto dynamically emit uniqueAirQualitySitenodes toEPA_AirQuality_sites.mcfat runtime. Addedcreate_sites_mcfhelper and preserved cross-call uniqueness across all 270+ annual archive extractions.UNIT_MAP(MicrogramsPerCubicMeter_lc,MicrogramsPerCubicMeter_25C,PartsPerMillion,PartsPerBillion) with camel-case fallback.requests.Session()withurllib3.util.Retry(10 retries, backoff factor 2, status codes[429, 500, 502, 503, 504], and 120s timeout) to prevent dropped connections during archive downloads.2. Schema Declarations & De-duplication (
scripts/us_epa/airdata/EPA_AirQuality.mcf)MeasurementMethodEnumnodes (Ozone_8hour_2015,NO2_1hour_2010,SO2_1hour_2010,PM25_24hour_2012,PM10_24hour_2006, etc.) that do not exist in Google3 schema.manual_epa_stat_vars.mcfandunit.mcf), avoiding double-ingestion intoinstance_mcf_nodes.mcf.3. Automation Configuration & Differ Guardrail (
scripts/us_epa/airdata/manifest.json)"config_override": {"invoke_differ_tool": false}to safely bypass the in-memory differ on the 123+ GB MCF output and prevent VM memory exhaustion."node_mcf": "EPA_AirQuality*.mcf"underimport_inputssogenmcfloads bothEPA_AirQuality.mcf(methods) and dynamically generatedEPA_AirQuality_sites.mcf(sites) intolocal_nodes.cpu: 32,memory: 128,disk: 512.4. Import Validation Configuration (
scripts/us_epa/airdata/validation_config.json)check_deleted_records_percentto preventDATA_ERRORwhen the differ tool is bypassed.threshold: 0forcheck_missing_refs_countandcheck_lint_error_count.check_max_date_consistent(MAX_DATE_CONSISTENT): Verifies uniformMaxDateacross all 18 air quality StatVars.check_date_freshness(SQL_VALIDATOR): Enforces dynamic 1-year publication lag freshness using DuckDB:5. Unit Tests (
scripts/us_epa/airdata/air_quality_test.py,README.md)7/7 OK).EPA_AirQuality_sites.mcfinREADME.md.Verification and Test Run Proof
Cloud Batch Staging Execution
573d49a867981d146f2371b5d0e2d0f992a4bb38(shourya116:fix-epacriteriaairpollutants)epacriteriaairpoll-9beed9ad-4c41-487c0, Project:datcom-infosys-dev, Region:us-west4)gs://datcom-import-test/scripts/us_epa/airdata/EPACriteriaAirPollutants/2026_09_16T02_19_58_109207_07_00Exit Code: 0)ImportStatus.STAGING.Validation Report (
input0/validation/validation_output.csv)check_empty_importcheck_missing_refs_countthreshold: 0)check_lint_error_countthreshold: 0)check_max_date_consistent2025-12-31across all 18 StatVarscheck_date_freshnesslatest_date = 2025-12-31 >= 2025)Artifact & De-duplication Audit
input0/genmcf/instance_mcf_nodes.mcf: confirmed 0 canonicalStatisticalVariablenodes and 0 canonicalUnitOfMeasurenodes (contains only 8,404AirQualitySite, 8,337GeoCoordinates, and 21MeasurementMethodEnuminstances).python3 -m unittest discover -v -s scripts/us_epa/airdata -p "*_test.py": 7/7 tests passed.References