Visualizations + dashboards¶
VisualizationsAccessor on Dhis2Client.visualizations + DashboardsAccessor on Dhis2Client.dashboards cover the authoring surface over /api/visualizations and /api/dashboards. VisualizationSpec is a typed builder that turns chart type + data elements + periods + org units + dimensional placement into a full Visualization that DHIS2's metadata importer accepts. DashboardSlot models the 60-unit-wide grid placement used by DashboardsAccessor.add_item.
Generic CRUD remains on client.resources.visualizations and client.resources.dashboards (the generated accessors). The helpers here layer the workflow pieces — spec-driven creation, clone, add/remove dashboard items — that the bare generated API forces callers to hand-roll.
VisualizationSpec — builder over the generated Visualization¶
Visualization is the generated model — emitted from DHIS2's OpenAPI schema with ~70 fields covering every knob the Data Visualizer app writes (plus DHIS2 bookkeeping: created, lastUpdated, href, access, favorites, translations). Authoring a chart by populating that model directly is tedious.
VisualizationSpec is the authoring shape — a frozen pydantic model whose fields are the tiny subset the caller actually supplies: name, viz_type, data_elements / indicators / program_indicators, periods, organisation_units, optional dimensional placement overrides, optional legend_set. VisualizationsAccessor.create_from_spec calls .build() internally to materialise the spec into the full typed Visualization that DHIS2's metadata importer accepts.
The spec exists because the wire shape needs transformation the generated Visualization doesn't carry on its own — chart-type-aware rows / columns / filters defaults driven off viz_type (see Dimensional placement below), and RelativePeriod enum fan-out into the 45 individual boolean fields DHIS2 exposes on Visualization.relativePeriods. Plain keyword args on accessor.create(...) would push both jobs onto every caller. Same pattern as MapSpec / MapLayerSpec / LegendSetSpec / LegendSpec / OptionSpec — see the Legend sets doc for the full spec-vs-generated-model cross-reference table and the rule for when reaching for a spec is the right call.
Dimensional placement¶
Every Visualization distributes the three DHIS2 analytics dimensions — dx (data), pe (period), ou (org unit) — across three slots:
rows— category axis on charts, left side of pivot tablescolumns— series (legend colours) on charts, top of pivot tablesfilters— narrows to specific items without occupying the canvas
VisualizationSpec._resolve_placement() picks sensible defaults per VisualizationType:
| Type family | rows | columns | filters |
|---|---|---|---|
LINE / COLUMN / STACKED_COLUMN / BAR / STACKED_BAR / AREA / RADAR / PIE |
pe |
ou |
dx |
PIVOT_TABLE |
ou |
pe |
dx |
SINGLE_VALUE |
(empty) | dx |
pe, ou |
Override any slot explicitly via category_dimension / series_dimension / filter_dimension when the data shape needs a different layout.
Why everything goes through /api/metadata¶
A direct PUT /api/visualizations/{uid} with rowDimensions / columnDimensions / filterDimensions set does not populate the derived rows / columns / filters collections that DHIS2 renders from. DHIS2 silently accepts the PUT, stores empty axes, and the dashboard app surfaces "A end date was not specified" or an empty grid. Routing through POST /api/metadata?importStrategy=CREATE_AND_UPDATE runs the same importer the UI does and expands the dimension selectors into the axes DHIS2 reads at render time. VisualizationsAccessor.create_from_spec and DashboardsAccessor.add_item both take that path; don't bypass them.
Related¶
- Analytics —
client.analytics.query(...)hits the same dimension model (dx,pe,ou) that powers visualizations. Every saved Visualization is essentially a persisted analytics query with a chart type + axis placement attached. - Client tutorial — visualizations guide — end-to-end walkthrough covering spec-driven authoring, clone, dashboard composition, and the screenshot path.
visualizations
¶
Visualization authoring helpers — Dhis2Client.visualizations.
DHIS2 Visualization is one of the richest schemas on the instance —
every combination of chart type, axis placement, legend, periods, and
data dimensions is one model. Most day-to-day authoring only touches a
small subset: the chart type, one or more data elements, a period
selection, an org-unit set, and which of those three dimensions lands on
the category axis / series / filter.
This module layers three things over the generated
client.resources.visualizations CRUD accessor:
VisualizationSpec— a typed builder covering the common authoring surface with sensible defaults per chart type. Produces a fullVisualizationready to POST via/api/metadata.VisualizationsAccessor—Dhis2Client.visualizations— provideslist / get / create_from_spec / clone / delete, all round-tripping typed models.DashboardSlot+ helpers used byDhis2Client.dashboards.add_item(seedashboards.py).
Dimensional placement cheat-sheet¶
DHIS2 visualizations have three dimensions — dx (data), pe (period),
ou (org unit) — distributed across three slots:
rows(category axis on charts; left side of a pivot)columns(series on charts; top of a pivot)filters(narrow to single value(s); invisible on the canvas)
VisualizationSpec._resolve_placement() picks sensible defaults per
VisualizationType:
- LINE / COLUMN / STACKED_COLUMN / BAR / STACKED_BAR / AREA / RADAR /
PIE:
rows=[pe],columns=[ou],filters=[dx]— time runs along the x-axis, one series per org unit, single data element filter. - PIVOT_TABLE:
rows=[ou],columns=[pe],filters=[dx]— the shape DHIS2 UIs ship as the default pivot layout. - SINGLE_VALUE:
rows=[],columns=[dx],filters=[pe, ou]— grid collapses to one cell so the KPI tile renders a single number.
Callers override any of those via category_dimension /
series_dimension / filter_dimension when the data shape needs a
different layout (e.g. one line per data element).
Why POST through /api/metadata¶
A direct PUT /api/visualizations/{uid} with rowDimensions /
columnDimensions / filterDimensions set does not populate the
derived rows / columns / filters collections — DHIS2 silently
accepts the PUT and stores empty axes, which the dashboard app
surfaces as "A end date was not specified" or an empty grid. Routing
through /api/metadata?importStrategy=CREATE_AND_UPDATE runs the
same importer the UI does and expands the dimension selectors into
the axes DHIS2 reads at render time. The accessor always takes that
path; don't bypass it.
Classes¶
VisualizationSpec
¶
Bases: BaseModel
Typed builder for the common visualization shapes DHIS2 exposes.
Captures the high-value authoring surface — chart type, data
elements, periods, org units, and dimensional placement — and
produces a full Visualization via to_visualization(). Use
client.visualizations.create_from_spec(spec) to round-trip
through /api/metadata (the only path that fully populates
DHIS2's derived axes — see the module docstring).
Source code in packages/dhis2w-client/src/dhis2w_client/visualizations.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 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 | |
Functions¶
to_visualization()
¶
Materialise the typed Visualization DHIS2's metadata importer accepts.
Source code in packages/dhis2w-client/src/dhis2w_client/visualizations.py
VisualizationsAccessor
¶
Dhis2Client.visualizations — workflow helpers over /api/visualizations.
Source code in packages/dhis2w-client/src/dhis2w_client/visualizations.py
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 | |
Functions¶
__init__(client)
¶
list_all(*, viz_type=None)
async
¶
List every Visualization, optionally narrowed by type. Sorted by name.
Source code in packages/dhis2w-client/src/dhis2w_client/visualizations.py
get(uid)
async
¶
Fetch one Visualization with axes + data dimensions resolved inline.
Source code in packages/dhis2w-client/src/dhis2w_client/visualizations.py
create_from_spec(spec)
async
¶
Build a Visualization from a spec and POST via /api/metadata.
The metadata importer expands rowDimensions / columnDimensions
/ filterDimensions into the derived rows / columns /
filters collections DHIS2 actually renders from. A direct
PUT /api/visualizations/{uid} skips that expansion and stores
empty axes — don't take that shortcut.
Source code in packages/dhis2w-client/src/dhis2w_client/visualizations.py
clone(source_uid, *, new_name, new_uid=None, new_description=None)
async
¶
Duplicate an existing Visualization with a fresh UID + new name.
Copies the full axes / data dimensions / period / org-unit
selection so the clone renders identically. Reset any display
overrides (title / subtitle) by passing new_description=...
explicitly — the source's description carries over otherwise.
Source code in packages/dhis2w-client/src/dhis2w_client/visualizations.py
Functions¶
dashboards
¶
Dashboard assembly helpers — Dhis2Client.dashboards.
DHIS2 Dashboard carries an ordered list of DashboardItems, each
slotted on a 60-unit-wide grid via explicit x / y / width / height.
The generated CRUD accessor (client.resources.dashboards) covers
basic lifecycle — this module layers workflow helpers that the
dashboard app's UI exposes but the raw API forces callers to hand-roll:
add_item(dashboard_uid, viz_uid, ...)— append one visualization item to an existing dashboard, read-modify-write against/api/metadata. Auto-stacks below existing items whenyis omitted.DashboardSlot— typed x/y/width/height bundle; passed through toadd_itemwhen the caller wants explicit placement.
The "auto-stack below existing" heuristic scans the dashboard's
current items, finds the largest y + height, and drops the new item
at that y. Callers who want tiling layouts (two charts side-by-side)
set x + width explicitly and share a y.
Classes¶
DashboardSlot
¶
Bases: BaseModel
Grid placement for one dashboard item — x/y/width/height + shape.
The DHIS2 grid is 60 units wide; heights stack freely. Use NORMAL
shape for blocks that sit alongside neighbours, FULL_WIDTH when
the item spans the entire row and nothing else shares its row, and
DOUBLE_WIDTH for 2-column layouts. The shape is rendering metadata
— actual layout comes from width + x.
Source code in packages/dhis2w-client/src/dhis2w_client/dashboards.py
DashboardsAccessor
¶
Dhis2Client.dashboards — compose dashboards from existing visualizations.
Source code in packages/dhis2w-client/src/dhis2w_client/dashboards.py
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 | |
Functions¶
__init__(client)
¶
get(uid)
async
¶
Fetch one Dashboard with every item + its referenced resource.
Source code in packages/dhis2w-client/src/dhis2w_client/dashboards.py
list_all()
async
¶
List every Dashboard on the instance, sorted by name.
Source code in packages/dhis2w-client/src/dhis2w_client/dashboards.py
add_item(dashboard_uid, target_uid, *, kind='visualization', slot=None, item_uid=None)
async
¶
Append one metadata-backed item (viz / map / event chart / …) to a dashboard.
kind picks which DHIS2 DashboardItem reference field carries
the UID — "visualization" (default), "map",
"eventVisualization", "eventChart", "eventReport". The
type enum on the item is set automatically from kind.
When slot is omitted the new item stacks below every existing
item: y is computed as max(existing.y + existing.height) so
nothing overlaps. Supply a DashboardSlot when you need
side-by-side tiling (share y, split x + width).
Returns the full updated Dashboard. The PUT uses /api/metadata
to route through the same importer the UI does, so derived
fields populate correctly.
Source code in packages/dhis2w-client/src/dhis2w_client/dashboards.py
remove_item(dashboard_uid, item_uid)
async
¶
Remove one dashboardItem by its UID and PUT the dashboard back.