cloudnet-api-client 0.12.9__tar.gz → 0.12.11__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.
- {cloudnet_api_client-0.12.9 → cloudnet_api_client-0.12.11}/CHANGELOG.md +8 -0
- {cloudnet_api_client-0.12.9 → cloudnet_api_client-0.12.11}/PKG-INFO +1 -1
- {cloudnet_api_client-0.12.9 → cloudnet_api_client-0.12.11}/cloudnet_api_client/client.py +2 -2
- {cloudnet_api_client-0.12.9 → cloudnet_api_client-0.12.11}/cloudnet_api_client/containers.py +10 -1
- cloudnet_api_client-0.12.11/cloudnet_api_client/version.py +1 -0
- cloudnet_api_client-0.12.11/tests/data/20250711_kenttarova_l3-cf_ecmwf.nc +0 -0
- cloudnet_api_client-0.12.11/tests/data/20250711_kenttarova_l3-cf_era5-1-12.nc +0 -0
- cloudnet_api_client-0.12.11/tests/data/20250711_kenttarova_l3-cf_era5-7-18.nc +0 -0
- {cloudnet_api_client-0.12.9 → cloudnet_api_client-0.12.11}/tests/test_client.py +59 -31
- cloudnet_api_client-0.12.9/cloudnet_api_client/version.py +0 -1
- {cloudnet_api_client-0.12.9 → cloudnet_api_client-0.12.11}/.github/dataportal.env +0 -0
- {cloudnet_api_client-0.12.9 → cloudnet_api_client-0.12.11}/.github/db.env +0 -0
- {cloudnet_api_client-0.12.9 → cloudnet_api_client-0.12.11}/.github/docker-compose.yml +0 -0
- {cloudnet_api_client-0.12.9 → cloudnet_api_client-0.12.11}/.github/initdb.d/init-dbs.sh +0 -0
- {cloudnet_api_client-0.12.9 → cloudnet_api_client-0.12.11}/.github/ss.env +0 -0
- {cloudnet_api_client-0.12.9 → cloudnet_api_client-0.12.11}/.github/workflows/publish.yml +0 -0
- {cloudnet_api_client-0.12.9 → cloudnet_api_client-0.12.11}/.github/workflows/test.yml +0 -0
- {cloudnet_api_client-0.12.9 → cloudnet_api_client-0.12.11}/.gitignore +0 -0
- {cloudnet_api_client-0.12.9 → cloudnet_api_client-0.12.11}/.pre-commit-config.yaml +0 -0
- {cloudnet_api_client-0.12.9 → cloudnet_api_client-0.12.11}/LICENSE +0 -0
- {cloudnet_api_client-0.12.9 → cloudnet_api_client-0.12.11}/README.md +0 -0
- {cloudnet_api_client-0.12.9 → cloudnet_api_client-0.12.11}/cloudnet_api_client/__init__.py +0 -0
- {cloudnet_api_client-0.12.9 → cloudnet_api_client-0.12.11}/cloudnet_api_client/dl.py +0 -0
- {cloudnet_api_client-0.12.9 → cloudnet_api_client-0.12.11}/cloudnet_api_client/py.typed +0 -0
- {cloudnet_api_client-0.12.9 → cloudnet_api_client-0.12.11}/cloudnet_api_client/utils.py +0 -0
- {cloudnet_api_client-0.12.9 → cloudnet_api_client-0.12.11}/pyproject.toml +0 -0
- {cloudnet_api_client-0.12.9 → cloudnet_api_client-0.12.11}/tests/data/20140205_hyytiala_classification.nc +0 -0
- {cloudnet_api_client-0.12.9 → cloudnet_api_client-0.12.11}/tests/data/20250801_Magurele_CHM170137_000.nc +0 -0
- {cloudnet_api_client-0.12.9 → cloudnet_api_client-0.12.11}/tests/data/20250803_JOYCE_WST_01m.dat +0 -0
- {cloudnet_api_client-0.12.9 → cloudnet_api_client-0.12.11}/tests/data/20250808_Granada_CHM170119_0045_000.nc +0 -0
- {cloudnet_api_client-0.12.9 → cloudnet_api_client-0.12.11}/tests/data/20250808_hyytiala_iwc-Z-T-method.nc +0 -0
- {cloudnet_api_client-0.12.9 → cloudnet_api_client-0.12.11}/tests/data/20250814_bucharest_classification.nc +0 -0
- {cloudnet_api_client-0.12.9 → cloudnet_api_client-0.12.11}/tests/data/20250821_limassol_parsivel_41582c49.nc +0 -0
- {cloudnet_api_client-0.12.9 → cloudnet_api_client-0.12.11}/tests/data/20250822_leipzig-lim_ecmwf-open.nc +0 -0
|
@@ -5,6 +5,14 @@ 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.11 – 2026-06-17
|
|
9
|
+
|
|
10
|
+
- Add model_id filtering to files() query
|
|
11
|
+
|
|
12
|
+
## 0.12.10 – 2026-06-09
|
|
13
|
+
|
|
14
|
+
- Update site types
|
|
15
|
+
|
|
8
16
|
## 0.12.9 – 2026-04-14
|
|
9
17
|
|
|
10
18
|
- Add s3key to file metadata
|
|
@@ -171,6 +171,7 @@ class APIClient:
|
|
|
171
171
|
"instrument": instrument_id,
|
|
172
172
|
"instrumentPid": instrument_pid,
|
|
173
173
|
"product": product_id,
|
|
174
|
+
"model": model_id,
|
|
174
175
|
"showLegacy": show_legacy,
|
|
175
176
|
}
|
|
176
177
|
if show_legacy is not True:
|
|
@@ -181,7 +182,7 @@ class APIClient:
|
|
|
181
182
|
params, date, date_from, date_to, updated_at, updated_at_from, updated_at_to
|
|
182
183
|
)
|
|
183
184
|
|
|
184
|
-
_check_params(
|
|
185
|
+
_check_params(params, ("showLegacy",))
|
|
185
186
|
|
|
186
187
|
no_instrument = instrument_id is None and instrument_pid is None
|
|
187
188
|
|
|
@@ -199,7 +200,6 @@ class APIClient:
|
|
|
199
200
|
for key in ("showLegacy", "product", "instrument", "instrumentPid"):
|
|
200
201
|
if key in params:
|
|
201
202
|
del params[key]
|
|
202
|
-
params["model"] = model_id
|
|
203
203
|
files_res += self._get("model-files", params, expected_code=400)
|
|
204
204
|
|
|
205
205
|
return _build_meta_objects(files_res)
|
{cloudnet_api_client-0.12.9 → cloudnet_api_client-0.12.11}/cloudnet_api_client/containers.py
RENAMED
|
@@ -3,7 +3,16 @@ import uuid
|
|
|
3
3
|
from dataclasses import dataclass
|
|
4
4
|
from typing import Literal
|
|
5
5
|
|
|
6
|
-
SITE_TYPE = Literal[
|
|
6
|
+
SITE_TYPE = Literal[
|
|
7
|
+
"cloudnet",
|
|
8
|
+
"model",
|
|
9
|
+
"hidden",
|
|
10
|
+
"campaign",
|
|
11
|
+
"mobile",
|
|
12
|
+
"arm",
|
|
13
|
+
"weather-radar",
|
|
14
|
+
"fmi-radar",
|
|
15
|
+
]
|
|
7
16
|
PRODUCT_TYPE = Literal["instrument", "geophysical", "evaluation", "model"]
|
|
8
17
|
STATUS = Literal["created", "uploaded", "processed", "invalid"]
|
|
9
18
|
TIMELINESS = Literal["rrt", "nrt", "scheduled"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.12.11"
|
|
@@ -24,6 +24,16 @@ from cloudnet_api_client.containers import (
|
|
|
24
24
|
)
|
|
25
25
|
from cloudnet_api_client.utils import CloudnetAPIError, md5sum, sha256sum
|
|
26
26
|
|
|
27
|
+
# Product file UUIDs (submitted by the files_product fixture).
|
|
28
|
+
CLASSIFICATION_UUID = "8dcc865c-6920-49ce-a627-de045ec896e8"
|
|
29
|
+
PARSIVEL_UUID = "ab872770-9136-4e61-8958-31e62abdfb1b"
|
|
30
|
+
ECMWF_OPEN_UUID = "277d54f0-d376-4448-a784-f1c8b819b46a"
|
|
31
|
+
|
|
32
|
+
# Raw file instrument PIDs (submitted by the files_raw fixture).
|
|
33
|
+
BUCHAREST_CHM15K_PID = "https://hdl.handle.net/21.12132/3.c60c931fac9d43f0"
|
|
34
|
+
GRANADA_CHM15K_PID = "https://hdl.handle.net/21.12132/3.77a75f3b32294855"
|
|
35
|
+
JUELICH_WST_PID = "https://hdl.handle.net/21.12132/3.726b3b29de1949cc"
|
|
36
|
+
|
|
27
37
|
|
|
28
38
|
class RawFile(NamedTuple):
|
|
29
39
|
filename: str
|
|
@@ -35,8 +45,9 @@ class RawFile(NamedTuple):
|
|
|
35
45
|
|
|
36
46
|
class File(NamedTuple):
|
|
37
47
|
filename: str
|
|
38
|
-
legacy: bool
|
|
39
48
|
volatile: bool
|
|
49
|
+
legacy: bool = False
|
|
50
|
+
model: str | None = None
|
|
40
51
|
|
|
41
52
|
|
|
42
53
|
@pytest.fixture(scope="session")
|
|
@@ -62,21 +73,21 @@ def files_raw() -> list[RawFile]:
|
|
|
62
73
|
site="bucharest",
|
|
63
74
|
instrument="chm15k",
|
|
64
75
|
date="2025-08-01",
|
|
65
|
-
pid=
|
|
76
|
+
pid=BUCHAREST_CHM15K_PID,
|
|
66
77
|
),
|
|
67
78
|
RawFile(
|
|
68
79
|
filename="20250808_Granada_CHM170119_0045_000.nc",
|
|
69
80
|
site="granada",
|
|
70
81
|
instrument="chm15k",
|
|
71
82
|
date="2025-08-08",
|
|
72
|
-
pid=
|
|
83
|
+
pid=GRANADA_CHM15K_PID,
|
|
73
84
|
),
|
|
74
85
|
RawFile(
|
|
75
86
|
filename="20250803_JOYCE_WST_01m.dat",
|
|
76
87
|
site="juelich",
|
|
77
88
|
instrument="weather-station",
|
|
78
89
|
date="2025-08-01",
|
|
79
|
-
pid=
|
|
90
|
+
pid=JUELICH_WST_PID,
|
|
80
91
|
),
|
|
81
92
|
]
|
|
82
93
|
|
|
@@ -84,11 +95,18 @@ def files_raw() -> list[RawFile]:
|
|
|
84
95
|
@pytest.fixture(scope="session")
|
|
85
96
|
def files_product() -> list[File]:
|
|
86
97
|
return [
|
|
87
|
-
File("20250814_bucharest_classification.nc",
|
|
88
|
-
File("20250808_hyytiala_iwc-Z-T-method.nc",
|
|
89
|
-
File("20140205_hyytiala_classification.nc",
|
|
90
|
-
File("20250821_limassol_parsivel_41582c49.nc",
|
|
91
|
-
File("20250822_leipzig-lim_ecmwf-open.nc",
|
|
98
|
+
File("20250814_bucharest_classification.nc", volatile=True),
|
|
99
|
+
File("20250808_hyytiala_iwc-Z-T-method.nc", volatile=False),
|
|
100
|
+
File("20140205_hyytiala_classification.nc", volatile=False, legacy=True),
|
|
101
|
+
File("20250821_limassol_parsivel_41582c49.nc", volatile=False),
|
|
102
|
+
File("20250822_leipzig-lim_ecmwf-open.nc", volatile=True),
|
|
103
|
+
File("20250711_kenttarova_l3-cf_ecmwf.nc", volatile=True, model="ecmwf"),
|
|
104
|
+
File(
|
|
105
|
+
"20250711_kenttarova_l3-cf_era5-1-12.nc", volatile=True, model="era5-1-12"
|
|
106
|
+
),
|
|
107
|
+
File(
|
|
108
|
+
"20250711_kenttarova_l3-cf_era5-7-18.nc", volatile=True, model="era5-7-18"
|
|
109
|
+
),
|
|
92
110
|
]
|
|
93
111
|
|
|
94
112
|
|
|
@@ -286,34 +304,30 @@ class TestInstruments:
|
|
|
286
304
|
|
|
287
305
|
class TestProductFiles:
|
|
288
306
|
def test_file_route_with_geophysical_product(self, client: APIClient):
|
|
289
|
-
|
|
290
|
-
meta = client.file(uuid)
|
|
307
|
+
meta = client.file(CLASSIFICATION_UUID)
|
|
291
308
|
assert isinstance(meta, ProductMetadata)
|
|
292
|
-
assert str(meta.uuid) ==
|
|
309
|
+
assert str(meta.uuid) == CLASSIFICATION_UUID
|
|
293
310
|
assert meta.instrument is None
|
|
294
311
|
assert meta.model is None
|
|
295
312
|
assert meta.product.id == "classification"
|
|
296
313
|
|
|
297
314
|
def test_file_route_with_instrument_product(self, client: APIClient):
|
|
298
|
-
|
|
299
|
-
meta = client.file(uuid)
|
|
315
|
+
meta = client.file(PARSIVEL_UUID)
|
|
300
316
|
assert meta.model is None
|
|
301
317
|
assert isinstance(meta.instrument, Instrument)
|
|
302
318
|
assert meta.instrument.instrument_id == "parsivel"
|
|
303
319
|
|
|
304
320
|
def test_file_route_with_model_product(self, client: APIClient):
|
|
305
|
-
|
|
306
|
-
meta = client.file(uuid)
|
|
321
|
+
meta = client.file(ECMWF_OPEN_UUID)
|
|
307
322
|
assert meta.instrument is None
|
|
308
323
|
assert isinstance(meta.model, Model)
|
|
309
324
|
assert meta.model.id == "ecmwf-open"
|
|
310
325
|
|
|
311
326
|
def test_versions_route(self, client: APIClient):
|
|
312
|
-
|
|
313
|
-
meta = client.versions(uuid)
|
|
327
|
+
meta = client.versions(CLASSIFICATION_UUID)
|
|
314
328
|
assert len(meta) == 1
|
|
315
329
|
assert isinstance(meta[0], VersionMetadata)
|
|
316
|
-
assert str(meta[0].uuid) ==
|
|
330
|
+
assert str(meta[0].uuid) == CLASSIFICATION_UUID
|
|
317
331
|
|
|
318
332
|
def test_product_option(self, client: APIClient):
|
|
319
333
|
meta = client.files(site_id="hyytiala", product_id="iwc")
|
|
@@ -333,13 +347,28 @@ class TestProductFiles:
|
|
|
333
347
|
meta = client.files(instrument_id="parsivel")
|
|
334
348
|
assert len(meta) == 1
|
|
335
349
|
|
|
350
|
+
def test_model_filter_on_l3_product(self, client: APIClient):
|
|
351
|
+
models = ("ecmwf", "era5-1-12", "era5-7-18")
|
|
352
|
+
all_l3 = client.files(site_id="kenttarova", product_id="l3-cf")
|
|
353
|
+
assert {m.model.id for m in all_l3 if m.model} == set(models)
|
|
354
|
+
for model_id in models:
|
|
355
|
+
meta = client.files(
|
|
356
|
+
site_id="kenttarova", product_id="l3-cf", model_id=model_id
|
|
357
|
+
)
|
|
358
|
+
assert len(meta) == 1
|
|
359
|
+
assert meta[0].model is not None
|
|
360
|
+
assert meta[0].model.id == model_id
|
|
361
|
+
no_match = client.files(
|
|
362
|
+
site_id="kenttarova", product_id="l3-cf", model_id="ecmwf-open"
|
|
363
|
+
)
|
|
364
|
+
assert len(no_match) == 0
|
|
365
|
+
|
|
336
366
|
def test_files_route_with_invalid_input(self, client: APIClient):
|
|
337
367
|
with pytest.raises(CloudnetAPIError):
|
|
338
368
|
client.files(site_id="invalid-site")
|
|
339
369
|
|
|
340
370
|
def test_file_is_hashable(self, client: APIClient):
|
|
341
|
-
|
|
342
|
-
meta = client.file(uuid)
|
|
371
|
+
meta = client.file(CLASSIFICATION_UUID)
|
|
343
372
|
hash(meta)
|
|
344
373
|
|
|
345
374
|
|
|
@@ -355,13 +384,11 @@ class TestRawFiles:
|
|
|
355
384
|
assert len(meta) == 1
|
|
356
385
|
|
|
357
386
|
def test_filter_by_instrument_pid(self, client: APIClient):
|
|
358
|
-
|
|
359
|
-
meta = client.raw_files(instrument_pid=pid)
|
|
387
|
+
meta = client.raw_files(instrument_pid=GRANADA_CHM15K_PID)
|
|
360
388
|
assert len(meta) == 1
|
|
361
389
|
|
|
362
390
|
def test_filter_by_instrument_pid_no_match(self, client: APIClient):
|
|
363
|
-
|
|
364
|
-
meta = client.raw_files(instrument_pid=pid, date="2022-01-01")
|
|
391
|
+
meta = client.raw_files(instrument_pid=GRANADA_CHM15K_PID, date="2022-01-01")
|
|
365
392
|
assert len(meta) == 0
|
|
366
393
|
|
|
367
394
|
def test_filter_by_date_range_from(self, client: APIClient):
|
|
@@ -390,8 +417,7 @@ class TestRawFiles:
|
|
|
390
417
|
|
|
391
418
|
def test_instrument_id_vs_pid_exclusivity(self, client: APIClient):
|
|
392
419
|
meta1 = client.raw_files(instrument_id="chm15k")
|
|
393
|
-
|
|
394
|
-
meta2 = client.raw_files(instrument_pid=pid)
|
|
420
|
+
meta2 = client.raw_files(instrument_pid=BUCHAREST_CHM15K_PID)
|
|
395
421
|
assert len(meta1) > 1 # Multiple chm15k files
|
|
396
422
|
assert len(meta2) == 1 # Specific PID
|
|
397
423
|
|
|
@@ -463,8 +489,7 @@ class TestDownloadingFunctionality:
|
|
|
463
489
|
assert paths2[0].stat().st_size == original_size
|
|
464
490
|
|
|
465
491
|
def test_downloading_single_metadata(self, client: APIClient, tmp_path: Path):
|
|
466
|
-
|
|
467
|
-
meta = client.file(uuid)
|
|
492
|
+
meta = client.file(PARSIVEL_UUID)
|
|
468
493
|
paths = client.download(meta, output_directory=tmp_path, progress=False)
|
|
469
494
|
assert len(paths) == 1
|
|
470
495
|
assert paths[0].exists()
|
|
@@ -534,12 +559,15 @@ def _submit_product_file(backend_url: str, data_path: Path, meta: File):
|
|
|
534
559
|
"volatile": meta.volatile,
|
|
535
560
|
"legacy": meta.legacy,
|
|
536
561
|
"uuid": str(UUID(nc.file_uuid)),
|
|
537
|
-
"pid": nc
|
|
562
|
+
"pid": getattr(nc, "pid", ""),
|
|
538
563
|
"instrumentPid": getattr(nc, "instrument_pid", None),
|
|
539
564
|
"s3key": None,
|
|
540
565
|
**file_info,
|
|
541
566
|
}
|
|
542
|
-
|
|
567
|
+
if payload["product"] == "model":
|
|
568
|
+
payload["model"] = product
|
|
569
|
+
else:
|
|
570
|
+
payload["model"] = meta.model
|
|
543
571
|
url = f"{backend_url}/files/{meta.filename}"
|
|
544
572
|
res = requests.put(url, json=payload)
|
|
545
573
|
if res.status_code == 403:
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.12.9"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cloudnet_api_client-0.12.9 → cloudnet_api_client-0.12.11}/tests/data/20250803_JOYCE_WST_01m.dat
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|