Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
0b8ad43
refactor: migrate to src layout and add linting/type-checking configs
mortenoh Feb 24, 2026
7322ade
chore: add make lint target
mortenoh Feb 24, 2026
2030fb8
chore: add make help as default target
mortenoh Feb 24, 2026
dc14e1e
chore: add uv_build build system and make help target
mortenoh Feb 24, 2026
13d2f74
refactor: split main.py into routers and add Pydantic response models
mortenoh Feb 24, 2026
e9e41ab
refactor: rename MessageResponse to StatusMessage and extract ogcapi …
mortenoh Feb 24, 2026
b05fb1f
docs: add explicit TilerFactory configuration with inline comments
mortenoh Feb 24, 2026
50d4a4f
test: add pytest with initial test suite and make test target
mortenoh Feb 24, 2026
19d7aa2
ci: add GitHub Actions workflow for lint and test
mortenoh Feb 24, 2026
18b19f5
chore: add .python-version pinned to 3.13
mortenoh Feb 24, 2026
71c5bc1
fix(ci): set PYGEOAPI_CONFIG and PYGEOAPI_OPENAPI env vars
mortenoh Feb 24, 2026
5cd339d
feat: add /health endpoint for Docker container health checks
mortenoh Feb 24, 2026
d39085a
feat: add Dockerfile and docker Makefile targets
mortenoh Feb 24, 2026
30f0c1c
feat: add /info endpoint and tag root routes as "System"
mortenoh Feb 24, 2026
4dec425
fix(docker): use PORT env var in CMD instead of hardcoded 8000
mortenoh Feb 24, 2026
ba1483a
feat(docker): add compose.yml and update Makefile targets
mortenoh Feb 24, 2026
b8cc98b
fix(docker): add init: true for proper signal forwarding
mortenoh Feb 24, 2026
9d50758
docs: document pygeoapi configuration system in ogcapi.py
mortenoh Feb 24, 2026
421135f
docs: add OGC API and pygeoapi reference guide
mortenoh Feb 24, 2026
29c03ed
merge: integrate main into refactor/src-layout-and-linting-config
mortenoh Feb 24, 2026
dc0b129
refactor: move pygeoapi plugins into src layout and fix lint
mortenoh Feb 24, 2026
afec0e9
refactor: add type annotations to pygeoapi plugin files
mortenoh Feb 24, 2026
11cc3be
refactor: replace docker-up/down with docker-build and docker-run tar…
mortenoh Feb 24, 2026
ec7983d
fix: implement get() method in DHIS2OrgUnitsProvider
mortenoh Feb 24, 2026
53b46f2
feat: fetch real DHIS2 org units via httpx with Pydantic models
mortenoh Feb 24, 2026
fa86adc
fix: return pygeoapi-compatible field dicts from get_fields()
mortenoh Feb 24, 2026
96b58e5
fix: include field titles in schema endpoint output
mortenoh Feb 24, 2026
1d3899c
refactor: move DHIS2 credentials to env vars and improve bbox handling
mortenoh Feb 24, 2026
3d35dbc
docs: update README with all Makefile targets and DHIS2 env vars
mortenoh Feb 24, 2026
3720b2c
refactor: auto-generate pygeoapi-openapi.yml and add DHIS2 CQL provider
mortenoh Feb 24, 2026
5e33407
docs: add CQL filtering section to ogcapi.md
mortenoh Feb 24, 2026
7ec27ea
docs: remove redundant filter-lang=cql-text from CQL examples
mortenoh Feb 24, 2026
7cdcac9
fix: add openapi generation step to CI workflow
mortenoh Feb 24, 2026
eb51542
ci: add Docker image build workflow and GHCR compose file
mortenoh Feb 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.venv/
.git/
__pycache__/
.env
*.egg-info/
.DS_Store
.ruff_cache/
.mypy_cache/
.pytest_cache/
tests/
.claude/
5 changes: 4 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
PYGEOAPI_CONFIG=pygeoapi-config.yml
PYGEOAPI_OPENAPI=pygeoapi-openapi.yml
PYGEOAPI_OPENAPI=pygeoapi-openapi.yml
DHIS2_BASE_URL=https://play.im.dhis2.org/stable-2-42-4/api
DHIS2_USERNAME=admin
DHIS2_PASSWORD=district
38 changes: 38 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
lint-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Create .env from example
run: cp .env.example .env

- name: Install uv
uses: astral-sh/setup-uv@v4
with:
version: "latest"

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.13"

- name: Install dependencies
run: uv sync --group dev

- name: Run linting
run: make lint

- name: Generate OpenAPI spec
run: make openapi

- name: Run tests
run: make test
72 changes: 72 additions & 0 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
name: Build and push Docker image

on:
push:
branches: [main]

env:
REGISTRY: ghcr.io
IMAGE_NAME: dhis2/eo-api

jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
attestations: write
id-token: write
steps:
- uses: actions/checkout@v4

- name: Create .env from example
run: cp .env.example .env

- name: Install uv
uses: astral-sh/setup-uv@v4
with:
version: "latest"

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.13"

- name: Install dependencies
run: uv sync

