Quick Start

git clone https://github.com/compbiolover/housing-nutrition-label.git
cd housing-nutrition-label
python3 -m venv .venv
source .venv/bin/activate
pip install -e .                    # installs package + dependencies
python scripts/run_pipeline.py      # run the full pipeline

Installing with pip install -e . (editable mode) gives you the housing-simulate and housing-pipeline console commands, plus installs requests and pandas automatically from pyproject.toml.

The pipeline will run all 12 stages in order. On first run, stages that query external APIs (FEMA flood, CDC PLACES, Census ACS) will take longer due to rate limiting. Subsequent runs skip stages whose output is already fresh.

API Keys

The live scoring path (any US address) needs no API keys — health, socioeconomic, and walkability are bundled national references. The keys below are optional and only relevant to the offline Shelby parcel pipeline / optional Walk Score enrichment:

Census ACS (Optional — offline parcel pipeline only)

Free signup: api.census.gov/data/key_signup.html

export CENSUS_API_KEY=your_key_here

The live socioeconomic score is a bundled national percentile (keyless). This key is only used by the offline Shelby parcel pipeline to attach each tract's raw ACS metric columns; the socioeconomic score is national either way.

Walk Score (Optional — not used by default)

Signup: walkscore.com/professional/api.php

export WALKSCORE_API_KEY=your_key_here
python src/housing_label/enrich/walkscore.py            # optional enrichment (resumable)

Walkability defaults to the bundled, public-domain EPA National Walkability Index — no key, no quota, and safe to store (unlike the Walk Score API, whose Terms of Use prohibit storing scores). Running the Walk Score enrichment above is optional; when its walk_score columns are present the pipeline honours them instead.

Project Structure

housing-nutrition-label/
├── src/housing_label/           # Installable Python package
│   ├── config.py                # Shared constants (URLs, rates, thresholds)
│   ├── utils.py                 # Shared helpers (HTTP, haversine, coords)
│   ├── ingest/                  # Data ingestion
│   │   ├── shelby_parcels.py    # ArcGIS parcel + CAMA pull
│   │   └── clean.py             # Data cleanup
│   ├── enrich/                  # Enrichment modules (9 data sources)
│   │   ├── fema_flood.py
│   │   ├── noaa_climate.py
│   │   ├── tornado.py
│   │   ├── seismic.py
│   │   ├── energy.py
│   │   ├── infrastructure.py
│   │   ├── health.py
│   │   ├── socioeconomic.py
│   │   └── walkscore.py
│   ├── score/                   # Scoring models
│   │   ├── resilience.py        # EAL-based disaster scoring
│   │   └── all_dimensions.py    # Multi-dimension scorer
│   └── simulate/                # CLI house simulator
│       └── house.py
├── scripts/run_pipeline.py       # Pipeline orchestrator
├── research/                    # Research & methodology docs
├── tests/                       # Test directory
├── docs/                        # housinglabel.dev site
├── pyproject.toml               # Package metadata & dependencies
└── README.md

Pipeline Architecture

The pipeline runs 12 stages in sequence. Each stage reads a CSV, enriches it, and writes the output for the next stage. All stages live under src/housing_label/.

1. ingest/shelby_parcels
2. ingest/clean
3. enrich/fema_flood
4. enrich/noaa_climate
5. enrich/tornado
6. enrich/seismic
7. enrich/energy
8. enrich/infrastructure
9. enrich/health
10. enrich/socioeconomic
11. score/resilience
12. score/all_dimensions

Pipeline Runner Options

# Run everything fresh
python scripts/run_pipeline.py --force

# Run from a specific stage onward
python scripts/run_pipeline.py --from energy

# Run a single stage
python scripts/run_pipeline.py --step flood

# Continue past failures
python scripts/run_pipeline.py --continue-on-error

# Preview what would run
python scripts/run_pipeline.py --dry-run

# Or use the console command (after pip install -e .)
housing-pipeline --force

House Simulator

The CLI simulator lets you define a hypothetical house and see its full nutrition label — all nine dimensions — instantly.

The five construction-driven dimensions (resilience, energy, durability, environmental, infrastructure) are modeled offline from the house configuration. The four location-driven dimensions depend on where the house sits: health (CDC PLACES), socioeconomic (Census ACS), and walkability (EPA National Walkability Index) are bundled national references resolved by the house's census tract — no API key needed, and comparable across locations; where a tract can't be resolved they show N/A and are excluded from the composite, never filled with a placeholder. Climate projections likewise come from a bundled per-county lookup (no API key) once the county is resolved — a known-but-unmapped county uses a national-average fallback, but like the other location dimensions it is excluded under --no-fetch, since resolving the county still needs the geocoder.

Full-Label Flags

# Emit the whole label (all dimensions + composite) as JSON
housing-simulate --preset icf-passive --lat 35.15 --lon -89.85 --json

# Skip live location lookups (offline; health/socio/walk left unscored)
housing-simulate --preset icf-passive --no-fetch

# Supply a location dimension directly instead of fetching it
housing-simulate --preset baseline --walk-score 79.6 --health-index 57 --socioeconomic-index 52

Using Presets

# Via the script directly
python src/housing_label/simulate/house.py --preset baseline --lat 35.15 --lon -89.85
python src/housing_label/simulate/house.py --preset icf-passive --lat 35.15 --lon -89.85

# Or via the console command (after pip install -e .)
housing-simulate --preset icf-passive --lat 35.15 --lon -89.85

