cloudnet-api-client 0.12.7__tar.gz → 0.12.9__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. {cloudnet_api_client-0.12.7 → cloudnet_api_client-0.12.9}/.github/dataportal.env +2 -2
  2. {cloudnet_api_client-0.12.7 → cloudnet_api_client-0.12.9}/.github/docker-compose.yml +1 -1
  3. {cloudnet_api_client-0.12.7 → cloudnet_api_client-0.12.9}/.github/workflows/publish.yml +3 -3
  4. {cloudnet_api_client-0.12.7 → cloudnet_api_client-0.12.9}/.github/workflows/test.yml +10 -6
  5. {cloudnet_api_client-0.12.7 → cloudnet_api_client-0.12.9}/CHANGELOG.md +9 -0
  6. {cloudnet_api_client-0.12.7 → cloudnet_api_client-0.12.9}/PKG-INFO +9 -9
  7. {cloudnet_api_client-0.12.7 → cloudnet_api_client-0.12.9}/README.md +8 -8
  8. {cloudnet_api_client-0.12.7 → cloudnet_api_client-0.12.9}/cloudnet_api_client/client.py +2 -2
  9. {cloudnet_api_client-0.12.7 → cloudnet_api_client-0.12.9}/cloudnet_api_client/containers.py +1 -0
  10. {cloudnet_api_client-0.12.7 → cloudnet_api_client-0.12.9}/cloudnet_api_client/dl.py +16 -7
  11. cloudnet_api_client-0.12.9/cloudnet_api_client/version.py +1 -0
  12. {cloudnet_api_client-0.12.7 → cloudnet_api_client-0.12.9}/tests/test_client.py +14 -0
  13. cloudnet_api_client-0.12.7/cloudnet_api_client/version.py +0 -1
  14. {cloudnet_api_client-0.12.7 → cloudnet_api_client-0.12.9}/.github/db.env +0 -0
  15. {cloudnet_api_client-0.12.7 → cloudnet_api_client-0.12.9}/.github/initdb.d/init-dbs.sh +0 -0
  16. {cloudnet_api_client-0.12.7 → cloudnet_api_client-0.12.9}/.github/ss.env +0 -0
  17. {cloudnet_api_client-0.12.7 → cloudnet_api_client-0.12.9}/.gitignore +0 -0
  18. {cloudnet_api_client-0.12.7 → cloudnet_api_client-0.12.9}/.pre-commit-config.yaml +0 -0
  19. {cloudnet_api_client-0.12.7 → cloudnet_api_client-0.12.9}/LICENSE +0 -0
  20. {cloudnet_api_client-0.12.7 → cloudnet_api_client-0.12.9}/cloudnet_api_client/__init__.py +0 -0
  21. {cloudnet_api_client-0.12.7 → cloudnet_api_client-0.12.9}/cloudnet_api_client/py.typed +0 -0
  22. {cloudnet_api_client-0.12.7 → cloudnet_api_client-0.12.9}/cloudnet_api_client/utils.py +0 -0
  23. {cloudnet_api_client-0.12.7 → cloudnet_api_client-0.12.9}/pyproject.toml +0 -0
  24. {cloudnet_api_client-0.12.7 → cloudnet_api_client-0.12.9}/tests/data/20140205_hyytiala_classification.nc +0 -0
  25. {cloudnet_api_client-0.12.7 → cloudnet_api_client-0.12.9}/tests/data/20250801_Magurele_CHM170137_000.nc +0 -0
  26. {cloudnet_api_client-0.12.7 → cloudnet_api_client-0.12.9}/tests/data/20250803_JOYCE_WST_01m.dat +0 -0
  27. {cloudnet_api_client-0.12.7 → cloudnet_api_client-0.12.9}/tests/data/20250808_Granada_CHM170119_0045_000.nc +0 -0
  28. {cloudnet_api_client-0.12.7 → cloudnet_api_client-0.12.9}/tests/data/20250808_hyytiala_iwc-Z-T-method.nc +0 -0
  29. {cloudnet_api_client-0.12.7 → cloudnet_api_client-0.12.9}/tests/data/20250814_bucharest_classification.nc +0 -0
  30. {cloudnet_api_client-0.12.7 → cloudnet_api_client-0.12.9}/tests/data/20250821_limassol_parsivel_41582c49.nc +0 -0
  31. {cloudnet_api_client-0.12.7 → cloudnet_api_client-0.12.9}/tests/data/20250822_leipzig-lim_ecmwf-open.nc +0 -0
