Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .cache/state/jobs.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions .cache/state/schedules.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions .cache/state/workflows.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"70a148c9-4c30-4ff4-bdc1-d793853cca5b": {"workflowId": "70a148c9-4c30-4ff4-bdc1-d793853cca5b", "name": "scheduled-workflow", "steps": [{"name": "aggregate", "processId": "eo-aggregate-import", "payload": {"inputs": {"datasetId": "chirps-daily", "parameters": ["precip"], "datetime": "2026-01-31T00:00:00Z", "orgUnitLevel": 2, "aggregation": "mean", "dhis2": {"dataElementId": "abc123", "dryRun": true}}}}], "created": "2026-02-23T21:59:01.674872Z", "updated": "2026-02-23T21:59:01.678721Z", "lastRunAt": "2026-02-23T21:59:01.678721Z", "lastRunJobIds": ["8337900a-0fc6-4f53-a13e-89540fb03d2e"]}, "5919d4fa-644f-4ffc-8541-d258e987b379": {"workflowId": "5919d4fa-644f-4ffc-8541-d258e987b379", "name": "scheduled-workflow", "steps": [{"name": "aggregate", "processId": "eo-aggregate-import", "payload": {"inputs": {"datasetId": "chirps-daily", "parameters": ["precip"], "datetime": "2026-01-31T00:00:00Z", "orgUnitLevel": 2, "aggregation": "mean", "dhis2": {"dataElementId": "abc123", "dryRun": true}}}}], "created": "2026-02-23T22:03:01.398456Z", "updated": "2026-02-23T22:03:01.503931Z", "lastRunAt": "2026-02-23T22:03:01.503931Z", "lastRunJobIds": ["d42e8fc4-1023-453c-9aae-8c33141ca838"]}, "90ee914e-925d-4775-bbdb-8489f79e6b2d": {"workflowId": "90ee914e-925d-4775-bbdb-8489f79e6b2d", "name": "scheduled-workflow", "steps": [{"name": "aggregate", "processId": "eo-aggregate-import", "payload": {"inputs": {"datasetId": "chirps-daily", "parameters": ["precip"], "datetime": "2026-01-31T00:00:00Z", "orgUnitLevel": 2, "aggregation": "mean", "dhis2": {"dataElementId": "abc123", "dryRun": true}}}}], "created": "2026-02-23T22:03:34.858713Z", "updated": "2026-02-23T22:03:34.964026Z", "lastRunAt": "2026-02-23T22:03:34.964026Z", "lastRunJobIds": ["d6098fe4-5916-4d3b-b540-de1076f51c8d"]}, "364063da-5825-4303-9736-51baf8692530": {"workflowId": "364063da-5825-4303-9736-51baf8692530", "name": "scheduled-workflow", "steps": [{"name": "aggregate", "processId": "eo-aggregate-import", "payload": {"inputs": {"datasetId": "chirps-daily", "parameters": ["precip"], "datetime": "2026-01-31T00:00:00Z", "orgUnitLevel": 2, "aggregation": "mean", "dhis2": {"dataElementId": "abc123", "dryRun": true}}}}], "created": "2026-02-23T22:04:09.181686Z", "updated": "2026-02-23T22:04:09.290683Z", "lastRunAt": "2026-02-23T22:04:09.290683Z", "lastRunJobIds": ["fb84e869-28c5-4187-b963-62a0f0ddb0d0"]}, "9be46657-dc95-4197-b520-034877ad53fd": {"workflowId": "9be46657-dc95-4197-b520-034877ad53fd", "name": "scheduled-workflow", "steps": [{"name": "aggregate", "processId": "eo-aggregate-import", "payload": {"inputs": {"datasetId": "chirps-daily", "parameters": ["precip"], "datetime": "2026-01-31T00:00:00Z", "orgUnitLevel": 2, "aggregation": "mean", "dhis2": {"dataElementId": "abc123", "dryRun": true}}}}], "created": "2026-02-23T22:05:26.277030Z", "updated": "2026-02-23T22:05:26.385208Z", "lastRunAt": "2026-02-23T22:05:26.385208Z", "lastRunJobIds": ["8eee9f98-bab7-43d9-a8ea-6d7d0f8c5bff"]}, "5b2d0e9f-a83b-46a1-bac0-93d7693df940": {"workflowId": "5b2d0e9f-a83b-46a1-bac0-93d7693df940", "name": "scheduled-workflow", "steps": [{"name": "aggregate", "processId": "eo-aggregate-import", "payload": {"inputs": {"datasetId": "chirps-daily", "parameters": ["precip"], "datetime": "2026-01-31T00:00:00Z", "orgUnitLevel": 2, "aggregation": "mean", "dhis2": {"dataElementId": "abc123", "dryRun": true}}}}], "created": "2026-02-23T22:06:19.984497Z", "updated": "2026-02-23T22:06:20.090912Z", "lastRunAt": "2026-02-23T22:06:20.090912Z", "lastRunJobIds": ["a0138e99-87f4-4213-b003-53e4435d2191"]}, "99f60372-418b-4205-b3e1-0f95b8c47ec3": {"workflowId": "99f60372-418b-4205-b3e1-0f95b8c47ec3", "name": "scheduled-workflow", "steps": [{"name": "aggregate", "processId": "eo-aggregate-import", "payload": {"inputs": {"datasetId": "chirps-daily", "parameters": ["precip"], "datetime": "2026-01-31T00:00:00Z", "orgUnitLevel": 2, "aggregation": "mean", "dhis2": {"dataElementId": "abc123", "dryRun": true}}}}], "created": "2026-02-23T22:07:10.279699Z", "updated": "2026-02-23T22:07:10.388391Z", "lastRunAt": "2026-02-23T22:07:10.388391Z", "lastRunJobIds": ["07711cbe-a49b-43ec-b9f5-a47ff809a637"]}, "d122ebc8-c984-41dc-a867-25507acedb91": {"workflowId": "d122ebc8-c984-41dc-a867-25507acedb91", "name": "scheduled-workflow", "steps": [{"name": "aggregate", "processId": "eo-aggregate-import", "payload": {"inputs": {"datasetId": "chirps-daily", "parameters": ["precip"], "datetime": "2026-01-31T00:00:00Z", "orgUnitLevel": 2, "aggregation": "mean", "dhis2": {"dataElementId": "abc123", "dryRun": true}}}}], "created": "2026-02-23T22:08:25.837044Z", "updated": "2026-02-23T22:08:25.948539Z", "lastRunAt": "2026-02-23T22:08:25.948539Z", "lastRunJobIds": ["e3f2a3ef-efb8-4a71-a892-437463f84e13"]}, "8615178b-17c8-434f-842b-e9f13b745301": {"workflowId": "8615178b-17c8-434f-842b-e9f13b745301", "name": "scheduled-workflow", "steps": [{"name": "aggregate", "processId": "eo-aggregate-import", "payload": {"inputs": {"datasetId": "chirps-daily", "parameters": ["precip"], "datetime": "2026-01-31T00:00:00Z", "orgUnitLevel": 2, "aggregation": "mean", "dhis2": {"dataElementId": "abc123", "dryRun": true}}}}], "created": "2026-02-23T22:10:11.462031Z", "updated": "2026-02-23T22:10:11.573452Z", "lastRunAt": "2026-02-23T22:10:11.573452Z", "lastRunJobIds": ["3bc40154-66d2-44b8-bf8a-3c931d7ce92a"]}, "696e988e-0e0d-4e2a-8bd2-9e6109ae798d": {"workflowId": "696e988e-0e0d-4e2a-8bd2-9e6109ae798d", "name": "scheduled-workflow", "steps": [{"name": "aggregate", "processId": "eo-aggregate-import", "payload": {"inputs": {"datasetId": "chirps-daily", "parameters": ["precip"], "datetime": "2026-01-31T00:00:00Z", "orgUnitLevel": 2, "aggregation": "mean", "dhis2": {"dataElementId": "abc123", "dryRun": true}}}}], "created": "2026-02-23T22:12:27.421003Z", "updated": "2026-02-23T22:12:27.537998Z", "lastRunAt": "2026-02-23T22:12:27.537998Z", "lastRunJobIds": ["faaa0822-0996-42aa-9b4a-09db3b38b253"]}, "76b22e29-d03d-41cc-8afd-1f6d98c904b8": {"workflowId": "76b22e29-d03d-41cc-8afd-1f6d98c904b8", "name": "scheduled-workflow", "steps": [{"name": "aggregate", "processId": "eo-aggregate-import", "payload": {"inputs": {"datasetId": "chirps-daily", "parameters": ["precip"], "datetime": "2026-01-31T00:00:00Z", "orgUnitLevel": 2, "aggregation": "mean", "dhis2": {"dataElementId": "abc123", "dryRun": true}}}}], "created": "2026-02-23T22:13:55.274478Z", "updated": "2026-02-23T22:13:55.383149Z", "lastRunAt": "2026-02-23T22:13:55.383149Z", "lastRunJobIds": ["89f1748d-a2ff-4fb4-bad4-034af929932e"]}, "f94af166-6944-45d6-b972-4500feec0eec": {"workflowId": "f94af166-6944-45d6-b972-4500feec0eec", "name": "scheduled-workflow", "steps": [{"name": "aggregate", "processId": "eo-aggregate-import", "payload": {"inputs": {"datasetId": "chirps-daily", "parameters": ["precip"], "datetime": "2026-01-31T00:00:00Z", "orgUnitLevel": 2, "aggregation": "mean", "dhis2": {"dataElementId": "abc123", "dryRun": true}}}}], "created": "2026-02-23T22:15:40.229164Z", "updated": "2026-02-23T22:15:40.343668Z", "lastRunAt": "2026-02-23T22:15:40.343668Z", "lastRunJobIds": ["a0aa8517-0ec2-4b4a-86c0-5645c4077a6b"]}, "ba8a6537-d7a7-4062-b169-bc65113cbb8c": {"workflowId": "ba8a6537-d7a7-4062-b169-bc65113cbb8c", "name": "scheduled-workflow", "steps": [{"name": "aggregate", "processId": "eo-aggregate-import", "payload": {"inputs": {"datasetId": "chirps-daily", "parameters": ["precip"], "datetime": "2026-01-31T00:00:00Z", "orgUnitLevel": 2, "aggregation": "mean", "dhis2": {"dataElementId": "abc123", "dryRun": true}}}}], "created": "2026-02-23T22:16:56.821962Z", "updated": "2026-02-23T22:16:56.938046Z", "lastRunAt": "2026-02-23T22:16:56.938046Z", "lastRunJobIds": ["9a34fb59-d815-4107-9c6b-d3207704d83a"]}, "5a390470-b37d-45b2-9633-8435c0167424": {"workflowId": "5a390470-b37d-45b2-9633-8435c0167424", "name": "scheduled-workflow", "steps": [{"name": "aggregate", "processId": "eo-aggregate-import", "payload": {"inputs": {"datasetId": "chirps-daily", "parameters": ["precip"], "datetime": "2026-01-31T00:00:00Z", "orgUnitLevel": 2, "aggregation": "mean", "dhis2": {"dataElementId": "abc123", "dryRun": true}}}}], "created": "2026-02-23T22:18:09.699497Z", "updated": "2026-02-23T22:18:09.812445Z", "lastRunAt": "2026-02-23T22:18:09.812445Z", "lastRunJobIds": ["50f69ac8-ca39-4892-bf3c-2921221183b8"]}, "a1ef8b41-460c-40ba-a219-aef155d1e67e": {"workflowId": "a1ef8b41-460c-40ba-a219-aef155d1e67e", "name": "scheduled-workflow", "steps": [{"name": "aggregate", "processId": "eo-aggregate-import", "payload": {"inputs": {"datasetId": "chirps-daily", "parameters": ["precip"], "datetime": "2026-01-31T00:00:00Z", "orgUnitLevel": 2, "aggregation": "mean", "dhis2": {"dataElementId": "abc123", "dryRun": true}}}}], "created": "2026-02-24T11:01:17.113543Z", "updated": "2026-02-24T11:01:17.228929Z", "lastRunAt": "2026-02-24T11:01:17.228929Z", "lastRunJobIds": ["30fe9414-3c22-4ec1-bad6-19a2890bfaf2"]}}
Binary file not shown.
Binary file not shown.
22 changes: 22 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,24 @@
- DHIS2 Maps app and DHIS2 Climate app are primary consumers of `eo-api`.
- `eo-api` should replace functionality currently sourced via Google Earth Engine for these apps.