- name: Generate OpenAPI spec
run: make openapi

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata (tags, labels)
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}

- name: Build and push
id: push
uses: docker/build-push-action@v6
with:
context: .
push: true
platforms: linux/amd64
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

- name: Generate artifact attestation
uses: actions/attest-build-provenance@v2
with:
subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
subject-digest: ${{ steps.push.outputs.digest }}
push-to-registry: true
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ __pycache__/
.venv/
.env
eo_api.egg-info/
pygeoapi-openapi.yml
1 change: 1 addition & 0 deletions .python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.13
7 changes: 7 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# CLAUDE.md

## Commit conventions

- Use conventional commits (e.g. `feat:`, `fix:`, `docs:`, `chore:`, `refactor:`, `test:`)
- No Co-Authored-By or other attribution lines
- Never use emojis anywhere — not in commits, code, comments, or responses
30 changes: 30 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
FROM ghcr.io/astral-sh/uv:0.10-python3.13-trixie-slim

RUN apt-get update && \
apt-get install -y --no-install-recommends \
git curl \
build-essential cython3 \
libgeos-dev libproj-dev && \
rm -rf /var/lib/apt/lists/*

RUN groupadd --system eo && useradd --system --gid eo --create-home eo

WORKDIR /app

COPY pyproject.toml uv.lock .python-version ./
COPY src/ src/

RUN uv sync --frozen --no-dev

COPY pygeoapi-config.yml pygeoapi-openapi.yml ./

ENV PYGEOAPI_CONFIG=/app/pygeoapi-config.yml
ENV PYGEOAPI_OPENAPI=/app/pygeoapi-openapi.yml
ENV PORT=8000

USER eo

HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD curl -f http://localhost:${PORT}/health || exit 1

CMD /app/.venv/bin/uvicorn eo_api.main:app --host 0.0.0.0 --port ${PORT}
29 changes: 24 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,8 +1,27 @@
sync:
.DEFAULT_GOAL := help

help: ## Show this help
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " %-15s %s\n", $$1, $$2}'

sync: ## Install dependencies with uv
uv sync

run:
uv run uvicorn main:app --reload
run: openapi ## Start the app with uvicorn
uv run uvicorn eo_api.main:app --reload

lint: ## Run ruff linting and formatting (autofix)
uv run ruff check --fix .
uv run ruff format .

test: ## Run tests with pytest
uv run pytest tests/

openapi: ## Generate pygeoapi OpenAPI spec
@set -a && . ./.env && set +a && \
PYTHONPATH="$(PWD)/src" uv run pygeoapi openapi generate ./pygeoapi-config.yml > pygeoapi-openapi.yml

start: openapi ## Start the Docker stack (builds images first)
docker compose up --build

openapi:
PYTHONPATH="$(PWD)" uv run pygeoapi openapi generate ./pygeoapi-config.yml > pygeoapi-openapi.yml
restart: openapi ## Tear down, rebuild, and start the Docker stack from scratch
docker compose down -v && docker compose build --no-cache && docker compose up
21 changes: 16 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,15 @@ Install dependencies (requires [uv](https://docs.astral.sh/uv/)):
Environment variables are loaded automatically from `.env` (via `python-dotenv`).
Copy `.env.example` to `.env` and adjust values as needed.

Key environment variables (used by the OGC API DHIS2 plugin):

- `DHIS2_BASE_URL` -- DHIS2 API base URL (defaults to play server in `.env.example`)
- `DHIS2_USERNAME` -- DHIS2 username
- `DHIS2_PASSWORD` -- DHIS2 password

Start the app:

`uv run uvicorn main:app --reload`
`uv run uvicorn eo_api.main:app --reload`

### Using pip (alternative)

Expand All @@ -25,7 +31,7 @@ If you can't use uv (e.g. mixed conda/forge environments):
python -m venv .venv
source .venv/bin/activate
pip install -e .
uvicorn main:app --reload
uvicorn eo_api.main:app --reload
```

### Using conda
Expand All @@ -34,13 +40,18 @@ uvicorn main:app --reload
conda create -n dhis2-eo-api python=3.13
conda activate dhis2-eo-api
pip install -e .
uvicorn main:app --reload
uvicorn eo_api.main:app --reload
```

### Makefile targets

- `make sync` — install dependencies with uv
- `make run` — start the app with uv
- `make sync` -- install dependencies with uv
- `make run` -- start the app with uvicorn
- `make lint` -- run ruff linting and format checks
- `make test` -- run tests with pytest
- `make openapi` -- generate pygeoapi OpenAPI spec
- `make start` -- start the Docker stack (builds images first)
- `make restart` -- tear down, rebuild, and start the Docker stack from scratch

### pygeoapi instructions

Expand Down
8 changes: 8 additions & 0 deletions compose.ghcr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
services:
api:
image: ghcr.io/dhis2/eo-api:latest
env_file: .env
ports:
- "${PORT:-8000}:${PORT:-8000}"
init: true
restart: unless-stopped
8 changes: 8 additions & 0 deletions compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
services:
api:
build: .
env_file: .env
ports:
- "${PORT:-8000}:${PORT:-8000}"
init: true
restart: unless-stopped
Loading