Skip to content

Commit 4c669d3

Browse files
authored
Merge pull request #7 from dhis2/chore/publishable-packaging
build: make the package publishable to PyPI (0.3.2)
2 parents 41d696a + bfc8fcb commit 4c669d3

33 files changed

Lines changed: 290 additions & 91 deletions

‎.github/workflows/publish.yml‎

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
name: Publish to PyPI
2+
3+
# Builds and uploads a release when a version tag is pushed. Uses PyPI trusted
4+
# publishing, so no token is stored: the project on PyPI must list this repository,
5+
# this workflow file and the "pypi" environment as a trusted publisher.
6+
on:
7+
push:
8+
tags: ["v*"]
9+
10+
jobs:
11+
build:
12+
runs-on: ubuntu-latest
13+
steps:
14+
- uses: actions/checkout@v4
15+
- uses: actions/setup-python@v5
16+
with:
17+
python-version: "3.12"
18+
- name: Check the tag matches the package version
19+
run: |
20+
version=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
21+
test "v${version}" = "${GITHUB_REF_NAME}" || { echo "tag ${GITHUB_REF_NAME} does not match version ${version}"; exit 1; }
22+
- name: Build
23+
run: python -m pip install --upgrade build && python -m build
24+
- name: Check metadata
25+
run: python -m pip install --upgrade twine && twine check dist/*
26+
- uses: actions/upload-artifact@v4
27+
with:
28+
name: dist
29+
path: dist/
30+
31+
publish:
32+
needs: build
33+
runs-on: ubuntu-latest
34+
environment: pypi
35+
permissions:
36+
id-token: write
37+
steps:
38+
- uses: actions/download-artifact@v4
39+
with:
40+
name: dist
41+
path: dist/
42+
- uses: pypa/gh-action-pypi-publish@release/v1

‎.github/workflows/test.yml‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name: Test
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
8+
jobs:
9+
test:
10+
runs-on: ubuntu-latest
11+
strategy:
12+
fail-fast: false
13+
matrix:
14+
python-version: ["3.10", "3.11", "3.12", "3.13"]
15+
steps:
16+
- uses: actions/checkout@v4
17+
- uses: actions/setup-python@v5
18+
with:
19+
python-version: ${{ matrix.python-version }}
20+
- name: Install
21+
run: python -m pip install --upgrade pip && pip install -e .[dev]
22+
- name: Lint
23+
run: ruff check .
24+
- name: Unit tests
25+
run: pytest -q

‎.gitignore‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,6 @@ build/
1313
.coverage
1414
htmlcov/
1515
.codex
16+
*.egg-info/
17+
.pytest_cache/
18+
.ruff_cache/

‎CHANGELOG.md‎

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
# Changelog
22

3+
## [0.3.2] - 2026-10-05
4+
### Added
5+
- `LICENSE` file (BSD-3-Clause), `py.typed` marker, and PyPI metadata (classifiers, URLs, keywords).
6+
- GitHub Actions: tests and lint on every push and pull request; publish to PyPI on a `v*` tag through trusted publishing.
7+
### Changed
8+
- The `dev` extra now installs everything the test suite needs (`respx`, `python-dotenv`); `matplotlib` moved to an `examples` extra.
9+
- Lint clean under the repository's ruff configuration.
10+
- First release on PyPI: `pip install dhis2-client`.
11+
312
## [0.3.1] - 2026-04-14
413
### Changed
514
- Corrected release wording from "Centrlized" to "Centralized".
615
- Added separate `connect_timeout` control (default `60s`) while keeping `timeout` for read/write/pool (default `30s`).
716

8-
## [0.1.0] - 2025-09-21
9-
### Added
10-
- Sync `DHIS2Client` using httpx (dict/JSON only).
11-
- Stdlib JSON logging via `ClientSettings`.
12-
- Clean paging (`list_paged`, `fetch_all`).
13-
- Resources: Users (read-only), OrgUnits (CRUD + geojson), DataElements (CRUD),
14-
DataSets (CRUD), DataValues (single + sets), Analytics (read), System info.
15-
- Unit tests (respx) + live integration tests (guarded by env).
16-
1717
## [0.3.0] - 2025-10-08
1818
### Added
1919
- Analytics: latest_period_for_level(de_uid, level) using /api/dataValueSets with calendar-aware windows.
@@ -23,3 +23,17 @@
2323
- HTTP timeout controls: added `connect_timeout` (default 60s) while keeping `timeout` for read/write/pool (default 30s).
2424
### Validation
2525
- Error if data element is linked to datasets with mixed periodType.
26+
27+
## [0.2.0] - 2025-10-01
28+
### Added
29+
- Sharing: read and set public access, user and user-group accesses on metadata objects.
30+
- User organisation-unit scope management (capture, data view, tracked-entity search).
31+
32+
## [0.1.0] - 2025-09-21
33+
### Added
34+
- Sync `DHIS2Client` using httpx (dict/JSON only).
35+
- Stdlib JSON logging via `ClientSettings`.
36+
- Clean paging (`list_paged`, `fetch_all`).
37+
- Resources: Users (read-only), OrgUnits (CRUD + geojson), DataElements (CRUD),
38+
DataSets (CRUD), DataValues (single + sets), Analytics (read), System info.
39+
- Unit tests (respx) + live integration tests (guarded by env).

‎LICENSE‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
BSD 3-Clause License
2+
3+
Copyright (c) 2025-2026, HISP Centre, University of Oslo
4+
5+
Redistribution and use in source and binary forms, with or without
6+
modification, are permitted provided that the following conditions are met:
7+
8+
1. Redistributions of source code must retain the above copyright notice, this
9+
list of conditions and the following disclaimer.
10+
11+
2. Redistributions in binary form must reproduce the above copyright notice,
12+
this list of conditions and the following disclaimer in the documentation
13+
and/or other materials provided with the distribution.
14+
15+
3. Neither the name of the copyright holder nor the names of its
16+
contributors may be used to endorse or promote products derived from
17+
this software without specific prior written permission.
18+
19+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

‎README.md‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,16 +66,16 @@ This client provides a **lightweight and simple** way to work with the DHIS2 Web
6666

6767
> Requires **Python 3.10+**
6868
69-
At this stage the package is not yet published on PyPI.
70-
You can install it from source:
69+
```bash
70+
pip install dhis2-client
71+
```
72+
73+
To work on the client itself, install it from a checkout with the development extras:
7174

7275
```bash
73-
# Clone the repo
7476
git clone https://github.com/dhis2/dhis2-python-client.git
7577
cd dhis2-python-client
76-
77-
# Install in editable/development mode
78-
pip install -e .
78+
pip install -e .[dev]
7979
```
8080

8181
---

‎conftest.py‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import sys
44
from pathlib import Path
55

6-
76
SRC_DIR = Path(__file__).resolve().parent / "src"
87
if str(SRC_DIR) not in sys.path:
98
sys.path.insert(0, str(SRC_DIR))

‎examples/example_1_org_unit_fetch.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from itertools import islice
22

33
from examples._config import make_client
4+
45
client = make_client()
56

67
ous = list(client.get_organisation_units())
@@ -22,4 +23,4 @@
2223
#client = DHIS2Client(base_url="http://localhost:9090", token="d2p_RSY3qHZ6bCkRghud0afwMIlaTlXI2PefJLhwc2ZEaoZo1bhsFZ")
2324

2425
#ous = list(client.get_organisation_units())
25-
#print("▶ Total number of ous available: ", len(ous))
26+
#print("▶ Total number of ous available: ", len(ous))

‎examples/example_2_geojson_fetch.py‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
import matplotlib.pyplot as plt
2-
import geopandas as gpd
31
import io
42
import json
53

4+
import geopandas as gpd
5+
import matplotlib.pyplot as plt
6+
67
from examples._config import make_client
8+
79
client = make_client()
810

911
info = client.get_system_info()
@@ -26,4 +28,4 @@
2628

2729
plt.axis('equal')
2830
plt.tight_layout()
29-
plt.show()
31+
plt.show()

‎examples/example_3_data_element_fetch.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from examples._config import make_client
2+
23
client = make_client()
34

45
fields='id,displayName,valueType,domainType'

0 commit comments

Comments
 (0)