## Current implemented baseline (keep in sync)

- Dataset discovery is implemented via:
- `GET /collections`
- `GET /collections/{collectionId}`
- OGC API - Coverages baseline is implemented via:
- `GET /collections/{collectionId}/coverage`
- Collections and coverages are split into separate endpoint modules:
- `eoapi/endpoints/collections.py`
- `eoapi/endpoints/coverages.py`
- Shared endpoint constants/errors live in:
- `eoapi/endpoints/constants.py`
- `eoapi/endpoints/errors.py`
- Dataset metadata is file-driven from `eoapi/datasets/<dataset-id>/<dataset-id>.yaml` and validated by Pydantic (`eoapi/datasets/catalog.py`).
- Dataset-specific source resolver logic lives in `eoapi/datasets/<dataset-id>/resolver.py`.
- Dataset validation command is available via `make validate-datasets`.
- Tests currently include endpoint error contract tests and run via `make test`.

## Product priorities

- Favor end-to-end data flow correctness over feature breadth.
Expand Down Expand Up @@ -41,6 +59,8 @@
- For async/long-running operations, expose job status instead of blocking calls.
- Include stable identifiers for datasets, processes, and executions.
- Treat Maps app and Climate app contracts as first-class compatibility targets.
- For collections/coverages, prefer OGC API - Common and OGC API - Coverages compatible response structures and link relations.
- Keep collections and coverages handlers decoupled, with shared helpers/constants in dedicated modules.

