d2ql: a pipeline query + transform language over JSON-shaped data, with an embedded expression core (d2path). Pure engine (tokenizer, parser, evaluator, planner) with no DHIS2 or FHIR runtime dependency.
Project description
dhis2w-ql
d2ql — a pipeline query and transform language with an embedded expression language, d2path.
Pure engine, no DHIS2 required: it queries any JSON-shaped data — lists of dicts, Pydantic models,
local .json/.ndjson files — with a pushdown seam for backends that can answer parts of a query
natively.
This package is the language engine alone: tokenizer, recursive-descent parser, Pydantic AST,
expression evaluator, query planner, and execution engine over a source-agnostic DataSource
protocol. Its only dependency is pydantic. The DHIS2 binding (live DataSource, pushdown
compiler, CLI, MCP tools) lives in the query plugin in dhis2w-core; FHIR is a consumer of the
engine (d2path evaluates over any JSON, and the transform stage can emit FHIR resources)
rather than a dependency.
Install
uv add dhis2w-ql # or: pip install dhis2w-ql
Quickstart — d2path over your own JSON
d2path is the expression layer: path navigation, operators, and ~40 functions with collection
semantics.
from dhis2w_ql import Evaluator, parse_expression
facilities = [
{"name": "Ngelehun CHC", "level": 4, "tags": ["chc", "rural"]},
{"name": "Kailahun MCHP", "level": 4, "tags": ["mchp"]},
{"name": "Bo District", "level": 2, "tags": []},
]
expression = parse_expression('where(level = 4 and tags.count() > 0).name.select(upper())')
print(Evaluator().evaluate(expression, facilities))
# ['NGELEHUN CHC', 'KAILAHUN MCHP']
Quickstart — a full pipeline over in-memory rows
The pipeline layer adds stages (where, select, transform, order, paging, group by,
fold), named definitions, and sinks. InMemoryBinder maps resource names to row lists; any
backend can implement the same DataSource protocol and advertise which filters/ordering/paging
it can execute natively — the planner pushes that prefix down and runs the rest locally.
import asyncio
from dhis2w_ql import InMemoryBinder, QueryEngine, parse
program = parse("""
facilities
| where level = 4
| select name, level
| order name asc
| limit 10
""")
engine = QueryEngine(program, InMemoryBinder({"facilities": facilities}))
result = asyncio.run(engine.run_terminal())
print(result.rows)
# [{'name': 'Kailahun MCHP', 'level': 4}, {'name': 'Ngelehun CHC', 'level': 4}]
Programs can also read local files directly — read("facilities.json") or
read("events.ndjson") as the source — and end in a sink (>> "out.csv",
>> stdout as ndjson).
Language shape
define ActiveAggregates:
dataElements | where domainType = "AGGREGATE"
ActiveAggregates
| where name ~ "ANC"
| select id, name, categoryCombo.name as combo
| transform { code: id, label: name }
| order name asc
| limit 20
>> "elements.csv"
- Expression layer (
d2path): path navigation, operators, and functions with collection semantics — used insidewhere,select,order, andtransform. - Pipeline layer: stages separated by
|, optionally ending in a>>sink. - Definitions:
define NAME: ...anddefine function NAME(args): ...make a.d2qlfile a reusable library of named queries and helpers.
The pipeline | is the stage separator; collection union is the union() function (not the |
operator) to keep the two unambiguous.
Collection semantics in one minute
- Every expression evaluates to a collection; navigation (
a.b) flattens one level per hop. - A single-element collection collapses to its scalar in results.
- String functions (
upper(),lower(),length(),trim(),toChars()) operate on a singleton focus; map them over a collection withselect(...)—name.select(upper()). - Comparisons are existential over collections:
tags = "chc"is true when any element matches.
Documentation
- d2ql tutorial and reference
- d2path reference and the generated example catalog — 140 examples covering every function, each parsed and evaluated in CI against this engine
- Language semantics and the cookbook
- Runnable sample programs:
examples/d2ql/in the repository — every file is parse-tested in CI
The curated catalogs ship in the package: dhis2w_ql.SAMPLES (sample programs) and
dhis2w_ql.DOC_EXAMPLES (the evaluator-verified example catalog behind the docs page).
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file dhis2w_ql-1.3.0.tar.gz.
File metadata
- Download URL: dhis2w_ql-1.3.0.tar.gz
- Upload date:
- Size: 47.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
54357c9c7bc0403a944da406b2a9e864028166ceda7fd56200eac6f4175c9a4e
|
|
| MD5 |
f40402885e882a47f497c88d6760e3d1
|
|
| BLAKE2b-256 |
12fdb624db1c0d1126f00f9b1f32a7d5c9f6e774b3c80fa84706bb11f5f66814
|
Provenance
The following attestation bundles were made for dhis2w_ql-1.3.0.tar.gz:
Publisher:
pypi-publish.yml on winterop-com/dhis2w-utils
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
dhis2w_ql-1.3.0.tar.gz -
Subject digest:
54357c9c7bc0403a944da406b2a9e864028166ceda7fd56200eac6f4175c9a4e - Sigstore transparency entry: 2155389508
- Sigstore integration time:
-
Permalink:
winterop-com/dhis2w-utils@f5d32a63038bb6fbac5d2f31d63e1844182632db -
Branch / Tag:
refs/tags/v1.3.0 - Owner: https://github.com/winterop-com
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi-publish.yml@f5d32a63038bb6fbac5d2f31d63e1844182632db -
Trigger Event:
push
-
Statement type:
File details
Details for the file dhis2w_ql-1.3.0-py3-none-any.whl.
File metadata
- Download URL: dhis2w_ql-1.3.0-py3-none-any.whl
- Upload date:
- Size: 55.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b749d57c56e998da9f7b1cdb1a0029a01375248f68f9ea01ecd1be485da2a5c3
|
|
| MD5 |
01f2f33be6af1adb674ce08364cde86a
|
|
| BLAKE2b-256 |
04ff45078acd4e89fbba2cde975f630e0a4570e1e5f139ced1f860c396930afa
|
Provenance
The following attestation bundles were made for dhis2w_ql-1.3.0-py3-none-any.whl:
Publisher:
pypi-publish.yml on winterop-com/dhis2w-utils
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
dhis2w_ql-1.3.0-py3-none-any.whl -
Subject digest:
b749d57c56e998da9f7b1cdb1a0029a01375248f68f9ea01ecd1be485da2a5c3 - Sigstore transparency entry: 2155389995
- Sigstore integration time:
-
Permalink:
winterop-com/dhis2w-utils@f5d32a63038bb6fbac5d2f31d63e1844182632db -
Branch / Tag:
refs/tags/v1.3.0 - Owner: https://github.com/winterop-com
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi-publish.yml@f5d32a63038bb6fbac5d2f31d63e1844182632db -
Trigger Event:
push
-
Statement type: