Skip to content

Commit 7d7203e

Browse files
authored
Merge branch 'main' into issues/36375543-noaa-hrrr
2 parents a225b83 + 959948d commit 7d7203e

11 files changed

Lines changed: 787 additions & 672 deletions

File tree

‎.github/copilot-instructions.md‎

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# Project Guidelines — Planetary Computer Tasks (pctasks)
2+
3+
## Architecture
4+
5+
Monorepo of 12 Python namespace packages under `pctasks/`, each independently installable via `hatchling`. Dependency order: `core → cli → task → client → ingest → ingest_task → dataset → run → notify → router → server → dev`. Additional code in `pctasks_funcs/` (Azure Functions), `datasets/` (~60 dataset definitions), and `deployment/` (Terraform/Helm).
6+
7+
**Core data flow**: blob storage assets → splits (by prefix) → chunks (file listing CSVs) → `Collection.create_item()` produces pystac Items → ingest into PgSTAC via pypgstac.
8+
9+
Key abstractions:
10+
- **`PCBaseModel`** (`pctasks/core/pctasks/core/models/base.py`): Pydantic v2 base with YAML serde, `by_alias=True`, `exclude_none=True`.
11+
- **`Task[T, U]`** (`pctasks/task/pctasks/task/task.py`): Generic abstract task, define `_input_model`/`_output_model` and `run(input, context)`.
12+
- **`Collection`** (`pctasks/dataset/pctasks/dataset/collection.py`): Abstract base for datasets; implement `create_item(cls, asset_uri, storage_factory) -> List[pystac.Item] | WaitTaskResult` as a classmethod.
13+
- **`StorageFactory`**: Creates `Storage` objects from `blob://<account>/<container>/<path>` URIs.
14+
- **Settings**: `pydantic-settings` with double-underscore env vars (`PCTASKS__COSMOSDB__URL`, `PCTASKS_RUN__TASK_RUNNER_TYPE`).
15+
16+
## Code Style
17+
18+
- **Formatter**: black (default settings) + isort. Run `scripts/format`.
19+
- **Linter**: flake8 with `max-line-length = 120`, ignores `E203, W503, E731, E722` (see `.flake8`).
20+
- **Type checking**: mypy with `disallow_untyped_defs = True` — all functions must have type annotations (see `mypy.ini`).
21+
- **No docstrings** unless explicitly requested.
22+
- All models extend `PCBaseModel` (Pydantic v2). Use `model_validate`, `model_dump`, `field_validator`, `model_validator`.
23+
24+
## Build and Test
25+
26+
```bash
27+
scripts/install # Create venv with uv, install all packages in editable mode
28+
scripts/server # Start local dev stack (Azurite, PgSTAC, server, STAC API)
29+
scripts/test # Full lint + test suite (runs in Docker via docker-compose.console.yml)
30+
scripts/test --subpackage core # Test one package
31+
scripts/test --test-only # Skip lint
32+
scripts/test --lint-only # Skip tests
33+
scripts/format # Auto-format all packages (black + isort)
34+
```
35+
36+
Per-package test commands (from within `pctasks/<pkg>/`):
37+
```bash
38+
pytest tests/ # Run package tests
39+
mypy --config-file ../../mypy.ini pctasks # Type-check
40+
```
41+
42+
## Project Conventions
43+
44+
### Dataset directory pattern (`datasets/<name>/`)
45+
```
46+
dataset.yaml # Required: dataset definition (id, image, collections, asset_storage, chunks)
47+
<name>.py # Required: Collection subclass with create_item classmethod
48+
collection/template.json # STAC collection Jinja2 template
49+
test_<name>.py # Tests: import collection class, call create_item with blob URIs
50+
requirements.txt # Optional: extra pip deps
51+
Dockerfile # Optional: custom image (otherwise uses pctasks-task-base)
52+
```
53+
54+
Dataset YAML references code via `${{ local.path(./<file>.py) }}` and class via `<module>:<ClassName>` (no `.py`).
55+
56+
### Templating syntax in YAML
57+
- `${{ args.<name> }}` — CLI arguments
58+
- `${{ secrets.<key> }}` — KeyVault/dev-secrets.yaml
59+
- `${{ local.path(<relative>) }}` — Path relative to YAML file
60+
- `${{ local.file(<relative>) }}` — Inline file contents
61+
62+
### CLI
63+
Click-based with plugin discovery via `pctasks.commands` entry point group. Each package registers subcommands in its `pyproject.toml`.
64+
65+
### Testing patterns
66+
- pytest with `asyncio_mode = auto` (see `pytest.ini`)
67+
- Dataset tests are co-located: `datasets/<name>/test_<name>.py`
68+
- Package tests: `pctasks/<pkg>/tests/`
69+
- Integration tests: `tests/`
70+
- Use `StorageFactory()` in tests for blob access; `SimpleWorkflowExecutor` for local workflow execution.
71+
72+
## Integration Points
73+
74+
- **Azure Blob Storage**: All asset URIs use `blob://<account>/<container>/<path>` scheme.
75+
- **Cosmos DB**: Workflow/run state persistence; emulated locally via docker-compose.
76+
- **PgSTAC**: STAC item storage (PostgreSQL + PostGIS), port 5499 locally.
77+
- **Azurite**: Local Azure Storage emulator, ports 10000-10002.
78+
- **Argo Workflows**: Production workflow orchestration on Kubernetes.
79+
- **Azure Batch**: Production task execution.
80+
- **Kind cluster**: Local Kubernetes for Argo testing (`scripts/cluster setup`).

‎datasets/ecmwf-forecast/dataset.yaml‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
id: ecmwf_forecast
2-
image: ${{ args.registry }}/pctasks-ecmwf-forecast:2026.01.12
2+
image: ${{ args.registry }}/pctasks-ecmwf-forecast:2026.02.26
33

44
args:
55
- registry
6+
- year-prefix
67

78
code:
89
src: ${{ local.path(./ecmwf_forecast.py) }}
@@ -17,6 +18,9 @@ collections:
1718
asset_storage:
1819
- uri: blob://ai4edataeuwest/ecmwf/
1920
chunks:
21+
splits:
22+
- depth: 1
23+
folder_matches: ^${{ args.year-prefix }}
2024
options:
2125
# currently excluding "aifs", in favor of "ifs"
2226
# Could put that in a different collection, or modify

‎datasets/era5-pds/README.md‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# ERA5 Collection
2+
3+
## Collection Update
4+
5+
```bash
6+
pctasks dataset ingest-collection \
7+
-d dataset.yaml \
8+
-a registry pccomponents \
9+
-c era5-pds \
10+
--confirm --submit
11+
```

0 commit comments

Comments
 (0)