Tracker schema¶
The authoring flip side of d2w tracker register / enroll / add-event. DHIS2's tracker writes need a schema on the instance: the TrackedEntityType that names the kind of subject, and the TrackedEntityAttributes that describe the fields captured per enrolled TEI. Two accessors cover the leaf half of tracker-schema CRUD:
| Accessor | API path | Purpose |
|---|---|---|
client.tracked_entity_attributes |
/api/trackedEntityAttributes |
Atomic fields on a TEI (National ID, Given Name, DOB, …). CRUD + rename + common toggles (unique, generated, confidential, inherit, pattern). |
client.tracked_entity_types |
/api/trackedEntityTypes |
The kind of TEI (Person, Case, Animal). CRUD + ordered attribute linkage through trackedEntityTypeAttributes[]. |
client.programs |
/api/programs |
Tracker container. Binds a TrackedEntityType, a set of TEAs on the enrollment form, a CategoryCombo, and the OUs that can capture. CRUD + add_attribute / remove_attribute for PTEA linkage + add_organisation_unit / remove_organisation_unit for OU scope. |
client.program_stages |
/api/programStages |
Inner tracker-schema layer. Each stage owns an ordered programStageDataElements[] list (a join table with compulsory / displayInReports / allowFutureDate flags). CRUD + add_element / remove_element / reorder. |
Scope¶
This page covers the full tracker-schema authoring chain: leaf resources (TrackedEntityAttribute + TrackedEntityType), the middle layer (Program + programTrackedEntityAttributes[]), and the inner layer (ProgramStage + programStageDataElements[]). Optional ProgramStageSection grouping (rarely used in the field) is still unauthored — reach for metadata patch if you need it.
TETA join table¶
Wiring a TEA onto a TET isn't a simple ref list — the link is a trackedEntityTypeAttributes[] entry that carries mandatory, searchable, and displayInList flags. The accessor's add_attribute / remove_attribute helpers round-trip the full TET, mutate the list, and PUT it back so those flags travel without a dedicated endpoint:
async with Dhis2Client(...) as client:
national_id = await client.tracked_entity_attributes.create(
name="National ID",
short_name="NatID",
unique=True,
generated=True,
pattern="RANDOM(#######)",
)
person = await client.tracked_entity_types.create(
name="Person",
short_name="Person",
allow_audit_log=True,
feature_type="NONE",
)
await client.tracked_entity_types.add_attribute(
person.id,
national_id.id,
mandatory=True,
searchable=True,
)
Self-ref stripping¶
DHIS2's /api/trackedEntityTypes/{uid} read embeds trackedEntityTypeAttributes[].trackedEntityType = {id: <parent>} even though that field is the inverse side the importer rejects on PUT. The accessor strips it automatically before every update, mirroring the DataSet + DataSetElement workaround (BUGS tracker parity — same shape as _strip_self_ref_from_dse).
unique + generated + pattern¶
DHIS2 supports auto-generated attribute values for registration:
unique=Truemakes the value unique across the instance (National ID, passport number).generated=True+patterntogether mean DHIS2 auto-fills the value when a new TEI is registered.- Common patterns:
"RANDOM(#######)"for a 7-digit random suffix,"#(ORGUNIT)(RANDOM)"to prefix the TEI's OU.
No *Spec builder¶
Same call as every other authoring accessor — keyword args. Continues the spec-audit data point.
CLI¶
# TrackedEntityAttribute
d2w metadata tracked-entity-attributes create \
--name "National ID" --short-name NatID --value-type TEXT \
--unique --generated --pattern "RANDOM(#######)"
# TrackedEntityType + attribute linkage
d2w metadata tracked-entity-types create \
--name Person --short-name Person --allow-audit-log --feature-type NONE
d2w metadata tracked-entity-types add-attribute <TET_UID> <TEA_UID> --mandatory --searchable
Every list has an ls alias; every destructive verb accepts --yes / -y.
MCP¶
12 tools: metadata_tracked_entity_attribute_* (list / get / create / rename / delete), metadata_tracked_entity_type_* (list / get / create / rename / add-attribute / remove-attribute / delete).
Using them with tracker writes¶
The point of authoring these here is to make the tracker-write plugin usable end-to-end from CLI alone:
# 1. author the schema
d2w metadata tracked-entity-types create --name Person --short-name Person ...
# 2. use it
d2w tracker register --type <TET_UID> --ou <OU_UID> ...
See the tracker plugin for the write-side reference.
Program authoring¶
A Program binds everything together. Two flavours: WITH_REGISTRATION (tracker — requires a TET, enrolls individual TEIs) and WITHOUT_REGISTRATION (event program — captures anonymous events directly).
async with Dhis2Client(...) as client:
program = await client.programs.create(
name="Antenatal care",
short_name="ANC",
program_type="WITH_REGISTRATION",
tracked_entity_type_uid=person.id,
display_incident_date=True,
only_enroll_once=True,
)
await client.programs.add_attribute(
program.id,
national_id.id,
mandatory=True,
searchable=True,
sort_order=1,
)
await client.programs.add_organisation_unit(program.id, root_ou_uid)
PTEA join table + mergeMode=REPLACE quirk¶
programTrackedEntityAttributes[] is a nested join table (DHIS2's wire name is trackedEntityAttribute on the entry, not attribute). DHIS2 v42's PUT /api/programs/{uid} treats nested-list updates additively by default — items omitted from the payload are NOT removed. The accessor always passes ?mergeMode=REPLACE on PUT so remove_attribute behaves symmetrically.
OU scoping¶
add_organisation_unit / remove_organisation_unit use DHIS2's per-item shortcut (POST/DELETE /api/programs/{program}/organisationUnits/{ou}) — avoids the round-trip PUT entirely.
tracked_entity_attributes
¶
TrackedEntityAttribute authoring — Dhis2Client.tracked_entity_attributes.
TrackedEntityAttributes are the atomic fields on a tracked entity
(National ID, Given Name, Date of Birth, …). They get wired into a
TrackedEntityType (via trackedEntityTypeAttributes[]) and/or into
programs (via programTrackedEntityAttributes[]).
This module adds the CRUD primitives — the run / write side lives on
Dhis2Client.tracker (the existing tracker write plugin).
create(...)— named kwargs over the minimal required subset (name,short_name,value_type,aggregation_type) plus the optional references (option_set_uid,legend_set_uids) and the common toggles (unique,generated,confidential,inherit,display_in_list_no_program,pattern).update(tea)/rename(uid, ...)— standard edit pathways.delete(uid)— DHIS2 rejects deletes on TEAs in use.
No *Spec builder — continues the spec-audit data point.
Classes¶
TrackedEntityAttribute
¶
Bases: BaseModel
Generated model for DHIS2 TrackedEntityAttribute.
DHIS2 Tracked Entity Attribute - persisted metadata (generated from /api/schemas at DHIS2 v43).
API endpoint: /api/trackedEntityAttributes.
Field Field(description=...) entries flag DHIS2 semantics the bare
type can't capture: which side of a relationship owns the link
(writable) vs the inverse side (ignored by the API), uniqueness
constraints, and length bounds.
Source code in packages/dhis2w-client/src/dhis2w_client/generated/v43/schemas/tracked_entity_attribute.py
TrackedEntityAttributesAccessor
¶
Dhis2Client.tracked_entity_attributes — CRUD over /api/trackedEntityAttributes.
Source code in packages/dhis2w-client/src/dhis2w_client/v43/tracked_entity_attributes.py
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 | |
Methods:¶
__init__(client)
¶
list_all(*, value_type=None, page=1, page_size=50)
async
¶
Page through TrackedEntityAttributes, optionally filtered by valueType.
Source code in packages/dhis2w-client/src/dhis2w_client/v43/tracked_entity_attributes.py
get(uid)
async
¶
Fetch one TrackedEntityAttribute with its optionSet + legendSet refs inline.
Source code in packages/dhis2w-client/src/dhis2w_client/v43/tracked_entity_attributes.py
create(*, name, short_name, value_type=ValueType.TEXT, aggregation_type=AggregationType.NONE, option_set_uid=None, legend_set_uids=None, unique=False, generated=False, confidential=False, inherit=False, display_in_list_no_program=False, orgunit_scope=False, pattern=None, field_mask=None, code=None, form_name=None, description=None, uid=None)
async
¶
Create a TrackedEntityAttribute.
value_type defaults to TEXT; switch to NUMBER, DATE,
PHONE_NUMBER, etc. via the ValueType StrEnum. unique=True
makes the value unique across the instance (National ID,
passport number). generated=True + pattern lets DHIS2
auto-generate the value when a TEI is registered (common for
case IDs). option_set_uid constrains to an option-set
picklist. confidential=True tags the attribute as sensitive
for audit + sharing policies.
Source code in packages/dhis2w-client/src/dhis2w_client/v43/tracked_entity_attributes.py
update(attribute)
async
¶
PUT an edited TrackedEntityAttribute back. attribute.id must be set.
Source code in packages/dhis2w-client/src/dhis2w_client/v43/tracked_entity_attributes.py
rename(uid, *, name=None, short_name=None, form_name=None, description=None)
async
¶
Partial-update shortcut — read, mutate the label fields, PUT.
Source code in packages/dhis2w-client/src/dhis2w_client/v43/tracked_entity_attributes.py
delete(uid)
async
¶
Delete a TrackedEntityAttribute — DHIS2 rejects deletes on TEAs wired into a TET or program.
Source code in packages/dhis2w-client/src/dhis2w_client/v43/tracked_entity_attributes.py
tracked_entity_types
¶
TrackedEntityType authoring — Dhis2Client.tracked_entity_types.
A TrackedEntityType is the kind of subject a tracker program enrols
(Person, Case, Animal). Each TET carries its own set of attributes
via trackedEntityTypeAttributes[] — a join table that flags which
TEAs are mandatory, searchable, and visible in the enrollment UI.
create(...)— named kwargs over the minimal required subset (name,short_name) plus common knobs (description,allow_audit_log,feature_type,min_attributes_required_to_search).add_attribute(tet_uid, tea_uid, *, mandatory=False, searchable=False, display_in_list=True)— wire a TEA onto the TET by round-tripping the full TET, mutatingtrackedEntityTypeAttributes[], and PUTing back. Mirrors DataSet + DataSetElement.remove_attribute(tet_uid, tea_uid)— drops the TEA ref from the TET.rename(uid, ...)/delete(uid)— standard edit pathways.
No *Spec builder — continues the spec-audit data point.
Classes¶
TrackedEntityType
¶
Bases: BaseModel
Generated model for DHIS2 TrackedEntityType.
DHIS2 Tracked Entity Type - persisted metadata (generated from /api/schemas at DHIS2 v43).
API endpoint: /api/trackedEntityTypes.
Field Field(description=...) entries flag DHIS2 semantics the bare
type can't capture: which side of a relationship owns the link
(writable) vs the inverse side (ignored by the API), uniqueness
constraints, and length bounds.
Source code in packages/dhis2w-client/src/dhis2w_client/generated/v43/schemas/tracked_entity_type.py
TrackedEntityTypesAccessor
¶
Dhis2Client.tracked_entity_types — CRUD + attribute-linkage helpers.
Source code in packages/dhis2w-client/src/dhis2w_client/v43/tracked_entity_types.py
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 | |
Methods:¶
__init__(client)
¶
list_all(*, page=1, page_size=50)
async
¶
Page through TrackedEntityTypes.
Source code in packages/dhis2w-client/src/dhis2w_client/v43/tracked_entity_types.py
get(uid)
async
¶
Fetch one TrackedEntityType with its TEA link table resolved inline.
Source code in packages/dhis2w-client/src/dhis2w_client/v43/tracked_entity_types.py
create(*, name, short_name, description=None, code=None, form_name=None, allow_audit_log=None, feature_type=None, min_attributes_required_to_search=None, max_tei_count_to_return=None, uid=None)
async
¶
Create a TrackedEntityType.
allow_audit_log enables the per-TEI audit trail (required for
compliance workflows). feature_type governs the geometry
attached to each TEI (NONE / POINT / POLYGON).
min_attributes_required_to_search sets the enrollment-search
minimum attribute count; higher values reduce accidental
full-table scans.
Source code in packages/dhis2w-client/src/dhis2w_client/v43/tracked_entity_types.py
update(tet)
async
¶
PUT an edited TrackedEntityType back. tet.id must be set.
Source code in packages/dhis2w-client/src/dhis2w_client/v43/tracked_entity_types.py
rename(uid, *, name=None, short_name=None, form_name=None, description=None)
async
¶
Partial-update shortcut — read, mutate the label fields, PUT.
Source code in packages/dhis2w-client/src/dhis2w_client/v43/tracked_entity_types.py
add_attribute(tet_uid, attribute_uid, *, mandatory=False, searchable=False, display_in_list=True)
async
¶
Wire a TrackedEntityAttribute onto the TET.
DHIS2 stores the link in trackedEntityTypeAttributes[] as a
nested join object — the accessor round-trips the full TET,
appends a new entry, and PUTs it back.
Source code in packages/dhis2w-client/src/dhis2w_client/v43/tracked_entity_types.py
remove_attribute(tet_uid, attribute_uid)
async
¶
Drop a TrackedEntityAttribute from the TET's link table.
Source code in packages/dhis2w-client/src/dhis2w_client/v43/tracked_entity_types.py
delete(uid)
async
¶
Delete a TrackedEntityType — DHIS2 rejects deletes on TETs in use by enrolled TEIs.
Source code in packages/dhis2w-client/src/dhis2w_client/v43/tracked_entity_types.py
Functions:¶
programs
¶
Program authoring — Dhis2Client.programs.
A DHIS2 Program is the tracker container: it binds a
TrackedEntityType (for WITH_REGISTRATION programs), a set of
TrackedEntityAttributes shown on the enrollment form, and the
OrganisationUnits that can capture enrollments or events. Programs
come in two flavours:
- WITH_REGISTRATION — tracker programs. Need a
trackedEntityTypeand register individual TEIs before capturing events. - WITHOUT_REGISTRATION — event programs. Capture anonymous events directly; no TET required.
This module is the authoring flip side of the existing tracker-write
plugin (d2w tracker register / enroll / add-event). The leaf half
(TET + TEA) lives in tracked_entity_types.py /
tracked_entity_attributes.py; the inner layer (ProgramStage + PSDE)
ships in a follow-up.
Surface:
- create(...) — named kwargs over the minimal required subset
(name, short_name, program_type) plus the refs + common knobs.
For WITH_REGISTRATION, tracked_entity_type_uid is required.
- add_attribute(program_uid, tea_uid, ...) — wire a TEA into the
enrollment form via the programTrackedEntityAttributes[] join
table.
- add_organisation_unit(program_uid, ou_uid) — scope the program
to an OU. Tracker writes need at least one OU in scope to register.
- rename(uid, ...) / delete(uid) — standard pathways.
- set_labels(uid, ...) — v43-only: customise the enrollmentsLabel
/ eventsLabel / programStagesLabel UI strings (Capture / Tracker
Capture apps).
- set_change_log_enabled(uid, enabled) — v43-only: flip the
enableChangeLog server-side audit toggle.
- set_enrollment_category_combo(uid, cc_uid) — v43-only: set the
enrollmentCategoryCombo alt-CC applied at enrollment time.
The three v43-only setters are deliberately split — they address unrelated concerns (UI text vs behavioural audit toggle vs data-model ref) that happen to land on the same v43 schema delta.
No *Spec builder — continues the spec-audit data point.
Classes¶
Program
¶
Bases: BaseModel
Generated model for DHIS2 Program.
DHIS2 Program - persisted metadata (generated from /api/schemas at DHIS2 v43).
API endpoint: /api/programs.
Field Field(description=...) entries flag DHIS2 semantics the bare
type can't capture: which side of a relationship owns the link
(writable) vs the inverse side (ignored by the API), uniqueness
constraints, and length bounds.
Source code in packages/dhis2w-client/src/dhis2w_client/generated/v43/schemas/program.py
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | |
ProgramsAccessor
¶
Dhis2Client.programs — CRUD + attribute + OU linkage over /api/programs.
Source code in packages/dhis2w-client/src/dhis2w_client/v43/programs.py
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 | |
Methods:¶
__init__(client)
¶
list_all(*, program_type=None, page=1, page_size=50)
async
¶
Page through Programs, optionally filtered by programType.
Source code in packages/dhis2w-client/src/dhis2w_client/v43/programs.py
get(uid)
async
¶
Fetch one Program with its PTEAs, OUs, and ProgramStage refs inline.
Source code in packages/dhis2w-client/src/dhis2w_client/v43/programs.py
create(*, name, short_name, program_type=ProgramType.WITH_REGISTRATION, tracked_entity_type_uid=None, category_combo_uid=None, description=None, code=None, form_name=None, display_incident_date=None, enrollment_date_label=None, incident_date_label=None, feature_type=None, only_enroll_once=None, select_enrollment_dates_in_future=None, select_incident_dates_in_future=None, expiry_days=None, min_attributes_required_to_search=None, max_tei_count_to_return=None, use_first_stage_during_registration=None, uid=None)
async
¶
Create a Program.
program_type=WITH_REGISTRATION (default) requires
tracked_entity_type_uid; pass WITHOUT_REGISTRATION for an
event program that skips TEI registration.
category_combo_uid defaults to the instance-wide default
combo (DHIS2 rejects programs without a CC ref).
display_incident_date + incident_date_label govern whether
the enrollment form captures an incident date distinct from
the enrollment date (required by some case-management flows).
Source code in packages/dhis2w-client/src/dhis2w_client/v43/programs.py
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 | |
update(program)
async
¶
PUT an edited Program back. program.id must be set.
DHIS2 v42's Program PUT importer treats nested-list updates
additively without mergeMode=REPLACE — items omitted from a
list aren't removed. The accessor always passes the flag so
add_attribute / remove_attribute behave symmetrically.
Source code in packages/dhis2w-client/src/dhis2w_client/v43/programs.py
rename(uid, *, name=None, short_name=None, form_name=None, description=None)
async
¶
Partial-update shortcut — read, mutate the label fields, PUT.
Source code in packages/dhis2w-client/src/dhis2w_client/v43/programs.py
set_labels(uid, *, enrollments_label=None, events_label=None, program_stages_label=None)
async
¶
Set the v43-only UI label overrides on a Program.
DHIS2 2.43 lets each Program override the default "Enrollments" / "Events" / "Program stages" UI terminology shown in the Capture and Tracker Capture apps. Useful for domain-native vocabulary — e.g. "Visits", "Encounters", "Care stages".
None-valued kwargs are left untouched on the program; pass only the labels you want to change. DHIS2 enforces 2-255 chars per label. Returns the re-fetched Program.
Source code in packages/dhis2w-client/src/dhis2w_client/v43/programs.py
set_change_log_enabled(uid, enabled)
async
¶
Toggle the v43-only enableChangeLog server-side audit flag on a Program.
When True, DHIS2 records enrollment + event change-logs surfaced via
/api/tracker/enrollments/{uid}/changeLogs,
/api/tracker/events/{event}/changeLogs, and the matching tracker
export endpoints. Default off. Behavioural switch — orthogonal to
the UI label overrides set by set_labels. Returns the re-fetched
Program.
Source code in packages/dhis2w-client/src/dhis2w_client/v43/programs.py
set_enrollment_category_combo(uid, category_combo_uid)
async
¶
Set the v43-only enrollmentCategoryCombo reference on a Program.
An alternative CategoryCombo applied specifically at enrollment time,
distinct from the Program's regular categoryCombo. Lets programs
carry one disaggregation for the Program-level metadata and a
different one for enrollment-time data capture. Returns the
re-fetched Program.
Source code in packages/dhis2w-client/src/dhis2w_client/v43/programs.py
add_attribute(program_uid, attribute_uid, *, mandatory=False, searchable=False, display_in_list=True, sort_order=None, allow_future_date=False, render_options_as_radio=False)
async
¶
Wire a TrackedEntityAttribute into the Program's enrollment form.
The PTEA join table carries the enrollment-form flags
(mandatory, searchable, displayInList, sortOrder,
allowFutureDate, renderOptionsAsRadio). Idempotent when
the TEA is already linked — the existing PTEA is left alone.
Source code in packages/dhis2w-client/src/dhis2w_client/v43/programs.py
remove_attribute(program_uid, attribute_uid)
async
¶
Drop a TrackedEntityAttribute from the Program's enrollment form.
Source code in packages/dhis2w-client/src/dhis2w_client/v43/programs.py
add_organisation_unit(program_uid, organisation_unit_uid)
async
¶
Scope the Program to an additional OrganisationUnit via the per-item POST shortcut.
Source code in packages/dhis2w-client/src/dhis2w_client/v43/programs.py
remove_organisation_unit(program_uid, organisation_unit_uid)
async
¶
Drop an OrganisationUnit from the Program scope via the per-item DELETE shortcut.
Source code in packages/dhis2w-client/src/dhis2w_client/v43/programs.py
delete(uid)
async
¶
Delete a Program — DHIS2 rejects deletes on programs with enrolled TEIs or saved events.
Source code in packages/dhis2w-client/src/dhis2w_client/v43/programs.py
ProgramStage authoring¶
Each Program owns a stage sequence (ANC 1st visit, ANC 2nd visit, …). Each stage owns an ordered programStageDataElements[] list — a join table with compulsory, displayInReports, allowFutureDate, allowProvidedElsewhere, renderOptionsAsRadio, sortOrder per entry.
stage = await client.program_stages.create(
name="ANC 1st visit",
program_uid=program.id,
sort_order=1,
repeatable=False,
min_days_from_start=0,
standard_interval=30,
)
await client.program_stages.add_element(
stage.id,
weight_de.id,
compulsory=True,
sort_order=0,
)
await client.program_stages.reorder(stage.id, [second_de.id, weight_de.id])
PSDE ordering helpers¶
add_element(stage_uid, de_uid, compulsory=..., sort_order=...)— appends a new PSDE entry with typed flags.remove_element(stage_uid, de_uid)— drops the PSDE entry; other flags on the remaining entries are preserved.reorder(stage_uid, [de_uids])— replaces the ordered list; PSDE flags are preserved for DEs that stay in the list andsortOrderis rewritten to match the new position.
mergeMode=REPLACE quirk¶
DHIS2 v42's PUT /api/programStages/{uid} treats nested-list updates additively by default (same quirk as Programs). The accessor always passes ?mergeMode=REPLACE on PUT so remove_element actually removes the PSDE entry instead of silently retaining it.
PSDE self-ref strip¶
The generated PSDE entry carries programStage = {id: <parent>} on reads, which DHIS2's importer rejects on PUT (inverse side). Stripped automatically before every update — mirrors DataSet+DSE, TET+TETA, Program+PTEA.
program_stages
¶
ProgramStage authoring — Dhis2Client.program_stages.
The inner layer of tracker schema authoring. A ProgramStage is
a stage/visit inside a Program — ANC 1st visit, ANC 2nd visit,
vaccination schedule entry, etc. Each stage owns an ordered list of
programStageDataElements[] (a join table: which DEs the stage
captures, in what order, with per-DE compulsory / displayInReports
/ allowFutureDate / allowProvidedElsewhere flags).
Ships step 3 of the tracker-schema stretch:
- Step 1 (#188) —
TrackedEntityAttribute+TrackedEntityType. - Step 2 (#189) —
Program+ PTEA + OU scope. - Step 3 (this) —
ProgramStage+programStageDataElements[].
Surface:
create(...)— kwargs over the minimal required subset (name,program_uid) plus common knobs (repeatable,auto_generate_event,min_days_from_start,sort_order,validation_strategy,feature_type).add_element(stage_uid, de_uid, *, compulsory, allow_future_date, display_in_reports, allow_provided_elsewhere, render_options_as_radio)— wire a DE into the stage's data-entry form via the PSDE join table. Round-trips the full stage + PUTs withmergeMode=REPLACE(matches the Program PUT quirk).remove_element/reorder(stage_uid, [de_uids]).rename(uid, ...)/delete(uid)— standard edit pathways.
No *Spec builder — continues the spec-audit data point.
Classes¶
ProgramStage
¶
Bases: BaseModel
Generated model for DHIS2 ProgramStage.
DHIS2 Program Stage - persisted metadata (generated from /api/schemas at DHIS2 v43).
API endpoint: /api/programStages.
Field Field(description=...) entries flag DHIS2 semantics the bare
type can't capture: which side of a relationship owns the link
(writable) vs the inverse side (ignored by the API), uniqueness
constraints, and length bounds.
Source code in packages/dhis2w-client/src/dhis2w_client/generated/v43/schemas/program_stage.py
ProgramStagesAccessor
¶
Dhis2Client.program_stages — CRUD + PSDE ordering helpers.
Source code in packages/dhis2w-client/src/dhis2w_client/v43/program_stages.py
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 | |
Methods:¶
__init__(client)
¶
list_all(*, program_uid=None, page=1, page_size=50)
async
¶
Page through ProgramStages, optionally filtered to one parent Program.
Source code in packages/dhis2w-client/src/dhis2w_client/v43/program_stages.py
list_for(program_uid)
async
¶
Return ProgramStages belonging to one Program, sorted by sortOrder.
Source code in packages/dhis2w-client/src/dhis2w_client/v43/program_stages.py
get(uid)
async
¶
Fetch one ProgramStage with its PSDE list resolved inline.
Source code in packages/dhis2w-client/src/dhis2w_client/v43/program_stages.py
create(*, name, program_uid, short_name=None, description=None, code=None, form_name=None, sort_order=None, repeatable=None, auto_generate_event=None, generated_by_enrollment_date=None, open_after_enrollment=None, block_entry_form=None, feature_type=None, period_type=None, validation_strategy=None, min_days_from_start=None, standard_interval=None, enable_user_assignment=None, pre_generate_uid=None, due_date_label=None, execution_date_label=None, event_label=None, uid=None)
async
¶
Create a ProgramStage under program_uid.
repeatable=True makes the stage reusable within one enrollment
(follow-up ANC visits, chronic-care check-ins).
auto_generate_event=True + generated_by_enrollment_date=True
tells DHIS2 to schedule an event when the enrollment is
created. min_days_from_start + standard_interval tune the
default due-date math.
Source code in packages/dhis2w-client/src/dhis2w_client/v43/program_stages.py
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 | |
update(stage)
async
¶
PUT an edited ProgramStage back. stage.id must be set.
Matches the Program PUT quirk: mergeMode=REPLACE forces
nested-list replacement so programStageDataElements[] items
omitted from the payload actually get removed instead of
silently retained.
Source code in packages/dhis2w-client/src/dhis2w_client/v43/program_stages.py
rename(uid, *, name=None, short_name=None, form_name=None, description=None)
async
¶
Partial-update shortcut — read, mutate label fields, PUT.
Source code in packages/dhis2w-client/src/dhis2w_client/v43/program_stages.py
add_element(stage_uid, data_element_uid, *, compulsory=False, allow_future_date=False, display_in_reports=True, allow_provided_elsewhere=False, render_options_as_radio=False, skip_synchronization=False, skip_analytics=False, sort_order=None)
async
¶
Wire a DataElement into the ProgramStage via the PSDE join table.
Round-trips the full stage, appends a new PSDE entry, PUTs with
mergeMode=REPLACE. Idempotent — returns early when the DE is
already attached.
Source code in packages/dhis2w-client/src/dhis2w_client/v43/program_stages.py
remove_element(stage_uid, data_element_uid)
async
¶
Drop a DataElement from the ProgramStage's PSDE list.
Source code in packages/dhis2w-client/src/dhis2w_client/v43/program_stages.py
reorder(stage_uid, data_element_uids)
async
¶
Replace the ordered programStageDataElements with exactly the given DE UIDs.
Any PSDE flags (compulsory, display_in_reports, etc.) on
dropped entries are lost. Use add_element + remove_element
for fine-grained edits; reach for reorder when the set of
attached DEs is known.
Source code in packages/dhis2w-client/src/dhis2w_client/v43/program_stages.py
delete(uid)
async
¶
Delete a ProgramStage — DHIS2 rejects deletes on stages with recorded events.