@@ -8,8 +8,8 @@ TYPEORM_PORT=5432
8
8
  TYPEORM_SYNCHRONIZE=false
9
9
  TYPEORM_MIGRATIONS_RUN=true
10
10
  TYPEORM_LOGGING=false
11
- TYPEORM_ENTITIES=build/entity/*.js
12
- TYPEORM_MIGRATIONS=build/migration/*.js
11
+ TYPEORM_ENTITIES=build/app/src/entity/*.js
12
+ TYPEORM_MIGRATIONS=build/app/src/migration/*.js
13
13
  DP_SS_URL=http://storage-service:5900
14
14
  DP_SS_USER=test
15
15
  DP_SS_PASSWORD=test
@@ -15,7 +15,7 @@ services:
15
15
  [
16
16
  "sh",
17
17
  "-c",
18
- "node build/fixtures.js /backend-fixtures/backend/fixtures TRUNCATE && node build/fixtures.js /dataportal-fixtures APPEND && npm run start",
18
+ "node build/app/src/fixtures.js /backend-fixtures/backend/fixtures TRUNCATE && node build/app/src/fixtures.js /dataportal-fixtures APPEND && npm run start",
19
19
  ]
20
20
  db:
21
21
  image: "postgres:16"
@@ -12,9 +12,9 @@ jobs:
12
12
  contents: write
13
13
  id-token: write
14
14
  steps:
15
- - uses: actions/checkout@v4
15
+ - uses: actions/checkout@v6
16
16
  - name: Set up Python
17
- uses: actions/setup-python@v5
17
+ uses: actions/setup-python@v6
18
18
  with:
19
19
  python-version: "3.10"
20
20
  - name: Install dependencies
@@ -30,7 +30,7 @@ jobs:
30
30
  echo "name=cloudnet-api-client $version" >> $GITHUB_OUTPUT
31
31
  id: changelog
32
32
  - name: Create release
33
- uses: softprops/action-gh-release@v1
33
+ uses: softprops/action-gh-release@v2
34
34
  with:
35
35
  name: ${{ steps.changelog.outputs.name }}
36
36
  body_path: ${{ github.workspace }}-CHANGELOG.txt
@@ -11,15 +11,15 @@ jobs:
11
11
  runs-on: ubuntu-latest
12
12
  steps:
13
13
  - name: Set up Python
14
- uses: actions/setup-python@v5
14
+ uses: actions/setup-python@v6
15
15
  with:
16
16
  python-version: ${{ matrix.python-version }}
17
17
 
18
18
  - name: Checkout code
19
- uses: actions/checkout@v4
19
+ uses: actions/checkout@v6
20
20
 
21
21
  - name: Cache Python dependencies
22
- uses: actions/cache@v4
22
+ uses: actions/cache@v5
23
23
  with:
24
24
  path: ~/.cache/pip
25
25
  key: ${{ runner.os }}-pip-${{ matrix.python-version }}-${{ hashFiles('**/pyproject.toml') }}
@@ -28,7 +28,7 @@ jobs:
28
28
  ${{ runner.os }}-pip-
29
29
 
30
30
  - name: Checkout fixtures
31
- uses: actions/checkout@v4
31
+ uses: actions/checkout@v6
32
32
  with:
33
33
  repository: actris-cloudnet/dataportal
34
34
  sparse-checkout: backend/fixtures/
@@ -36,13 +36,13 @@ jobs:
36
36
  path: backend-fixtures
37
37
 
38
38
  - name: Checkout production fixtures
39
- uses: actions/checkout@v4
39
+ uses: actions/checkout@v6
40
40
  with:
41
41
  repository: actris-cloudnet/dataportal-fixtures
42
42
  path: dataportal-fixtures
43
43
 
44
44
  - name: Set up Docker Buildx
45
- uses: docker/setup-buildx-action@v3
45
+ uses: docker/setup-buildx-action@v4
46
46
 
47
47
  - name: Start dataportal
48
48
  run: docker compose -f .github/docker-compose.yml up -d --wait
@@ -62,3 +62,7 @@ jobs:
62
62
  - name: Shutdown backend
63
63
  if: always()
64
64
  run: docker compose -f .github/docker-compose.yml down
65
+
66
+ - name: Print logs
67
+ if: always()
68
+ run: docker compose -f .github/docker-compose.yml logs
@@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## 0.12.9 – 2026-04-14
9
+
10
+ - Add s3key to file metadata
11
+
12
+ ## 0.12.8 – 2026-03-24
13
+
14
+ - Support downloading of single metadata objects
15
+ - Hide total progress bar when downloading a single file
16
+
8
17
  ## 0.12.7 – 2025-12-18
