Maintenance¶
MaintenanceAccessor on Dhis2Client.maintenance — the data-integrity reader (get_integrity_report, iter_integrity_issues) plus the on-demand CategoryOptionCombo matrix regeneration (update_category_option_combos). DHIS2's other background-job triggers (analytics refresh, predictor runs, validation runs, cache clears) live in the matching plugin services and on the CLI / MCP — see Triggering analytics / monitoring refresh below for the right entry point.
When to reach for it¶
- Run DHIS2's built-in data-integrity scan (81 checks) and pull the typed report.
- Stream tagged integrity issues as they're emitted (large instances can have thousands;
iter_integrity_issuesis the streaming consumer). - Trigger COC matrix regeneration after a
CategoryCombosave on v43 (update_category_option_combos) — see alsocategory_combos.wait_for_coc_generationfor the polling helper that pairs with it.
Worked example — stream integrity issues¶
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:
# `iter_integrity_issues` is an async iterator — one `IntegrityIssueRow`
# per row as DHIS2 emits it; safe for instances with thousands of issues.
# Each row carries the owning check's metadata (`check_name`,
# `check_display_name`, `severity`) plus the typed issue itself
# (`row.issue.name`, `row.issue.id`, `row.issue.comment`).
severity_counts: dict[str, int] = {}
async for row in client.maintenance.iter_integrity_issues():
sev = row.severity or "UNKNOWN"
severity_counts[sev] = severity_counts.get(sev, 0) + 1
if severity_counts[sev] <= 3:
print(f" [{sev}] {row.check_display_name or row.check_name}: {row.issue.name} ({row.issue.id})")
print(f"total by severity: {severity_counts}")
Worked example — full integrity report (snapshot, not stream)¶
async with open_client(profile_from_env()) as client:
# Returns a typed `DataIntegrityReport` carrying `.results`, a
# `dict[str, DataIntegrityResult]` keyed by check name. Each result
# has `.name`, `.severity`, `.count`, and `.issues` (list of
# DataIntegrityIssue with `.name`, `.id`, `.comment`).
report = await client.maintenance.get_integrity_report()
print(f"{len(report.results)} checks ran")
for check_key, result in list(report.results.items())[:5]:
print(f" {check_key} severity={result.severity} issues={len(result.issues)}")
Triggering analytics / monitoring refresh¶
The accessor doesn't expose a refresh trigger directly — refresh is a plugin-service surface, not a raw-client one. Three real paths:
- CLI:
d2w maintenance refresh analytics --watch. - MCP:
maintenance_refresh_analyticstool withwatch=true. - Python via plugin service: import
dhis2w_core.v42.plugins.maintenance.service.refresh_analytics(profile, ...)directly — seeexamples/v42/client/task_polling.pyfor the full kick-off + poll-with-client.tasks.await_completionpattern.
Related examples¶
examples/v42/client/task_polling.py— kick off an analytics refresh via rawclient.post_raw("/api/resourceTables/analytics", ...)+ block on it withclient.tasks.await_completion.examples/v42/client/integrity_issues_stream.py—iter_integrity_issues+ severity histogram + early-break scan.
maintenance
¶
Typed models + client accessor for DHIS2 maintenance + data-integrity + task-notification APIs.
DataIntegrityCheck and DataIntegrityIssue come from
dhis2w_client.generated.v42.oas. DataIntegrityResult and
DataIntegrityReport stay hand-written — OpenAPI splits the result into
separate DataIntegrityDetails / DataIntegritySummary shapes, but this
module's callers want the merged view + the client-side {check_name: result}
map. Notification re-exports the OAS type so callers get typed
category: JobType, dataType: NotificationDataType, level: NotificationLevel
enums + time: datetime.
MaintenanceAccessor (bound to Dhis2Client.maintenance) exposes the
data-integrity read paths. iter_integrity_issues is the ergonomic
entry point for large runs — yields one issue at a time tagged with its
owning check's metadata, so callers don't have to walk the
{check_name: {issues: [...]}} two-level shape themselves.
Classes¶
DataIntegrityCheck
¶
Bases: BaseModel
OpenAPI schema DataIntegrityCheck.
Source code in packages/dhis2w-client/src/dhis2w_client/generated/v42/oas/data_integrity_check.py
DataIntegrityIssue
¶
Bases: BaseModel
OpenAPI schema DataIntegrityIssue.
Source code in packages/dhis2w-client/src/dhis2w_client/generated/v42/oas/data_integrity_issue.py
Notification
¶
Bases: BaseModel
OpenAPI schema Notification.
Source code in packages/dhis2w-client/src/dhis2w_client/generated/v42/oas/notification.py
JobType
¶
Bases: StrEnum
JobType.
Source code in packages/dhis2w-client/src/dhis2w_client/generated/v42/oas/_enums.py
NotificationDataType
¶
NotificationLevel
¶
Bases: StrEnum
NotificationLevel.
Source code in packages/dhis2w-client/src/dhis2w_client/generated/v42/oas/_enums.py
DataIntegrityResult
¶
Bases: BaseModel
Result of one check — populated after the async job completes.
Merges OpenAPI's DataIntegrityDetails (has issues[]) and
DataIntegritySummary (has count) into one caller-friendly shape.
Both modes populate startTime / finishedTime once the job has run;
an unrun check returns the definition block alone.
Source code in packages/dhis2w-client/src/dhis2w_client/v42/maintenance.py
DataIntegrityReport
¶
Bases: BaseModel
/api/dataIntegrity/summary or /details response — keyed by check name.
Hand-written: DHIS2 returns {check_name: result} — a client-side convenience
shape not in OpenAPI. The from_api classmethod hides the raw-dict detail.
Source code in packages/dhis2w-client/src/dhis2w_client/v42/maintenance.py
IntegrityIssueRow
¶
Bases: BaseModel
One issue from a data-integrity run, tagged with its owning check's metadata.
iter_integrity_issues yields these as a flat stream so callers can
filter / transform without walking the two-level
{check_name: {issues: [...]}} shape themselves.
Source code in packages/dhis2w-client/src/dhis2w_client/v42/maintenance.py
MaintenanceAccessor
¶
Dhis2Client.maintenance — read paths for the data-integrity surface.
Writes (kicking off a run, clearing cache) stay on the plugin-layer
service in dhis2w_core for now — those need a Profile for OAuth2
token-store keying, which the raw client doesn't know about.
Source code in packages/dhis2w-client/src/dhis2w_client/v42/maintenance.py
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 | |
Functions¶
__init__(client)
¶
Bind to the sharing client — reuses its auth + HTTP pool for every request.
get_integrity_report(*, checks=None, details=True)
async
¶
Fetch the full /api/dataIntegrity/{details|summary} report as a typed model.
details=True (the default) populates issues[] on each result;
details=False hits the cheaper /summary endpoint which returns
just counts + timing. Pass checks to narrow to specific check
names (from list_dataintegrity_checks); omit for every check
the last run produced.
Source code in packages/dhis2w-client/src/dhis2w_client/v42/maintenance.py
iter_integrity_issues(*, checks=None)
async
¶
Stream every issue from /api/dataIntegrity/details one at a time.
DHIS2's endpoint returns the whole {check_name: {issues: [...]}}
structure in one response (no server-side pagination). This helper
still buys you:
- A flat stream —
async for row in ...instead of nested loops. - Tagged rows — each yielded
IntegrityIssueRowcarries the owning check's name + display name + severity, so the caller knows the provenance without a second lookup. - Early break — stop iteration mid-stream without building the full list in memory on the Python side.
Issues yield in the order DHIS2 returns checks, then the order
of that check's issues[] list — stable across runs.
Source code in packages/dhis2w-client/src/dhis2w_client/v42/maintenance.py
update_category_option_combos()
async
¶
Trigger DHIS2 to (re)generate the CategoryOptionCombo matrix.
DHIS2 v42 auto-generated COCs whenever a CategoryCombo was saved, so callers rarely needed this. v43 changed the behavior — saving a CategoryCombo no longer triggers regeneration; the matrix stays empty until this maintenance task runs.
client.category_combos.wait_for_coc_generation calls this
helper internally before polling so the helper "just works" on
both versions. Call it directly when you need to ensure the COC
matrix is up to date for an existing combo (e.g. after appending
a category via add_category).
The endpoint is synchronous — DHIS2 walks every persisted combo, adds missing COCs, removes orphaned ones, and returns when done.