Outcome: fill the three TODO functions and pass all four self-checks below against the LOCAL fixture generator — no network, no hosted server.
# src/ingest/fetch.py
import time
from src.ingest.fixture import get_page # LOCAL fixture, not a server
def fetch_page(resource: str, page: int, per_page: int = 100) -> dict:
# TODO: call get_page(resource, page, per_page)
# TODO: retry 3x with exponential backoff on 429/5xx
# (simulate: raise on first call for page 2, succeed on retry)
raise NotImplementedError
def fetch_incremental(resource: str, since: str):
# TODO: loop pages until an empty `data` list; skip rows with
# updated_at <= since (watermark); write each page to
# data/parquet/{resource}/page_{n}.parquet
raise NotImplementedError
# src/ingest/load.py
import glob
import pandas as pd
from sqlalchemy import create_engine, text
def load_staging(resource: str, engine, watermark: str) -> int:
# TODO: read data/parquet/{resource}/*.parquet
# TODO: upsert into stg_{resource} on primary key inside ONE transaction
# TODO: advance ingest_watermark to max(updated_at) only on success
raise NotImplementedError
Self-checks before submitting (run in repo root)
python -m src.ingest.fixture --check— prints FIXTURE OK (200 orders / 2 pages, unique ids, monotonicupdated_at).- Run fetch + load twice with no new data — staging counts identical
the second time (idempotent re-run, ms 276
done_when). - Mutate one fixture row (change status, bump
updated_atpast the watermark) and re-run — exactly one row changes. findings/01_ingestion.mdrecords counts + watermark + re-run proof; kill a load mid-transaction and show the re-run reconciles infindings/02_failure_drill.md(ms 277).