Skip to content

Getting Started — anza-elt

This guide gets the anza-elt project running on Windows or macOS, including Dev Containers, PostgreSQL, pgAdmin 4, and Dagster.


Table of contents

  1. Prerequisites

  2. Windows: WSL 2 and Docker

  3. macOS: Docker

  4. Open the project in a Dev Container

  5. Environment and data paths

  6. PostgreSQL and PostGIS

  7. pgAdmin 4

  8. Dagster

  9. Project directory reference


1. Prerequisites

  • Git

  • VS Code or Cursor with the Dev Containers extension

  • Docker (via Docker Desktop; on Windows you’ll use WSL 2)


2. Windows: WSL 2 and Docker

The project runs inside a Dev Container. On Windows you need WSL 2 and Docker Desktop using the WSL 2 backend.

Install WSL 2

  1. Open PowerShell as Administrator and run:
wsl --install

This installs WSL 2 and a default Linux distro (e.g. Ubuntu).

  1. Restart your machine if prompted.

  2. (Optional) Set WSL 2 as the default:

wsl --set-default-version 2
  1. Confirm WSL is running and on version 2:
wsl --list --verbose

wsl --status

Install Docker Desktop and enable WSL 2

  1. Download Docker Desktop for Windows:

https://www.docker.com/products/docker-desktop

  1. Run the installer and leave “Use WSL 2 instead of Hyper-V” (or “Install required components for WSL 2”) checked.

  2. After installation, open Docker DesktopSettings (gear icon).

  3. Go to General and ensure “Use the WSL 2 based engine” is enabled.

  4. In ResourcesWSL Integration, enable integration for your WSL distro (e.g. Ubuntu).

  5. Click Apply & Restart.

  6. Confirm Docker works in WSL (open Ubuntu from the Start menu):

docker --version

docker run hello-world

Once this works, you can open the repo in Cursor/VS Code and use “Reopen in Container” as in §4.


3. macOS: Docker

  1. Download Docker Desktop for Mac:

https://www.docker.com/products/docker-desktop

  1. Install and open Docker Desktop. Allow the privileged helper when prompted.

  2. Confirm Docker works:

docker --version

docker run hello-world
  1. For Apple Silicon (M1/M2/M3): the project’s Dev Container uses arm64 by default (see .devcontainer/devcontainer.json). No extra steps needed.

4. Open the project in a Dev Container

  1. Clone the repo (if you haven’t already):
git clone <repo-url> anza-elt

cd anza-elt
  1. Open the folder in Cursor or VS Code:
code anza-elt

# or

cursor anza-elt
  1. When prompted, choose “Reopen in Container” (or use Command Palette → “Dev Containers: Reopen in Container”).

  2. Wait for the container to build and start. The first time can take several minutes (Python, Node, GDAL, etc.).

  3. The Dev Container mounts (see .devcontainer/devcontainer.json):

  4. ~/data/_data (e.g. election/census data)

  5. ~/Downloads/igsigs (optional; comment out if you don’t use it)

Create these on the host if you use them:

  • Windows (WSL): e.g. mkdir -p ~/data ~/Downloads/igs

  • macOS: mkdir -p ~/data ~/Downloads/igs


5. Environment and data paths

  1. Copy the env template and edit as needed:
cp env.template .env
  1. In .env, the important variables for Postgres and Dagster are:

  2. PG_HOST, PG_PORT, PG_USER, PG_PWD, PG_DB

  3. When running inside the Dev Container, use PG_HOST=host.docker.internal so the container can reach Postgres on your host (Windows or macOS).

  4. Create any directories your .env references (e.g. _data/elec, _data/census, _data/igs) or adjust paths in .env to match your layout.

  5. For elec tests, the README asks for fixture data under ingestion/elec/tests/fixtures/elec/ (e.g. from g22.zip). Add those if you run elec ingestion/tests.


6. PostgreSQL and PostGIS

The pipeline expects a PostgreSQL database with PostGIS (and optionally postgis_topology). Run this on your host (or in a separate Postgres container), not inside the Dev Container.

Option A: PostgreSQL on the host (Windows WSL or macOS)

  • Windows (WSL): Install Postgres and PostGIS inside your WSL distro (e.g. Ubuntu):
sudo apt update && sudo apt install -y postgresql postgresql-contrib postgis
sudo service postgresql start
  • macOS: Install with Homebrew:
brew install postgresql@16 postgis
brew services start postgresql@16

Or use Postgres.app and add PostGIS.

Create the database and extensions using the project’s SQL script:

# From the project root (host or WSL), with psql in PATH:
psql -U postgres -f ingestion/precincts/postgis_setup/precinct_topology_db.sql

If your setup uses a different locale (e.g. WSL without en_US.UTF-8), use the second CREATE DATABASE block in that file (the one without LC_COLLATE/LC_CTYPE/ICU_LOCALE).

Option B: PostgreSQL in Docker (host)

Run Postgres + PostGIS on the host so the Dev Container can reach it via host.docker.internal:

docker run -d --name anza-postgres \
-e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=postgres \
-e POSTGRES_DB=precinct_topology \
-p 5432:5432 \
postgis/postgis:16-3.4

Then create extensions and schema (connect with psql or pgAdmin and run the contents of ingestion/precincts/postgis_setup/precinct_topology_db.sql as needed).

Use PG_HOST=host.docker.internal and PG_PORT=5432 in .env inside the Dev Container.


7. pgAdmin 4

pgAdmin 4 is a GUI for managing PostgreSQL. Use it to inspect the precinct_topology database and run SQL.

Install pgAdmin 4

  • Windows:

https://www.pgadmin.org/download/pgadmin-4-windows/

  • macOS:

https://www.pgadmin.org/download/pgadmin-4-macos/

Or: brew install --cask pgadmin4

Connect to your database

  1. Open pgAdmin 4.
  2. Right‑click ServersRegisterServer.
  3. General tab: set a name (e.g. anza-elt).
  4. Connection tab:
    • Host: localhost (or host.docker.internal if you’re inside a container and want to reach host Postgres).
    • Port: 5432 (or your PG_PORT).
    • Maintenance database: postgres.
    • Username: postgres
    • Password: your postgres
  5. Save.

You can then browse the precinct_topology database, run precinct_topology_db.sql or parts of it, and inspect tables/schemas (e.g. postgis, anza_geom, staging schemas).


8. Dagster

Dagster runs the pipeline (ingestion assets, dbt, etc.) and serves the Dagster UI. The Dev Container is configured so Dagster runs inside the container.

Configuration

  • DAGSTER_HOME is set in the Dev Container to /root/.dagster_home (see .devcontainer/devcontainer.json).
  • Dagster config is in .devcontainer/dagster.yaml (e.g. SQLite storage, no run retries).

Environment variables

Ensure the same Postgres variables are available where Dagster runs (Dev Container). The container should load .env or you can set:

  • PG_HOST, PG_DB, PG_USER, PG_PWD, PG_PORT
  • Optionally: PG_GEOM_SCHEMA, PG_STAGING_SCHEMA, CENSUS_ROOT, etc., as in env.template.

Run Dagster (inside the Dev Container)

  1. Reopen the project in the Dev Container (§4).
  2. In a terminal inside the container:
    uv sync
    dg dev
    
    Or, if you use the legacy CLI:
    dagster dev
    
  3. The Dagster UI is forwarded to port 3000. Open: http://localhost:3000
  4. In the UI you can launch runs, view assets, and inspect the dbt and precinct ingestion jobs.

If the Dagster UI doesn’t open

  • Confirm port 3000 is forwarded (Dev Container “Ports” panel or .devcontainer/devcontainer.json).
  • Ensure Postgres is reachable from the container (PG_HOST=host.docker.internal when Postgres is on the host).

9. Project directory reference

Directory Description
.devcontainer/ Dev Container definition: Dockerfile (Python, Node, GDAL, mapshaper, Dagster env), devcontainer.json (mounts, ports, extensions), and dagster.yaml used as DAGSTER_HOME config.
dagster_project/ Dagster code: definitions.py (asset and resource wiring), resources.py (e.g. Postgres connection), precincts/assets.py (precinct/PostGIS and mapshaper assets), and defs/ for dbt components (e.g. dbt_postgres, dbt_duckdb).
ingestion/ Data ingestion: elec/ (election file validation and load; pipeline, ingest, duckdb, data models, tests) and precincts/ (preprocessing, PostGIS/topology ingest, tests; postgis_setup/ has SQL for the precinct_topology DB and PostGIS; postgis_topo_ingest/ for topology-focused ingest).
transform/ dbt project: models/ (elec and precincts), macros/, seeds/, snapshots/, analyses/, tests/. Builds with dbt run / Dagster dbt assets; profiles.yml uses env vars (e.g. PG_*) for the Postgres target.
__ingestion_local_writes/ Default local output directory for ingestion writes (referenced in env.template as INGESTION_LOCAL_WRITE_DIR).
.venv/ Python virtual environment (uv/poetry); used for uv run and make targets.
.tmp_dagster_home_*/ Temporary Dagster home directories (e.g. from local runs); can be ignored or removed.
_data/ Intended mount for data (e.g. elec, census, igs); created on host and mounted via Dev Container (see .devcontainer/devcontainer.json).
igs/ Optional mount for IGS data; same as above.

Quick checklist

  • WSL 2 + Docker Desktop (Windows) or Docker Desktop (macOS)
  • Cursor/VS Code + Dev Containers extension
  • Repo cloned, .env created from env.template, PG_HOST=host.docker.internal when using host Postgres
  • PostgreSQL + PostGIS installed or running (Docker/host), precinct_topology DB created (and extensions/schema from precinct_topology_db.sql)
  • pgAdmin 4 installed and connected to precinct_topology
  • Project reopened in Dev Container; uv sync and dg dev (or dagster dev); Dagster UI at http://localhost:3000

For more on elec ingestion, fixtures, and precincts workflow, see the main README.md.