# Multi-unit buildings
housing-simulate --preset duplex --lat 35.15 --lon -89.85
housing-simulate --preset icf-quadplex --lat 35.15 --lon -89.85

Available Presets

PresetDescription
baseline2000 wood frame, slab, average condition, 2,000 sqft, 0.25 ac
premium2026 brick, slab, excellent, 3,000 sqft, 0.35 ac, $450K
icf-passive2026 ICF + solar + generator + passive house + safe room + hip roof + straps + metal roof + sealed deck + elevation
worst-case1945 frame, full basement, Zone AE, poor condition, 2.0 ac
fortified-gold2026 frame + IBHS FORTIFIED Gold certification
duplex2026 brick, 2 units × 1,200 sqft, 0.15 ac, $300K
quadplex2026 brick, 4 units × 900 sqft, 0.20 ac, $500K
icf-quadplex2026 ICF, 4 units + solar + passive + straps + hip roof, $600K

Custom Builds

# Build your own scenario
housing-simulate \
  --year-built 2026 \
  --construction icf \
  --foundation slab \
  --condition excellent \
  --flood-zone X \
  --lat 35.13 --lon -89.99 \
  --value 330000 \
  --units 2 \
  --sqft 900 \
  --lot-acres 0.12 \
  --solar \
  --hurricane-straps \
  --hip-roof \
  --sealed-roof-deck

Above-Code Feature Flags

CategoryFlags
Wind/Tornado--hurricane-straps --hip-roof --impact-garage-door --sealed-roof-deck --metal-roof --reinforced-gable --ring-shank-nails --truss-16oc
FORTIFIED--fortified-roof --fortified-silver --fortified-gold
Seismic--cripple-wall-bracing --seismic-hold-downs --auto-gas-shutoff --seismic-retrofit
Flood--elevation-1ft --elevation-2ft --elevation-3ft --flood-vents --backflow-valve --leak-detection
General--solar --backup-generator --passive-house --tornado-safe-room --fire-sprinklers

Address-search API

The Examples page can score any US address with a live nutrition label. Because the site is static (GitHub Pages), the scoring runs behind a small HTTP API that wraps the CLI simulator — the same scoring path, no model drift.

Run it

pip install -e ".[api]"            # installs FastAPI + uvicorn
# optional: full 8 dimensions need keys (server-side)
export CENSUS_API_KEY=your_key WALKSCORE_API_KEY=your_key
housing-api                        # serves on :8000 (PORT overrides)
# or: uvicorn housing_label.api:app --host 0.0.0.0 --port 8000

Endpoints: GET /healthz, GET /suggest?q=<text> (address typeahead), and GET /label?address=<addr>&preset=<preset> (or ?lat=&lon=). Optional /label overrides: year_built, construction, foundation, condition, sqft, units, lot_acres, value, flood_zone, and upgrades (comma-separated resilience flags, e.g. upgrades=solar,fortified_roof,hurricane_straps). Unknown values return a 400.

Address autocomplete

GET /suggest?q=<text> returns up to five US suggestions as [{label, lat, lon}], proxied server-side so visitors' partial text reaches only your origin (no third-party script, IP, or cookies leave the browser). Selecting a suggestion submits its coordinates to /label?lat=&lon= so the scored point matches exactly; free-typed addresses still fall back to ?address= geocoding.

Provider: by default it uses the keyless Photon geocoder (OpenStreetMap, PHOTON_URL overridable to self-host). Photon's US house-number ranking is weak; for sharper results set GEOAPIFY_API_KEY (free tier at geoapify.com, EU/GDPR, OSM + OpenAddresses) — the API then uses Geoapify and falls back to Photon if it's unreachable. The key stays server-side; it never reaches the browser. GEOAPIFY_URL overrides the endpoint.

Deploy it

GitHub Pages can't host the Python API — deploy it anywhere that runs Python (Render, Fly, Cloud Run, Railway, a small VM). The repo ships two ready-to-use configs:

CORS: the API restricts browser origins to https://housinglabel.dev by default. Override with the ALLOWED_ORIGINS env var (comma-separated) for a different domain or local dev, e.g. ALLOWED_ORIGINS="https://housinglabel.dev,http://localhost:8000".

Point the page at your API

The search bar reads its API base URL from, in order: a ?api= query param on the Examples page, then window.HOUSING_LABEL_API. For a permanent default, set the global before nav.js loads, e.g.:

<script>window.HOUSING_LABEL_API = "https://your-api-host"</script>

Until an API URL is configured the search bar shows a short setup note instead of calling out.

Adding a New Dimension

The pipeline is designed to be extensible. To add a new scoring dimension:

  1. Write an enrichment module in src/housing_label/enrich/ following the existing pattern: accepts --input/--output/--limit flags, reads the previous stage's CSV, adds new columns, writes the enriched CSV.
  2. Add a scoring function in src/housing_label/score/all_dimensions.py that maps your new metric to a 0–100 score.
  3. Wire it into scripts/run_pipeline.py's STAGES list in the correct position.
  4. The dual grading (national + local) and composite score automatically include the new dimension.

Tech Stack

Python 3.9+ with two dependencies: requests and pandas. No geo libraries, no databases, no frameworks. All spatial calculations (Haversine, Web Mercator conversion, polygon centroids) use pure math. This is intentional — the entire pipeline runs on any machine with Python installed.

The project is packaged as a standard Python package via pyproject.toml and can be installed with pip install -e . for development or built as a wheel for distribution.

Roadmap

Phase 1 (current) covers data ingestion and scoring. The longer-term vision includes: