Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Full workflow for importing CHIRPS v3 into DHIS2

In this example workflow, we demonstrate how to use DHIS2 Climate Tools to make sure that DHIS2 is continuously updated with the latest climate data. Specifically we show step by step how to download and import the latest available daily precipitation data from the CHIRPS v3 data.

The notebook fetches and imports only data that is not yet present in DHIS2, making it safe to run on a recurring basis and ensuring that DHIS2 stays up to date with the latest available climate data.

If you’re only interested in downloading CHIRPS v3 data, see this detailed step-by-step guide.

Important: This notebook only aggregates to daily periods according the Gregorian calendar. Other calendar systems, like those used in Nepal or Ethiopia, are not yet supported.

Prerequisites

Before proceeding with the notebook, make sure the following are in place:

1. Required DHIS2 data element

Your DHIS2 instance must contain a data element that can receive the imported data.

For daily precipitation, the data element must have:

  • valueType = NUMBER

  • aggregationType = SUM

  • It must belong to a data set with periodType = DAILY

If this data element does not already exist, you have two options:

Once the data element exists, copy its UID and set it as DHIS2_DATA_ELEMENT_ID in the Input Parameters section further down.

Library imports

We start by importing the necessary libraries:

from datetime import date
import json

import geopandas as gpd
import xarray as xr
from earthkit import transforms

from dhis2_client import DHIS2Client
from dhis2_client.settings import ClientSettings

from dhis2eo.data.chc import chirps3
from dhis2eo.integrations.pandas import dataframe_to_dhis2_json

Input parameters

Let’s first define all the input parameters so they are clearly stated at the top of the notebook.

For this example we will connect to a public DHIS2 instance, so it’s important that you create the precipitation data element (as described previously) and update the DHIS2_DATA_ELEMENT_ID below. Note also that the public instance resets every night, so this process will have to be repeated for each new day.

Note that CHIRPS precipitation data is already provided in the units (millimeters) and temporal resolution (daily) we want to import into DHIS2. Therefore, this workflow does not include parameters for unit conversion or temporal aggregation.

# DHIS2 connection
DHIS2_BASE_URL = "https://play.im.dhis2.org/stable-2-42-3-1"
DHIS2_USERNAME = "admin"
DHIS2_PASSWORD = "district"

# DHIS2 import settings
DHIS2_DATA_ELEMENT_ID = '<INSERT-DATA-ELEMENT-ID-HERE>'
DHIS2_ORG_UNIT_LEVEL = 2
DHIS2_DRY_RUN = True                            # default to safe dry-run mode; set to False for actual import

# CHIRPS import configuration
IMPORT_VALUE_COL = "precip"                     # variable name in the downloaded xarray dataset
IMPORT_START_DATE = "2025-07-01"                # how far back in time to start import
IMPORT_END_DATE = date.today().isoformat()      # automatically tries to import the latest data

# Download settings
DOWNLOAD_FOLDER = "../../guides/data/local"
DOWNLOAD_PREFIX = "chirps3-daily"    # prefix for caching downloads; existing files are reused

# Aggregation settings
SPATIAL_AGGREGATION = "mean"

Connect to DHIS2

First, we connect the python-client to the DHIS2 instance we want to import into. You can point this to your own instance, but for the purposes of this example we will use one of the public access DHIS2 instances, since these are continuously reset:

# Client configuration
cfg = ClientSettings(
  base_url=DHIS2_BASE_URL,
  username=DHIS2_USERNAME,
  password=DHIS2_PASSWORD
)

client = DHIS2Client(settings=cfg)
info = client.get_system_info()

# Check if everything is working.
# You should see your current DHIS2 version info.
print("Current DHIS2 version:", info["version"])
Current DHIS2 version: 2.42.3.1

Get the DHIS2 organisation units

In order to download and aggregate the data to our DHIS2 organisation units, we also use the python-client to get the level 2 organisation units from our DHIS2 instance:

### Get org units GeoJSON from DHIS2
org_units_geojson = client.get_org_units_geojson(level=DHIS2_ORG_UNIT_LEVEL)

# Convert GeoJSON to geopandas
org_units = gpd.read_file(json.dumps(org_units_geojson))
org_units
Skipping field groups: unsupported OGR type: 5
Loading...

Check when the data was last imported

Since we want to run this script on a regular interval, we want to avoid importing data that has already been imported. We therefore first want to check the last date for which data was imported for the data element we want to import into. This can be done using the convenience function analytics_latest_period_for_level() provided by the python-client:

last_imported_response = client.analytics_latest_period_for_level(de_uid=DHIS2_DATA_ELEMENT_ID, level=DHIS2_ORG_UNIT_LEVEL)
last_imported_response
{'meta': {'dataElement': 'UKuEMLLnoQI', 'level': 2, 'periodType': 'DAILY', 'calendar': 'iso8601', 'years_checked': 31}, 'existing': None, 'next': None}

Let’s extract and report the last imported month:

last_imported_period = last_imported_response["existing"]
last_imported_month_string = last_imported_period["id"][:6] if last_imported_period else None

if last_imported_month_string:
    print(f"Last imported period: {last_imported_month_string}")
else:
    print("No existing data found")
No existing data found

We then use this information to define when we will start the data download, and ensure that we only download data after the last_imported_string:

if last_imported_month_string:
    IMPORT_START_DATE_OVERRIDE = max(last_imported_month_string, IMPORT_START_DATE)
else:
    IMPORT_START_DATE_OVERRIDE = IMPORT_START_DATE

print(f'Import will start at {IMPORT_START_DATE_OVERRIDE}')
Import will start at 2025-07-01

Download the necessary data

In the next step we download all the requested data to the local file system, using convenience functionality from the dhis2eo.data.chc.chirps3 module.

Running this step may take some time depending on how many months of data are requested.

Note that after the initial data download, subsequent runs of this notebook will re-use the previously imported files to avoid repeated downloads of the same data.

For more details on this step, see our guide for Downloading CHIRPS v3 data.

print(f'Downloading data for the period: {IMPORT_START_DATE_OVERRIDE} to {IMPORT_END_DATE}...')
files = chirps3.daily.download(
    start=IMPORT_START_DATE_OVERRIDE, 
    end=IMPORT_END_DATE, 
    bbox=org_units.total_bounds, 
    dirname=DOWNLOAD_FOLDER, 
    prefix=DOWNLOAD_PREFIX, 
)
files
Downloading data for the period: 2025-07-01 to 2026-01-14...
INFO - 2026-01-14 10:31:01,128 - dhis2eo.data.chc.chirps3.daily - Fetching CHIRPS v3 daily from 2025-7 to 2026-1 (inclusive)
INFO - 2026-01-14 10:31:01,131 - dhis2eo.data.chc.chirps3.daily - Stage/flavor: final/rnl
INFO - 2026-01-14 10:31:01,134 - dhis2eo.data.chc.chirps3.daily - BBox: [-13.3035   6.9176 -10.2658  10.0004]
INFO - 2026-01-14 10:31:01,136 - dhis2eo.data.chc.chirps3.daily - Month 2025-7
INFO - 2026-01-14 10:31:01,141 - dhis2eo.data.chc.chirps3.daily - File already downloaded: C:\Users\karimba\Documents\Github\climate-tools\docs\guides\data\local\chirps3-daily_2025-07.nc
INFO - 2026-01-14 10:31:01,143 - dhis2eo.data.chc.chirps3.daily - Month 2025-8
INFO - 2026-01-14 10:31:01,152 - dhis2eo.data.chc.chirps3.daily - File already downloaded: C:\Users\karimba\Documents\Github\climate-tools\docs\guides\data\local\chirps3-daily_2025-08.nc
INFO - 2026-01-14 10:31:01,155 - dhis2eo.data.chc.chirps3.daily - Month 2025-9
INFO - 2026-01-14 10:31:01,159 - dhis2eo.data.chc.chirps3.daily - File already downloaded: C:\Users\karimba\Documents\Github\climate-tools\docs\guides\data\local\chirps3-daily_2025-09.nc
INFO - 2026-01-14 10:31:01,161 - dhis2eo.data.chc.chirps3.daily - Month 2025-10
INFO - 2026-01-14 10:31:01,167 - dhis2eo.data.chc.chirps3.daily - File already downloaded: C:\Users\karimba\Documents\Github\climate-tools\docs\guides\data\local\chirps3-daily_2025-10.nc
INFO - 2026-01-14 10:31:01,170 - dhis2eo.data.chc.chirps3.daily - Month 2025-11
INFO - 2026-01-14 10:31:01,172 - dhis2eo.data.chc.chirps3.daily - File already downloaded: C:\Users\karimba\Documents\Github\climate-tools\docs\guides\data\local\chirps3-daily_2025-11.nc
INFO - 2026-01-14 10:31:01,174 - dhis2eo.data.chc.chirps3.daily - Month 2025-12
WARNING - 2026-01-14 10:31:01,175 - dhis2eo.data.chc.chirps3.daily - Skipping downloads for months that have not been published yet (after 20th of the following month).
WARNING - 2026-01-14 10:31:01,176 - dhis2eo.data.chc.chirps3.daily - Last available month in CHIRPS v3: 2025-11
INFO - 2026-01-14 10:31:01,180 - dhis2eo.data.chc.chirps3.daily - Month 2026-1
WARNING - 2026-01-14 10:31:01,184 - dhis2eo.data.chc.chirps3.daily - Skipping downloads for months that have not been published yet (after 20th of the following month).
WARNING - 2026-01-14 10:31:01,187 - dhis2eo.data.chc.chirps3.daily - Last available month in CHIRPS v3: 2025-11
[WindowsPath('C:/Users/karimba/Documents/Github/climate-tools/docs/guides/data/local/chirps3-daily_2025-07.nc'), WindowsPath('C:/Users/karimba/Documents/Github/climate-tools/docs/guides/data/local/chirps3-daily_2025-08.nc'), WindowsPath('C:/Users/karimba/Documents/Github/climate-tools/docs/guides/data/local/chirps3-daily_2025-09.nc'), WindowsPath('C:/Users/karimba/Documents/Github/climate-tools/docs/guides/data/local/chirps3-daily_2025-10.nc'), WindowsPath('C:/Users/karimba/Documents/Github/climate-tools/docs/guides/data/local/chirps3-daily_2025-11.nc')]