## Geospatial/data handling guidance

Expand Down Expand Up @@ -86,3 +106,5 @@

- Update docs when adding endpoints, process parameters, or output schema changes.
- Include example requests/responses for new process execution paths.
- Keep `README.md` concise and place endpoint examples in `API_EXAMPLES.md`.
- Keep dataset schema documentation in `eoapi/datasets/README.md`.
8 changes: 7 additions & 1 deletion .github/skills/eo-pipeline-orchestration/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ description: Define scheduled and custom EO data pipelines with optional pre/pos
# EO Pipeline Orchestration

## Use this skill when

- Creating recurring ingestion workflows
- Adding custom pre/post-processing pipeline steps
- Designing orchestration handoffs (Airflow/Prefect)

## Canonical pipeline stages
1. Discover dataset and validate metadata

1. Discover dataset and validate metadata (from `eoapi/datasets/<dataset-id>/<dataset-id>.yaml` definitions)
2. Extract data for time/area window
3. Cache source/intermediate artifacts as files when needed
4. Transform and harmonize units/CRS
Expand All @@ -21,6 +23,7 @@ description: Define scheduled and custom EO data pipelines with optional pre/pos
8. Trigger import or publish for downstream ingestion

## Orchestration guidance

- Treat each stage as an idempotent task where possible
- Persist execution metadata and lineage
- Use retries/backoff for transient provider failures
Expand All @@ -32,10 +35,13 @@ description: Define scheduled and custom EO data pipelines with optional pre/pos
- Promote reusable orchestration helpers to upstream libraries when they are broadly applicable beyond `eo-api`

