Aggregate data values¶
DataValue and DataValueSet — the typed wire shapes for DHIS2's aggregate-data path (/api/dataValueSets GET / POST and the per-value /api/dataValues endpoint). Pairs with Data values (streaming), which exposes the same shape via client.data_values.stream(...) for very large imports.
CompleteDataSetRegistration and CompleteDataSetRegistrations are the sibling completeness resource: DHIS2 records whether a data set is finished for a period separately from the values, under the same (dataSet, period, organisationUnit, attributeOptionCombo) key, at /api/completeDataSetRegistrations.
When to reach for it¶
- Pushing a small or medium batch of aggregate values from a script (CSV-to-DHIS2 sync, ETL pipeline, integration test fixture).
- Reading values back to verify a write landed or to drive an analytics-side check.
- Building the typed payload the streaming accessor and the bulk-grouped helper both consume.
- Marking a data set complete for a period once its values are in.
Worked example — typed write, then read-back¶
from dhis2w_client import DataValue, DataValueSet
from dhis2w_core.client_context import open_client
from dhis2w_core.profile import profile_from_env
async with open_client(profile_from_env()) as client:
# Push two values. `import_grouped_by_dataset` is the cross-version
# write path (required on v43 BUGS #35, accepted on v41 + v42). The
# typed `DataValue`s are validated by pydantic before they hit the wire.
values = [
DataValue(
dataElement="fbfJHSPpUQD",
period="202604",
orgUnit="ImspTQPwCqd",
categoryOptionCombo="HllvX50cXC0",
attributeOptionCombo="HllvX50cXC0",
value="42",
),
DataValue(
dataElement="fbfJHSPpUQD",
period="202605",
orgUnit="ImspTQPwCqd",
categoryOptionCombo="HllvX50cXC0",
attributeOptionCombo="HllvX50cXC0",
value="43",
),
]
# `import_grouped_by_dataset` POSTs one envelope per DataSet group
# (required on v43 BUGS #35). Returns a list — one WebMessageResponse
# per POST. Aggregate the per-envelope counts to get a total.
envelopes = await client.data_values.import_grouped_by_dataset(values)
total_imported = sum((env.import_count().imported if env.import_count() else 0) for env in envelopes)
print(f"posted {len(envelopes)} group(s) total imported={total_imported}")
for env in envelopes:
print(f" status={env.status} message={env.message!r}")
# Read back. `/api/dataValueSets` returns the typed DataValueSet shape;
# validate the raw dict through the pydantic model.
raw = await client.get_raw(
"/api/dataValueSets",
params={"dataSet": "lyLU2wR22tC", "period": "202604", "orgUnit": "ImspTQPwCqd"},
)
dvs = DataValueSet.model_validate(raw)
for v in dvs.dataValues or []:
print(f" DE={v.dataElement} pe={v.period} ou={v.orgUnit} value={v.value}")
Worked example — mark a data set complete for a period¶
from dhis2w_client import CompleteDataSetRegistration, CompleteDataSetRegistrations
from dhis2w_core.client_context import open_client
from dhis2w_core.profile import profile_from_env
async with open_client(profile_from_env()) as client:
# Register completeness only once the values are in: the claim is about
# data DHIS2 has taken, and a claim about data it refused would be false.
registrations = CompleteDataSetRegistrations(
completeDataSetRegistrations=[
CompleteDataSetRegistration(
dataSet="BfMAe6Itzgt",
period="202604",
organisationUnit="ImspTQPwCqd",
# Omit attributeOptionCombo on a default-combo data set - DHIS2 fills it.
date="2026-05-02",
completed=True,
)
]
)
answer = await client.post_raw(
"/api/completeDataSetRegistrations",
registrations.model_dump(by_alias=True, exclude_none=True, mode="json"),
)
print(answer["response"]["importCount"]) # {'imported': 1, ...}
# Read the tuple back. DHIS2 answers `{}` - not an empty list - when it
# holds no registration for it.
stored = await client.get_raw(
"/api/completeDataSetRegistrations",
params={"dataSet": "BfMAe6Itzgt", "period": "202604", "orgUnit": "ImspTQPwCqd"},
)
print(stored.get("completeDataSetRegistrations", []))
Registering a tuple DHIS2 already holds counts updated rather than conflicting, so re-running the same registration is safe.
Worked example — read completeness back¶
client.complete_data_set_registrations.export is the read side of the same resource, shaped like client.data_values.export: one id or a sequence for data_set / period / org_unit, children=True for the subtree, and a start_date / end_date range in place of a period list. created and last_updated narrow to registrations filed or touched since a date.
async with open_client(profile_from_env()) as client:
envelope = await client.complete_data_set_registrations.export(
data_set="BfMAe6Itzgt",
org_unit="ImspTQPwCqd",
children=True,
start_date="2026-01-01",
end_date="2026-06-30",
)
for registration in envelope.completeDataSetRegistrations:
print(
f"{registration.period} {registration.organisationUnit} completed {registration.date} by {registration.storedBy}"
)
When to use which write path¶
import_grouped_by_dataset(values) is the safe cross-version default. It pre-fetches each DataElement's DataSet membership and POSTs one {"dataSet": …, "dataValues": [...]} envelope per group — required on DHIS2 v43 for any DE that belongs to multiple DataSets (BUGS #35: v43 rejects mixed batches with 409 E8002). v41 + v42 accept the same envelope shape, so the call is portable.
client.data_values.stream(values, ...) is the streaming alternative for very large imports — wraps the values as an async-byte stream so httpx2 doesn't have to materialise the full payload in memory.
Related examples¶
-
examples/client/complete_data_set_registrations_read.py— every registration filed for one data set below the root over a date range. -
examples/client/push_data_value.py— minimal single-value push. examples/client/stream_data_values.py— streaming reads, four shapes (bytes, sync generator, Path/CSV, 1000-row file with timing).examples/client/aggregate_bulk_grouped.py— the grouped bulk path.
aggregate
¶
Typed models for DHIS2 aggregate data values (shim over generated/v43/oas).
Covers the /api/dataValueSets GET response (a DataValueSet envelope
containing a list of DataValues). The corresponding POST/import path
returns a WebMessageResponse (see dhis2w_client/envelopes.py).
Covers the sibling completeness resource too: CompleteDataSetRegistration is
the row /api/completeDataSetRegistrations files under the same
(dataSet, period, organisationUnit, attributeOptionCombo) key the values ride,
and CompleteDataSetRegistrations is the envelope a batch of them posts as.
Distinct from the generated DataElement / DataSet / CategoryOptionCombo
metadata models (those come out of /api/schemas codegen) — these describe
the runtime values captured against that metadata. OpenAPI ships both
shapes under components/schemas/{DataValue,DataValueSet}.
Classes¶
DataValue
¶
Bases: BaseModel
OpenAPI schema DataValue.
Source code in packages/dhis2w-client/src/dhis2w_client/generated/v43/oas/data_value.py
DataValueSet
¶
Bases: BaseModel
OpenAPI schema DataValueSet.
Source code in packages/dhis2w-client/src/dhis2w_client/generated/v43/oas/data_value_set.py
CompleteDataSetRegistration
¶
Bases: BaseModel
One statement that a data set is reported complete for a period, an organisation unit, and a combo.
The key is the same four facts /api/dataValueSets files values under, and completeness is a
separate claim about them: the values are what was reported, and this is the reporter saying the
report is finished. DHIS2 fills attributeOptionCombo with the default combo, date with today,
and storedBy with the authenticated user when the write names none of them.
Hand-written rather than generated: the OpenAPI document declares
/api/completeDataSetRegistrations with an untyped request body and ships no component schema for
the row it carries, so there is nothing under generated/v{41,42,43}/oas to import (BUGS.md 80).
Source code in packages/dhis2w-client/src/dhis2w_client/v43/aggregate.py
Attributes¶
date = None
class-attribute
instance-attribute
¶
The day the report was completed, as YYYY-MM-DD. DHIS2 stores today's date when none is given.
storedBy = None
class-attribute
instance-attribute
¶
Who reported it complete, stored verbatim - DHIS2 does not check it against its user table.
CompleteDataSetRegistrations
¶
Bases: BaseModel
The envelope POST /api/completeDataSetRegistrations reads a batch of registrations from.
Source code in packages/dhis2w-client/src/dhis2w_client/v43/aggregate.py
complete_data_set_registrations
¶
Read access to DHIS2 dataset completeness — client.complete_data_set_registrations.
GET /api/completeDataSetRegistrations returns the completeness claims filed
against the same (dataSet, period, organisationUnit, attributeOptionCombo) key
/api/dataValueSets files values under. The write side posts a
CompleteDataSetRegistrations envelope back; this accessor reads it and parses
the same typed envelope, mirroring client.data_values.export.
Classes¶
CompleteDataSetRegistrationsAccessor
¶
Dhis2Client.complete_data_set_registrations — read /api/completeDataSetRegistrations.
Source code in packages/dhis2w-client/src/dhis2w_client/v43/complete_data_set_registrations.py
Methods:¶
__init__(client)
¶
Bind to the sharing client — reuses its auth + HTTP pool for every request.
export(*, data_set=None, period=None, org_unit=None, children=False, start_date=None, end_date=None, created=None, last_updated=None, extra_params=None)
async
¶
GET /api/completeDataSetRegistrations and return the parsed envelope.
The read side of the completeness resource, shaped like
client.data_values.export. data_set, period, and org_unit each
accept a single id or a sequence of ids, and repeat as
dataSet=/period=/orgUnit= query params the way DHIS2 expects.
children=True->children=true: include the org units below eachorg_unit.start_date/end_date(YYYY-MM-DD): select by date range instead of byperiod.created(YYYY-MM-DD): return only registrations filed on or after then.last_updated(YYYY-MM-DD): return only registrations touched since then.extra_paramscovers the rest of the surface (idScheme,orgUnitIdScheme,dataSetIdScheme,attributeOptionComboIdScheme, ...). Pass a flat mapping or a list of 2-tuples.
Buffers the whole response into a typed CompleteDataSetRegistrations.
Raises Dhis2ApiError on 4xx / 5xx.