9
18
 
10
19
  - Adjust progress bars
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cloudnet-api-client
3
- Version: 0.12.7
3
+ Version: 0.12.9
4
4
  Summary: Cloudnet API client
5
5
  Author-email: Simo Tukiainen <simo.tukiainen@fmi.fi>
6
6
  License-File: LICENSE
@@ -51,7 +51,7 @@ sites = client.sites()
51
51
  site = client.site("hyytiala")
52
52
 
53
53
  products = client.products()
54
- product = client.products("classification")
54
+ product = client.product("classification")
55
55
 
56
56
  models = client.models()
57
57
  model = client.model("ecmwf-open")
@@ -239,13 +239,13 @@ Download files from the fetched metadata.
239
239
 
240
240
  Parameters:
241
241
 
242
- | name | type | default |
243
- | ----------------- | ---------------------------------------------- | ----------------- |
244
- | metadata | `list[RawMetadata]` or `list[ProductMetadata]` | |
245
- | output_directory | `PathLike` or `str` | Current directory |
246
- | concurrency_limit | `int` | 5 |
247
- | progress | `bool` or `None` | `None` |
248
- | validate_checksum | `bool` | `False` |
242
+ | name | type | default |
243
+ | ----------------- | ----------------------------------------------- | ----------------- |
244
+ | metadata | `RawMetadata`, `ProductMetadata` or `list[...]` | |
245
+ | output_directory | `PathLike` or `str` | Current directory |
246
+ | concurrency_limit | `int` | 5 |
247
+ | progress | `bool` or `None` | `None` |
248
+ | validate_checksum | `bool` | `False` |
249
249
 
250
250
  There's also an asynchronous version of this function:
251
251
  `cloudnet_api_client.adownload`. It's useful for usage inside Jupyter notebook.
@@ -22,7 +22,7 @@ sites = client.sites()
22
22
  site = client.site("hyytiala")
23
23
 
24
24
  products = client.products()
25
- product = client.products("classification")
25
+ product = client.product("classification")
26
26
 
27
27
  models = client.models()
28
28
  model = client.model("ecmwf-open")
@@ -210,13 +210,13 @@ Download files from the fetched metadata.
210
210
 
211
211
  Parameters:
212
212
 
213
- | name | type | default |
214
- | ----------------- | ---------------------------------------------- | ----------------- |
215
- | metadata | `list[RawMetadata]` or `list[ProductMetadata]` | |
216
- | output_directory | `PathLike` or `str` | Current directory |
217
- | concurrency_limit | `int` | 5 |
218
- | progress | `bool` or `None` | `None` |
219
- | validate_checksum | `bool` | `False` |
213
+ | name | type | default |
214
+ | ----------------- | ----------------------------------------------- | ----------------- |
215
+ | metadata | `RawMetadata`, `ProductMetadata` or `list[...]` | |
216
+ | output_directory | `PathLike` or `str` | Current directory |
217
+ | concurrency_limit | `int` | 5 |
218
+ | progress | `bool` or `None` | `None` |
219
+ | validate_checksum | `bool` | `False` |
220
220
 
221
221
  There's also an asynchronous version of this function:
222
222
  `cloudnet_api_client.adownload`. It's useful for usage inside Jupyter notebook.
@@ -322,7 +322,7 @@ class APIClient:
322
322
 
