dengue_prep Pipeline#
dengue_prep is the data preparation pipeline. It runs independently of the
forecast pipeline; its only job is to keep prepared_data/ current. The
downstream dengue pipeline reads from that directory and does not care how the
data got there.
See SOURCES.md for the case & weather source plugin architecture, and RUNNING_PIPELINES.md for how prep + forecast are run together in production.
What it does#
Two independent branches, executed in parallel:
Case branch — pulls raw IHIP-shaped case files from a configurable source (local directory, live dashboard API, or a custom plugin), parses each row into
(region_id, date), aggregates to a daily count, and upserts intoprepared_data/{region_type}/cases_daily.csv.Weather branch — fetches daily weather records for every region in the target geojson layer (via Open-Meteo, ERA5-Land CDS, filesystem, or a custom plugin), normalises units to Kelvin / metres on disk, aggregates hourly → daily where needed, and upserts into
prepared_data/{region_type}/weather_daily.csv.
Both outputs are append-deduped on (region_id, date) — re-running the same
window is idempotent; a corrected re-fetch overwrites the prior value.
DAG#
download_case_data ──> parse_case_data
download_weather_data ──> parse_weather_data
The two branches are unconnected — either can be disabled independently via
data.case_download.enabled / data.weather_download.enabled when you want to
refresh only one side.
Steps live in pipelines/dengue_prep/steps/ and are wired in
pipelines/dengue_prep/pipeline.py:build_pipeline.
Steps#
Step |
Class |
Responsibility |
|---|---|---|
|
|
Resolve the configured case-source plugin ( |
|
|
Read every IHIP file, resolve |
|
|
Resolve the weather source plugin, incrementally fetch missing months, normalise units, write per-month CSVs under |
|
|
Aggregate hourly / raw weather CSVs to daily via |
Config surface#
Every top-level section lives under data: in the YAML. Full schema is in
pipelines/dengue_prep/configs.py.
data.region_type#
The admin level being prepared — district, ward, mandal, zone, corp.
Selects which geojson layer under data.geojson.base_path is used for the
region_id allowlist and centroid lookups.
data.prepared_data.base_dir#
Where cases_daily.csv and weather_daily.csv are written. This is a
persistent, shared store — not inside the per-run artifact folder.
prepared_data:
base_dir: "ap_datasets/prepared_data"
data.date_range#
Global date window (start / end). If end is empty, it defaults to the
current run date. Both branches honour this — case rows outside are dropped;
weather fetches are clipped.
data.geojson.base_path#
Root directory holding <region_type>/*.geojson or <region_type>s/*.geojson
files. Used to (a) build the region_id allowlist, (b) compute centroids for
Open-Meteo fetches, (c) provide polygons for the geocode → PIP fallback.
data.case_download#
Selects and configures a case-source plugin. Full details in SOURCES.md.
case_download:
enabled: true
source_mode: dashboard # filesystem | dashboard | dotted.path | ./path.py
source_path: "./cache/dashboard_ap_cases"
# dashboard-only:
base_url: "https://apps.artpark.ai/disease-dashboard"
date_start: "2021-01-01"
backfill_days: 30
disease: "Dengue"
selected_region_id: "gulb_gba" # scope filter passed to the API
chunk_days: 365
data.case_parse#
Controls IHIP-file parsing.
case_parse:
region_types: ["district"]
date_column:
- "Date Of Onset"
- "Test Performed Date"
- "Sample Collected Date"
region_id_column: "Region Id" # if present, trusted verbatim
lgd_code_column: "District Code" # if present, LGD → region_id map
lat_column: "Latitude"
lon_column: "Longitude"
header_row: 0
filters: # AND across entries, OR within values
- column: "Test Suspected For"
values: ["Dengue"]
geocoding:
enabled: false # opt-in; see docs/GEOCODING.md
address_fields: ["Patient Address"]
fallback_address_fields: ["Facility"]
restrict_admin_area_tokens: ["Karnataka"]
bounds: [11.5, 74.0, 18.5, 78.7]
date_column — candidate list with per-row fallback. The parser accepts
either a string (back-compat) or a list. For each row, it walks the list in
order and uses the first column that yields a parseable date. This matters
for IHIP exports where Date Of Onset is sparse but Sample Collected Date
is universally populated — the row still gets counted on its most-authoritative
available date.
Universal region_id allowlist. Regardless of which resolver strategy runs,
the final row-level region_id values are intersected with the set of
region_ids parsed out of the geojson layer under
{geojson.base_path}/{region_type}/. Any row whose resolved region_id is not
in that allowlist is dropped. This is the single guarantee that no downstream
row can reference a region the forecast pipeline doesn’t know about.
data.weather_download#
Selects and configures a weather-source plugin. Full details in SOURCES.md.
weather_download:
enabled: true
source_mode: "openmeteo" # openmeteo | filesystem | cds | dotted.path | ./path.py
source_path: "od_datasets/weather" # required for source_mode=filesystem
parsed_output_path: "od_datasets/weather"
temperature_unit: "celsius" # unit the SOURCE returns; parser converts to K
precipitation_unit: "mm" # unit the SOURCE returns; parser converts to m
w_params: ["t2m", "d2m", "tp"]
threshold_km: 25.0
data.weather_parse#
Controls daily aggregation of weather CSVs.
weather_parse:
region_type: "district"
weather_variables: ["2mTemperature", "totalPrecipitation", "2mDewpointTemperature"]
daily_agg:
- {name: "2mTemperature", op: "mean"}
- {name: "2mDewpointTemperature", op: "mean"}
- {name: "totalPrecipitation", op: "sum"}
Output artifacts#
prepared_data/{region_type}/cases_daily.csv#
Column |
Type |
Description |
|---|---|---|
|
string |
e.g. |
|
YYYY-MM-DD |
Reporting date (resolved via |
|
int |
Daily confirmed case count |
Only (region_id, date) pairs with ≥1 case are stored. A missing row means
zero cases, not missing data. Region IDs are always inside the allowlist
derived from the geojson layer.
prepared_data/{region_type}/weather_daily.csv#
Column |
Type |
Description |
|---|---|---|
|
string |
Admin region identifier |
|
YYYY-MM-DD |
Date |
|
float |
Daily mean (Kelvin) |
|
float |
Daily mean (Kelvin) |
|
float |
Daily total (metres) |
Column names use ERA5 conventions so the dengue pipeline can apply rolling
transforms uniformly regardless of which source produced the data. Units on
disk are always Kelvin / metres — the download step normalises based on
temperature_unit / precipitation_unit config (see SOURCES.md).
outputs/prep_summary.md#
Per-run markdown report written into the artifact folder, summarising:
case files parsed, rows dropped by filter / region-allowlist / date-parse
weather months fetched vs. skipped-complete vs. upstream-empty
coverage: which target region_ids have zero cases / zero weather
Common run recipes#
# Andhra Pradesh (districts, dashboard-sourced cases, Open-Meteo weather)
uv run python -m acestor.run \
--pipeline pipelines.dengue_prep.pipeline:build_pipeline \
--config configs/ap_district_prep.yaml \
--run-id ap-prep-$(date +%Y%m%d)
# Odisha (districts, dashboard cases, Open-Meteo weather)
uv run python -m acestor.run \
--pipeline pipelines.dengue_prep.pipeline:build_pipeline \
--config configs/od_district_prep.yaml \
--run-id od-prep-$(date +%Y%m%d)
# Greater Bengaluru — three geographies share the same case source
uv run python -m acestor.run \
--pipeline pipelines.dengue_prep.pipeline:build_pipeline \
--config configs/gba_ward_prep.yaml \
--run-id gba-ward-prep-$(date +%Y%m%d)
uv run python -m acestor.run \
--pipeline pipelines.dengue_prep.pipeline:build_pipeline \
--config configs/gba_zone_prep.yaml \
--run-id gba-zone-prep-$(date +%Y%m%d)
uv run python -m acestor.run \
--pipeline pipelines.dengue_prep.pipeline:build_pipeline \
--config configs/gba_corp_prep.yaml \
--run-id gba-corp-prep-$(date +%Y%m%d)
# Karnataka (districts, filesystem-sourced weather CSVs already on disk)
uv run python -m acestor.run \
--pipeline pipelines.dengue_prep.pipeline:build_pipeline \
--config configs/ka_district_prep.yaml \
--run-id ka-prep-$(date +%Y%m%d)
Required env vars for the dashboard case source:
export DASHBOARD_URL="https://apps.artpark.ai/disease-dashboard"
export DASHBOARD_CLIENT_ID="…"
export DASHBOARD_CLIENT_SECRET="…"
See SOURCES.md — Environment variables for the full list.