Open the downloaded data

Once the data has been downloaded, we can then pass the list of files to xr.open_mfdataset(). This allows us to open and work with the data as if it were a single xarray dataset:

ds_daily = xr.open_mfdataset(files)
ds_daily
Loading...

Aggregate to organisation units

Since the data is already at the correct daily level, we can proceed directly to spatial aggregation of the gridded data to our organisation units:

print("Aggregating to organisation units...")
ds_org_units = transforms.spatial.reduce(
    ds_daily,
    org_units,
    mask_dim="id",
    how=SPATIAL_AGGREGATION,
)
ds_org_units
Aggregating to organisation units...
Loading...

Post-processing

After aggregating the precipitation data to the desired organizational units, we convert the xarray Dataset to a Pandas DataFrame. This makes it easier to inspect the data and prepare it for subsequent post-processing:

dataframe = ds_org_units.to_dataframe().reset_index()
dataframe
Loading...

Since CHIRPS precipitation is already recorded in the desired millimeter import units, no unit conversion or other post-processing is needed.

Create DHIS2 payload

At this point we have the final data that we want to import into DHIS2. In order to submit the data to DHIS2 we first have to convert the data a standardized JSON format, which can be done with the help of the dhis2eo library:

print(f"Creating payload with {len(dataframe)} values...")
payload = dataframe_to_dhis2_json(
    df=dataframe,
    org_unit_col="id",
    period_col="time",
    value_col=IMPORT_VALUE_COL,
    data_element_id=DHIS2_DATA_ELEMENT_ID,
)
payload['dataValues'][:3]
Creating payload with 1989 values...
[{'orgUnit': 'O6uvpzGd5pu', 'period': '20250701', 'value': '7.1407991762', 'dataElement': 'UKuEMLLnoQI'}, {'orgUnit': 'fdc6uOvgoji', 'period': '20250701', 'value': '11.684088448', 'dataElement': 'UKuEMLLnoQI'}, {'orgUnit': 'lc3eMKXaEfw', 'period': '20250701', 'value': '4.6883696756', 'dataElement': 'UKuEMLLnoQI'}]

Import to DHIS2

print(f"Importing payload into DHIS2 (dryrun={DHIS2_DRY_RUN})...")
res = client.post("/api/dataValueSets", json=payload, params={"dryRun": str(DHIS2_DRY_RUN).lower()})
print(f'Result: {res["response"]["importCount"]}')
Importing payload into DHIS2 (dryrun=True)...
Result: {'imported': 1988, 'updated': 0, 'ignored': 0, 'deleted': 0}

We have now successfully completed a full workflow for downloading, postprocessing, aggegating, and importing daily CHIRPS v3 precipitation data into DHIS2.