## Data integrity checks

- Nodata handling rules are explicit
- CRS mismatches are detected and resolved deterministically
- Aggregation method and temporal windows are logged
- Dataset definition schema is validated before runs (e.g. `make validate-datasets`)

## MVP constraints

- Prefer simple, inspectable DAGs over highly dynamic graphs
- Prioritize reliable daily scheduled runs for climate and population flows
26 changes: 22 additions & 4 deletions .github/skills/eo-process-api-design/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,40 @@ description: Design OGC API - Processes style EO execution endpoints and request
# EO Process API Design

## Use this skill when

- Adding or modifying process execution endpoints
- Designing dataset/process discovery and execution contracts
- Defining long-running execution behavior and status tracking

## Required endpoint baseline
- `GET /processes`
- `GET /processes/{process-id}`
- `POST /processes/{process-id}/execution`
## Current API baseline in this repo

- Dataset discovery:
- `GET /collections`
- `GET /collections/{collectionId}`
- Coverage retrieval:
- `GET /collections/{collectionId}/coverage`
- Process execution endpoints (target baseline):
- `GET /processes`
- `GET /processes/{process-id}`
- `POST /processes/{process-id}/execution`

## OGC endpoint guidance

- For collections, follow OGC API - Common response structures (`extent`, `links`, stable IDs).
- For coverage responses, follow OGC API - Coverages/CoverageJSON-compatible structures (`domain`, `parameters`, `ranges`).
- Validate query parameters and return structured `InvalidParameterValue` errors.
- Return `NotFound` for unknown collections/processes.

## Design rules

- Keep resource names consistent and stable
- Use explicit IDs for process, execution, dataset
- Return structured validation errors with actionable messages
- Prefer async execution with status polling for long jobs
- Keep response schemas backward compatible

## EO/DHIS2 checks

- Document CRS assumptions and aggregation method semantics
- Include preview/dry-run support where feasible
- Use `dhis2-python-client` for DHIS2 Web API operations
Expand All @@ -33,6 +50,7 @@ description: Design OGC API - Processes style EO execution endpoints and request
- Prioritize process capabilities that replace current Google Earth Engine-backed functionality

## Output checklist

- Endpoint contract
- Example request/response
- Error model
Expand Down
5 changes: 5 additions & 0 deletions .github/skills/prd-authoring/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ description: Draft or refine PRDs for DHIS2 EO API features with clear MVP scope
- Includes compatibility and migration implications for `maps-app` and `climate-app`
- Explicitly identifies which Google Earth Engine-dependent behavior is replaced by `eo-api`
- Calls out standards alignment (OGC/STAC) when applicable
- Reflects current implemented baseline where relevant:
- `/collections`, `/collections/{collectionId}`
- `/collections/{collectionId}/coverage`
- File-driven dataset metadata in `eoapi/datasets/<dataset-id>/<dataset-id>.yaml` validated by Pydantic
- Validation/test workflow via `make validate-datasets` and `make test`

## Quality bar

Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
__pycache__/
.venv/
*.egg-info/
Loading