Singapore (SG) build pipeline
Builds from a staged Universal-Schema (FedEx-pipeline) export, not a live fetch — the
raw postal source was already scraped once and staged as
sources/singapore_fedex_base.csv (121,513 rows, one per postal code, building-level,
0 duplicates). The original scraper still exists and is maintained separately at
~/Desktop/Research/fedex-agents-prototype/countries/SGP/scripts/scrape_postal.py if
the base ever needs re-fetching from scratch — not duplicated here, this pipeline picks
up from the staged export.
Column mapping (Universal Schema -> 13-column postal-codes schema):
| Source | Target | Note |
|---|---|---|
countryCode | country_code | SG, not SGP — this repo's convention is alpha-2 |
postalCode | postal_code | |
additionalInfo | place_name | BUILDING name if present, else ROAD_NAME, else subzone as last resort |
stateName / stateCode | admin_name1 / admin_code1 | CDC District |
county | admin_name2 | URA Planning Area; no code source exists |
cityName | admin_name3 | URA Subzone; no code source exists |
| (none) | alternative_city_name | nothing available — left blank, not invented |
latitude/longitude/accuracy are intentionally left blank by build_base.py —
join_coordinates.py fills them from a separately staged geocode lookup
(sources/singapore_coordinates.csv) in a second pass, the same two-stage shape as
Canada's admin2 enrichment.
Run order
python3 build_base.py
python3 join_coordinates.py
python3 integrity_checker.py
python3 package.py
python3 publish_r2.py # dry run by default; --apply to publish
Verification (2026-08-19)
Byte-identical. All 4 output files (csv, datapackage.json, README.md, zip) match
what's live on R2 exactly. publish_r2.py (written this session — it didn't exist
before) confirms a zero-diff plan on dry run.