323
323
  def download(
324
324
  self,
325
- metadata: MetadataList,
325
+ metadata: MetadataList | TMetadata,
326
326
  output_directory: str | PathLike = ".",
327
327
  concurrency_limit: int = 5,
328
328
  progress: bool | None = None,
@@ -340,7 +340,7 @@ class APIClient:
340
340
 
341
341
  async def adownload(
342
342
  self,
343
- metadata: MetadataList,
343
+ metadata: MetadataList | TMetadata,
344
344
  output_directory: str | PathLike = ".",
345
345
  concurrency_limit: int = 5,
346
346
  progress: bool | None = None,
@@ -131,6 +131,7 @@ class ProductMetadata(Metadata):
131
131
  format: str
132
132
  start_time: datetime.datetime | None
133
133
  stop_time: datetime.datetime | None
134
+ s3key: str | None
134
135
 
135
136
 
136
137
  @dataclass(frozen=True, slots=True)
@@ -8,14 +8,22 @@ import aiohttp
8
8
  from tqdm import tqdm
9
9
 
10
10
  from cloudnet_api_client import utils
11
- from cloudnet_api_client.containers import Metadata, ProductMetadata
11
+ from cloudnet_api_client.containers import (
12
+ Metadata,
13
+ ProductMetadata,
14
+ )
12
15
 
13
16
 
14
17
  class BarConfig:
15
18
  def __init__(
16
- self, disable: bool | None, max_workers: int, total_bytes: int
19
+ self,
20
+ disable: bool | None,
21
+ max_workers: int,
22
+ total_bytes: int,
23
+ n_files: int,
17
24
  ) -> None:
18
25
  self.disable = disable
26
+ self.single_file = n_files <= 1
19
27
  self.position_queue = self._init_position_queue(max_workers)
20
28
  self.total_amount = tqdm(
21
29
  total=total_bytes,
@@ -23,7 +31,7 @@ class BarConfig:
23
31
  unit="iB",
24
32
  unit_scale=True,
25
33
  unit_divisor=1024,
26
- disable=self.disable,
34
+ disable=self.disable if not self.single_file else True,
27
35
  position=0,
28
36
  leave=False,
29
37
  colour="green",
@@ -32,7 +40,8 @@ class BarConfig:
32
40
 
33
41
  def _init_position_queue(self, max_workers: int) -> asyncio.Queue:
34
42
  queue: asyncio.Queue = asyncio.Queue()
35
- for i in range(1, max_workers + 1):
43
+ start = 0 if self.single_file else 1
44
+ for i in range(start, start + max_workers):
36
45
  queue.put_nowait(i)
37
46
  return queue
38
47
 
@@ -49,17 +58,17 @@ class DlParams:
49
58
 
50
59
  async def download_files(
51
60
  base_url: str,
52
- metadata: Iterable[Metadata],
61
+ metadata: Iterable[Metadata] | Metadata,
53
62
  output_path: Path,
54
63
  concurrency_limit: int,
55
64
  disable_progress: bool | None,
56
65
  validate_checksum: bool = False,
57
66
  ) -> list[Path]:
58
- metas = list(metadata)
67
+ metas = list(metadata) if isinstance(metadata, Iterable) else [metadata]
59
68
  file_exists = _checksum_matches if validate_checksum else _size_and_name_matches
60
69
  semaphore = asyncio.Semaphore(concurrency_limit)
61
70
  total_bytes = sum(meta.size for meta in metas)
62
- bar_config = BarConfig(disable_progress, concurrency_limit, total_bytes)
71
+ bar_config = BarConfig(disable_progress, concurrency_limit, total_bytes, len(metas))
63
72
  full_paths = []
64
73
  async with aiohttp.ClientSession() as session:
65
74
  tasks = []
@@ -0,0 +1 @@
1
+ __version__ = "0.12.9"
@@ -462,6 +462,19 @@ class TestDownloadingFunctionality:
462
462
  assert paths1 == paths2
463
463
  assert paths2[0].stat().st_size == original_size
464
464
 
465
+ def test_downloading_single_metadata(self, client: APIClient, tmp_path: Path):
466
+ uuid = "ab872770-9136-4e61-8958-31e62abdfb1b"
467
+ meta = client.file(uuid)
468
+ paths = client.download(meta, output_directory=tmp_path, progress=False)
469
+ assert len(paths) == 1
470
+ assert paths[0].exists()
471
+
472
+ def test_downloading_single_metadata_II(self, client: APIClient, tmp_path: Path):
473
+ meta = client.raw_files(date_from="2025-08-01")
474
+ assert len(meta) == 3
475
+ paths = client.download(meta[0], output_directory=tmp_path, progress=False)
476
+ assert len(paths) == 1
477
+
465
478
  async def test_async_download(self, client: APIClient, tmp_path: Path):
466
479
  meta = client.raw_files(date_from="2025-08-01")
467
480
  assert len(meta) == 3
@@ -523,6 +536,7 @@ def _submit_product_file(backend_url: str, data_path: Path, meta: File):
523
536
  "uuid": str(UUID(nc.file_uuid)),
524
537
  "pid": nc.pid,
525
538
  "instrumentPid": getattr(nc, "instrument_pid", None),
539
+ "s3key": None,
526
540
  **file_info,
527
541
  }
528
542
  payload["model"] = product if payload["product"] == "model" else None
@@ -1 +0,0 @@
1
- __version__ = "0.12.7"