An automated end-to-end Prefect v3 ETL pipeline that ingests, transforms, and warehouses socioeconomic, health, and environmental indicators for Yorkshire into PostgreSQL, powering the Yorkshire Vitality Suite dashboards.
Full documentation is at yhoda-project.github.io/yhoda-pipeline, including:
- Onboarding - get set up on the pipeline
- Architecture - how the system works
- Runbooks - what to do when something goes wrong
- Reference - environment variables, ERD, glossary
flowchart LR
A[Live APIs] --> C[Extract]
B[Static Releases] --> C
C --> D[Transform & Validate]
D --> E[(PostgreSQL)]
E --> F[Power BI\nDashboards]
- Python 3.11+
uvpackage manager- PostgreSQL 14+ with the target database created
- Self-hosted Prefect v3 server
git clone https://github.com/yhoda-project/yhoda-pipeline.git
cd yhoda-pipeline
uv sync --extra dev
cp .env.example .env # then fill in DATABASE_URL and DWP_API_KEY at minimumuv run alembic upgrade head
uv run python -m yhovi_pipeline.utils.seed_geo_lookup
uv run python -m yhovi_pipeline.utils.load_csv
uv run python -m yhovi_pipeline.utils.load_jobs
uv run python -m yhovi_pipeline.utils.load_industry
uv run python -m yhovi_pipeline.utils.load_neighbourhoods
uv run python -m yhovi_pipeline.utils.compute_correlationsuv run prefect deploy --all --no-prompt
uv run python -c "from yhovi_pipeline.config import get_settings; print('ok')"
uv run pytest
uv run ruff check src/ tests/uv run ruff check --fix src/ tests/ # lint
uv run ruff format src/ tests/ # format
uv run mypy src/ # type check
uv run pytest --cov=src/yhovi_pipeline # tests with coverageUnit tests require env vars but no real database:
export DATABASE_URL="postgresql+psycopg2://t:t@localhost/d"
export DWP_API_KEY="x"
uv run pytest tests/unit/Pre-commit hooks (ruff, mypy, file hygiene) run automatically on every commit. Install them once with:
uv run pre-commit installCI runs on every push and pull request. Deployments to Prefect are triggered automatically on merge to main.
src/yhovi_pipeline/
├── config.py # pydantic-settings; always use get_settings()
├── db/ # SQLAlchemy models + Alembic migrations
├── flows/ # 15 Prefect flows across economy/, society/, environment/
├── tasks/
│ ├── extract/ # one module per source (nomis, fingertips, dwp, ons, ...)
│ ├── transform/ # validate, normalise, geo
│ └── load/ # upsert_indicators, write_metadata
└── utils/ # CSV loaders, geo lookup, email alerts
See LICENSE.