cloudnet-api-client 0.9.2__tar.gz → 0.11.0__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.11.0/.github/dataportal.env +29 -0
- cloudnet_api_client-0.11.0/.github/db.env +4 -0
- cloudnet_api_client-0.11.0/.github/docker-compose.yml +45 -0
- cloudnet_api_client-0.11.0/.github/initdb.d/init-dbs.sh +9 -0
- cloudnet_api_client-0.11.0/.github/ss.env +12 -0
- cloudnet_api_client-0.11.0/.github/workflows/test.yml +64 -0
- {cloudnet_api_client-0.9.2 → cloudnet_api_client-0.11.0}/.gitignore +1 -0
- {cloudnet_api_client-0.9.2 → cloudnet_api_client-0.11.0}/.pre-commit-config.yaml +2 -2
- {cloudnet_api_client-0.9.2 → cloudnet_api_client-0.11.0}/CHANGELOG.md +10 -0
- {cloudnet_api_client-0.9.2 → cloudnet_api_client-0.11.0}/PKG-INFO +26 -3
- {cloudnet_api_client-0.9.2 → cloudnet_api_client-0.11.0}/README.md +23 -2
- {cloudnet_api_client-0.9.2 → cloudnet_api_client-0.11.0}/cloudnet_api_client/client.py +41 -8
- {cloudnet_api_client-0.9.2 → cloudnet_api_client-0.11.0}/cloudnet_api_client/containers.py +22 -1
- {cloudnet_api_client-0.9.2 → cloudnet_api_client-0.11.0}/cloudnet_api_client/utils.py +8 -3
- cloudnet_api_client-0.11.0/cloudnet_api_client/version.py +1 -0
- {cloudnet_api_client-0.9.2 → cloudnet_api_client-0.11.0}/pyproject.toml +4 -1
- cloudnet_api_client-0.11.0/tests/data/20140205_hyytiala_classification.nc +0 -0
- cloudnet_api_client-0.11.0/tests/data/20250801_Magurele_CHM170137_000.nc +0 -0
- cloudnet_api_client-0.11.0/tests/data/20250803_JOYCE_WST_01m.dat +525 -0
- cloudnet_api_client-0.11.0/tests/data/20250808_Granada_CHM170119_0045_000.nc +0 -0
- cloudnet_api_client-0.11.0/tests/data/20250808_hyytiala_iwc-Z-T-method.nc +0 -0
- cloudnet_api_client-0.11.0/tests/data/20250814_bucharest_classification.nc +0 -0
- cloudnet_api_client-0.11.0/tests/test_client.py +359 -0
- cloudnet_api_client-0.9.2/.github/workflows/test.yml +0 -27
- cloudnet_api_client-0.9.2/cloudnet_api_client/version.py +0 -1
- {cloudnet_api_client-0.9.2 → cloudnet_api_client-0.11.0}/.github/workflows/publish.yml +0 -0
- {cloudnet_api_client-0.9.2 → cloudnet_api_client-0.11.0}/LICENSE +0 -0
- {cloudnet_api_client-0.9.2 → cloudnet_api_client-0.11.0}/cloudnet_api_client/__init__.py +0 -0
- {cloudnet_api_client-0.9.2 → cloudnet_api_client-0.11.0}/cloudnet_api_client/dl.py +0 -0
- {cloudnet_api_client-0.9.2 → cloudnet_api_client-0.11.0}/cloudnet_api_client/py.typed +0 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
NODE_ENV=test
|
|
2
|
+
SS_MODE=remote
|
|
3
|
+
TYPEORM_HOST=db
|
|
4
|
+
TYPEORM_USERNAME=dataportal
|
|
5
|
+
TYPEORM_PASSWORD=dev
|
|
6
|
+
TYPEORM_DATABASE=dataportal
|
|
7
|
+
TYPEORM_PORT=5432
|
|
8
|
+
TYPEORM_SYNCHRONIZE=false
|
|
9
|
+
TYPEORM_MIGRATIONS_RUN=true
|
|
10
|
+
TYPEORM_LOGGING=false
|
|
11
|
+
TYPEORM_ENTITIES=build/entity/*.js
|
|
12
|
+
TYPEORM_MIGRATIONS=build/migration/*.js
|
|
13
|
+
DP_SS_URL=http://storage-service:5900
|
|
14
|
+
DP_SS_USER=test
|
|
15
|
+
DP_SS_PASSWORD=test
|
|
16
|
+
DP_BACKEND_URL=http://localhost:3000/api
|
|
17
|
+
DP_FRONTEND_URL=http://localhost:8080
|
|
18
|
+
GEOLITE2_COUNTRY_PATH=tests/data/GeoLite2-Country-Test.mmdb
|
|
19
|
+
CITATION_SERVICE_URL=http://citation-service
|
|
20
|
+
DATACITE_API_URL=http://localhost:5802
|
|
21
|
+
DATACITE_API_USERNAME=XXX
|
|
22
|
+
DATACITE_API_PASSWORD=XXX
|
|
23
|
+
DATACITE_API_TIMEOUT_MS=2000
|
|
24
|
+
DATACITE_DOI_SERVER=http://handle.datacite.test
|
|
25
|
+
DATACITE_DOI_PREFIX=XXX
|
|
26
|
+
LABELLING_URL=http://localhost:5803
|
|
27
|
+
HANDLE_API_URL=http://localhost:5804
|
|
28
|
+
DVAS_URL=https://dvas.test
|
|
29
|
+
DC_URL=https://dc.test
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
services:
|
|
2
|
+
dataportal-backend:
|
|
3
|
+
image: ghcr.io/actris-cloudnet/dataportal-backend
|
|
4
|
+
ports:
|
|
5
|
+
- "3000:3000"
|
|
6
|
+
depends_on:
|
|
7
|
+
db:
|
|
8
|
+
condition: service_healthy
|
|
9
|
+
volumes:
|
|
10
|
+
- ../dataportal-fixtures:/dataportal-fixtures
|
|
11
|
+
- ../backend-fixtures:/backend-fixtures
|
|
12
|
+
env_file:
|
|
13
|
+
- dataportal.env
|
|
14
|
+
command:
|
|
15
|
+
[
|
|
16
|
+
"sh",
|
|
17
|
+
"-c",
|
|
18
|
+
"node build/fixtures.js /backend-fixtures/backend/fixtures TRUNCATE && node build/fixtures.js /dataportal-fixtures APPEND && npm run start",
|
|
19
|
+
]
|
|
20
|
+
db:
|
|
21
|
+
image: "postgres:16"
|
|
22
|
+
volumes:
|
|
23
|
+
- ./initdb.d:/docker-entrypoint-initdb.d
|
|
24
|
+
ports:
|
|
25
|
+
- "54321:54321"
|
|
26
|
+
env_file:
|
|
27
|
+
- db.env
|
|
28
|
+
healthcheck:
|
|
29
|
+
test: ["CMD", "psql", "-c", "select 1"]
|
|
30
|
+
interval: 1s
|
|
31
|
+
retries: 120
|
|
32
|
+
moto-server:
|
|
33
|
+
image: "motoserver/moto:3.0.1"
|
|
34
|
+
storage-service:
|
|
35
|
+
image: ghcr.io/actris-cloudnet/storage-service
|
|
36
|
+
ports:
|
|
37
|
+
- "5900:5900"
|
|
38
|
+
depends_on:
|
|
39
|
+
db:
|
|
40
|
+
condition: service_healthy
|
|
41
|
+
moto-server:
|
|
42
|
+
condition: service_started
|
|
43
|
+
env_file:
|
|
44
|
+
- ss.env
|
|
45
|
+
command: ["sh", "-c", "node build/init.js && npm start"]
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -e
|
|
3
|
+
|
|
4
|
+
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
|
|
5
|
+
CREATE USER ss WITH PASSWORD 'dev';
|
|
6
|
+
CREATE DATABASE ss OWNER ss;
|
|
7
|
+
CREATE USER dataportal WITH PASSWORD 'dev';
|
|
8
|
+
CREATE DATABASE dataportal OWNER dataportal;
|
|
9
|
+
EOSQL
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
NODE_ENV=test
|
|
2
|
+
SS_USER=test
|
|
3
|
+
SS_PWHASH=9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08
|
|
4
|
+
SS_MAXOBJECTSPERBUCKET=10000
|
|
5
|
+
PGHOST=db
|
|
6
|
+
PGDATABASE=ss
|
|
7
|
+
PGUSER=ss
|
|
8
|
+
PGPASSWORD=dev
|
|
9
|
+
S3_ENDPOINT=http://moto-server:5000
|
|
10
|
+
S3_ACCESSKEYID=dev
|
|
11
|
+
S3_SECRETACCESSKEY=dev
|
|
12
|
+
S3_FORCE_PATH_STYLE=true
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
name: Test and lint
|
|
2
|
+
|
|
3
|
+
on: [push, pull_request]
|
|
4
|
+
|
|
5
|
+
jobs:
|
|
6
|
+
test:
|
|
7
|
+
timeout-minutes: 5
|
|
8
|
+
strategy:
|
|
9
|
+
matrix:
|
|
10
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- name: Set up Python
|
|
14
|
+
uses: actions/setup-python@v5
|
|
15
|
+
with:
|
|
16
|
+
python-version: ${{ matrix.python-version }}
|
|
17
|
+
|
|
18
|
+
- name: Checkout code
|
|
19
|
+
uses: actions/checkout@v4
|
|
20
|
+
|
|
21
|
+
- name: Cache Python dependencies
|
|
22
|
+
uses: actions/cache@v4
|
|
23
|
+
with:
|
|
24
|
+
path: ~/.cache/pip
|
|
25
|
+
key: ${{ runner.os }}-pip-${{ matrix.python-version }}-${{ hashFiles('**/pyproject.toml') }}
|
|
26
|
+
restore-keys: |
|
|
27
|
+
${{ runner.os }}-pip-${{ matrix.python-version }}-
|
|
28
|
+
${{ runner.os }}-pip-
|
|
29
|
+
|
|
30
|
+
- name: Checkout fixtures
|
|
31
|
+
uses: actions/checkout@v4
|
|
32
|
+
with:
|
|
33
|
+
repository: actris-cloudnet/dataportal
|
|
34
|
+
sparse-checkout: backend/fixtures/
|
|
35
|
+
sparse-checkout-cone-mode: false
|
|
36
|
+
path: backend-fixtures
|
|
37
|
+
|
|
38
|
+
- name: Checkout production fixtures
|
|
39
|
+
uses: actions/checkout@v4
|
|
40
|
+
with:
|
|
41
|
+
repository: actris-cloudnet/dataportal-fixtures
|
|
42
|
+
path: dataportal-fixtures
|
|
43
|
+
|
|
44
|
+
- name: Set up Docker Buildx
|
|
45
|
+
uses: docker/setup-buildx-action@v3
|
|
46
|
+
|
|
47
|
+
- name: Start dataportal
|
|
48
|
+
run: docker compose -f .github/docker-compose.yml up -d --wait
|
|
49
|
+
|
|
50
|
+
- name: Install dependencies
|
|
51
|
+
run: |
|
|
52
|
+
pip install --upgrade pip
|
|
53
|
+
pip install .[dev,test]
|
|
54
|
+
|
|
55
|
+
- name: Run pre-commit checks
|
|
56
|
+
if: matrix.python-version == '3.13'
|
|
57
|
+
run: pre-commit run --all-files --show-diff-on-failure
|
|
58
|
+
|
|
59
|
+
- name: Run tests
|
|
60
|
+
run: pytest -s -vv
|
|
61
|
+
|
|
62
|
+
- name: Shutdown backend
|
|
63
|
+
if: always()
|
|
64
|
+
run: docker compose -f .github/docker-compose.yml down
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
exclude: ^tests/data/
|
|
2
2
|
repos:
|
|
3
3
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
4
|
-
rev:
|
|
4
|
+
rev: v6.0.0
|
|
5
5
|
hooks:
|
|
6
6
|
- id: check-case-conflict
|
|
7
7
|
- id: check-executables-have-shebangs
|
|
@@ -13,7 +13,7 @@ repos:
|
|
|
13
13
|
args: ["--fix", "lf"]
|
|
14
14
|
- id: trailing-whitespace
|
|
15
15
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
16
|
-
rev: v0.
|
|
16
|
+
rev: v0.12.8
|
|
17
17
|
hooks:
|
|
18
18
|
- id: ruff
|
|
19
19
|
args: ["--fix"]
|
|
@@ -5,6 +5,16 @@ 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.11.0 – 2025-08-16
|
|
9
|
+
|
|
10
|
+
- Adjust routes and responses
|
|
11
|
+
- Improve tests
|
|
12
|
+
|
|
13
|
+
## 0.10.0 – 2025-08-13
|
|
14
|
+
|
|
15
|
+
- Add `volatile` to metadata response
|
|
16
|
+
- Run CI tests against true dataportal backend
|
|
17
|
+
|
|
8
18
|
## 0.9.2 – 2025-08-04
|
|
9
19
|
|
|
10
20
|
- Use updated Cloudnet API
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cloudnet-api-client
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.11.0
|
|
4
4
|
Summary: Cloudnet API client
|
|
5
5
|
Author-email: Simo Tukiainen <simo.tukiainen@fmi.fi>
|
|
6
6
|
License-File: LICENSE
|
|
@@ -22,7 +22,9 @@ Requires-Dist: types-requests; extra == 'dev'
|
|
|
22
22
|
Requires-Dist: types-tqdm; extra == 'dev'
|
|
23
23
|
Provides-Extra: test
|
|
24
24
|
Requires-Dist: mypy; extra == 'test'
|
|
25
|
+
Requires-Dist: netcdf4; extra == 'test'
|
|
25
26
|
Requires-Dist: pytest; extra == 'test'
|
|
27
|
+
Requires-Dist: pytest-asyncio; extra == 'test'
|
|
26
28
|
Description-Content-Type: text/markdown
|
|
27
29
|
|
|
28
30
|
[](https://github.com/actris-cloudnet/cloudnet-api-client/actions/workflows/test.yml)
|
|
@@ -80,6 +82,7 @@ Parameters:
|
|
|
80
82
|
| updated_at_to | `str`, `date` or `datetime` | `None` | "2025-01-01T12:00:00" |
|
|
81
83
|
| instrument_id | `str` or `list[str]` | `None` | "rpg-fmcw-94" |
|
|
82
84
|
| instrument_pid | `str` or `list[str]` | `None` | "https://hdl.handle.net/21.12132/3.191564170f8a4686" |
|
|
85
|
+
| model_id | `str` or `list[str]` | `None` | "gdas1" |
|
|
83
86
|
| product\* | `str` or `list[str]` | `None` | "classification" |
|
|
84
87
|
| show_legacy\* | `bool` | `False` | |
|
|
85
88
|
| filename_prefix\*\* | `str` or `list[str]` | `None` | "stare" |
|
|
@@ -127,13 +130,33 @@ Parameters:
|
|
|
127
130
|
|
|
128
131
|
\* = only with `RawMetadata`
|
|
129
132
|
|
|
133
|
+
### `APIClient().file()` → `ProductMetadata`
|
|
134
|
+
|
|
135
|
+
Fetch metadata of a single file.
|
|
136
|
+
|
|
137
|
+
Parameters:
|
|
138
|
+
|
|
139
|
+
| name | type |
|
|
140
|
+
| ---- | -------------------- |
|
|
141
|
+
| uuid | `str` or `uuid.UUID` |
|
|
142
|
+
|
|
143
|
+
### `APIClient().versions()` → `list[VersionMetadata]`
|
|
144
|
+
|
|
145
|
+
Fetch information of all versions of a file.
|
|
146
|
+
|
|
147
|
+
Parameters:
|
|
148
|
+
|
|
149
|
+
| name | type |
|
|
150
|
+
| ---- | -------------------- |
|
|
151
|
+
| uuid | `str` or `uuid.UUID` |
|
|
152
|
+
|
|
130
153
|
### `APIClient().sites()` → `list[Site]`
|
|
131
154
|
|
|
132
155
|
Fetch cloudnet sites.
|
|
133
156
|
|
|
134
157
|
Parameters:
|
|
135
158
|
|
|
136
|
-
| name | type |
|
|
159
|
+
| name | type | choices | default |
|
|
137
160
|
| ------- | -------------------- | ----------------------------------------- | ------- |
|
|
138
161
|
| site_id | `str` | | `None` |
|
|
139
162
|
| type | `str` or `list[str]` | "cloudnet", "campaign", "model", "hidden" | `None` |
|
|
@@ -144,7 +167,7 @@ Fetch cloudnet products.
|
|
|
144
167
|
|
|
145
168
|
Parameters:
|
|
146
169
|
|
|
147
|
-
| name | type |
|
|
170
|
+
| name | type | choices | default |
|
|
148
171
|
| ---- | -------------------- | ----------------------------------------- | ------- |
|
|
149
172
|
| type | `str` or `list[str]` | "instrument", "geophysical", "evaluation" | `None` |
|
|
150
173
|
|
|
@@ -53,6 +53,7 @@ Parameters:
|
|
|
53
53
|
| updated_at_to | `str`, `date` or `datetime` | `None` | "2025-01-01T12:00:00" |
|
|
54
54
|
| instrument_id | `str` or `list[str]` | `None` | "rpg-fmcw-94" |
|
|
55
55
|
| instrument_pid | `str` or `list[str]` | `None` | "https://hdl.handle.net/21.12132/3.191564170f8a4686" |
|
|
56
|
+
| model_id | `str` or `list[str]` | `None` | "gdas1" |
|
|
56
57
|
| product\* | `str` or `list[str]` | `None` | "classification" |
|
|
57
58
|
| show_legacy\* | `bool` | `False` | |
|
|
58
59
|
| filename_prefix\*\* | `str` or `list[str]` | `None` | "stare" |
|
|
@@ -100,13 +101,33 @@ Parameters:
|
|
|
100
101
|
|
|
101
102
|
\* = only with `RawMetadata`
|
|
102
103
|
|
|
104
|
+
### `APIClient().file()` → `ProductMetadata`
|
|
105
|
+
|
|
106
|
+
Fetch metadata of a single file.
|
|
107
|
+
|
|
108
|
+
Parameters:
|
|
109
|
+
|
|
110
|
+
| name | type |
|
|
111
|
+
| ---- | -------------------- |
|
|
112
|
+
| uuid | `str` or `uuid.UUID` |
|
|
113
|
+
|
|
114
|
+
### `APIClient().versions()` → `list[VersionMetadata]`
|
|
115
|
+
|
|
116
|
+
Fetch information of all versions of a file.
|
|
117
|
+
|
|
118
|
+
Parameters:
|
|
119
|
+
|
|
120
|
+
| name | type |
|
|
121
|
+
| ---- | -------------------- |
|
|
122
|
+
| uuid | `str` or `uuid.UUID` |
|
|
123
|
+
|
|
103
124
|
### `APIClient().sites()` → `list[Site]`
|
|
104
125
|
|
|
105
126
|
Fetch cloudnet sites.
|
|
106
127
|
|
|
107
128
|
Parameters:
|
|
108
129
|
|
|
109
|
-
| name | type |
|
|
130
|
+
| name | type | choices | default |
|
|
110
131
|
| ------- | -------------------- | ----------------------------------------- | ------- |
|
|
111
132
|
| site_id | `str` | | `None` |
|
|
112
133
|
| type | `str` or `list[str]` | "cloudnet", "campaign", "model", "hidden" | `None` |
|
|
@@ -117,7 +138,7 @@ Fetch cloudnet products.
|
|
|
117
138
|
|
|
118
139
|
Parameters:
|
|
119
140
|
|
|
120
|
-
| name | type |
|
|
141
|
+
| name | type | choices | default |
|
|
121
142
|
| ---- | -------------------- | ----------------------------------------- | ------- |
|
|
122
143
|
| type | `str` or `list[str]` | "instrument", "geophysical", "evaluation" | `None` |
|
|
123
144
|
|
|
@@ -3,12 +3,12 @@ import calendar
|
|
|
3
3
|
import datetime
|
|
4
4
|
import os
|
|
5
5
|
import re
|
|
6
|
-
import uuid
|
|
7
6
|
from dataclasses import fields, is_dataclass
|
|
8
7
|
from os import PathLike
|
|
9
8
|
from pathlib import Path
|
|
10
9
|
from typing import TypeVar, cast
|
|
11
10
|
from urllib.parse import urljoin
|
|
11
|
+
from uuid import UUID
|
|
12
12
|
|
|
13
13
|
import requests
|
|
14
14
|
from requests.adapters import HTTPAdapter
|
|
@@ -25,6 +25,7 @@ from cloudnet_api_client.containers import (
|
|
|
25
25
|
RawMetadata,
|
|
26
26
|
RawModelMetadata,
|
|
27
27
|
Site,
|
|
28
|
+
VersionMetadata,
|
|
28
29
|
)
|
|
29
30
|
from cloudnet_api_client.dl import download_files
|
|
30
31
|
|
|
@@ -42,6 +43,8 @@ class APIClient:
|
|
|
42
43
|
base_url: str = "https://cloudnet.fmi.fi/api/",
|
|
43
44
|
session: requests.Session | None = None,
|
|
44
45
|
) -> None:
|
|
46
|
+
if not base_url.endswith("/"):
|
|
47
|
+
base_url += "/"
|
|
45
48
|
self.base_url = base_url
|
|
46
49
|
self.session = session or _make_session()
|
|
47
50
|
|
|
@@ -74,7 +77,7 @@ class APIClient:
|
|
|
74
77
|
instrument_id=obj["instrument"]["id"],
|
|
75
78
|
model=obj["model"],
|
|
76
79
|
type=obj["type"],
|
|
77
|
-
uuid=
|
|
80
|
+
uuid=UUID(obj["uuid"]),
|
|
78
81
|
pid=obj["pid"],
|
|
79
82
|
owners=obj["owners"],
|
|
80
83
|
serial_number=obj["serialNumber"],
|
|
@@ -83,6 +86,31 @@ class APIClient:
|
|
|
83
86
|
for obj in res
|
|
84
87
|
]
|
|
85
88
|
|
|
89
|
+
def file(
|
|
90
|
+
self,
|
|
91
|
+
uuid: str | UUID,
|
|
92
|
+
) -> ProductMetadata:
|
|
93
|
+
res = self._get_response(f"files/{uuid}")
|
|
94
|
+
return _build_meta_objects(res)[0]
|
|
95
|
+
|
|
96
|
+
def versions(self, uuid: str | UUID) -> list[VersionMetadata]:
|
|
97
|
+
res = self._get_response(
|
|
98
|
+
f"files/{uuid}/versions",
|
|
99
|
+
{"properties": ["pid", "dvasId", "legacy", "size", "checksum"]},
|
|
100
|
+
)
|
|
101
|
+
return [
|
|
102
|
+
VersionMetadata(
|
|
103
|
+
uuid=UUID(obj["uuid"]),
|
|
104
|
+
created_at=_parse_datetime(obj["createdAt"]),
|
|
105
|
+
pid=obj["pid"],
|
|
106
|
+
dvas_id=obj["dvasId"],
|
|
107
|
+
legacy=obj["legacy"],
|
|
108
|
+
size=int(obj["size"]),
|
|
109
|
+
checksum=obj["checksum"],
|
|
110
|
+
)
|
|
111
|
+
for obj in res
|
|
112
|
+
]
|
|
113
|
+
|
|
86
114
|
def metadata(
|
|
87
115
|
self,
|
|
88
116
|
site_id: QueryParam = None,
|
|
@@ -105,6 +133,10 @@ class APIClient:
|
|
|
105
133
|
"product": product,
|
|
106
134
|
"showLegacy": show_legacy,
|
|
107
135
|
}
|
|
136
|
+
if show_legacy is not True:
|
|
137
|
+
# API shows legacy files with any value (even <False>)
|
|
138
|
+
del params["showLegacy"]
|
|
139
|
+
|
|
108
140
|
_add_date_params(
|
|
109
141
|
params, date, date_from, date_to, updated_at, updated_at_from, updated_at_to
|
|
110
142
|
)
|
|
@@ -125,7 +157,8 @@ class APIClient:
|
|
|
125
157
|
or (model_id is not None and (product is None or "model" in product))
|
|
126
158
|
):
|
|
127
159
|
for key in ("showLegacy", "product", "instrument", "instrumentPid"):
|
|
128
|
-
|
|
160
|
+
if key in params:
|
|
161
|
+
del params[key]
|
|
129
162
|
params["model"] = model_id
|
|
130
163
|
files_res += self._get_response("model-files", params)
|
|
131
164
|
|
|
@@ -418,7 +451,7 @@ def _build_meta_objects(res: list[dict]) -> list[ProductMetadata]:
|
|
|
418
451
|
created_at=_parse_datetime(obj["createdAt"]),
|
|
419
452
|
updated_at=_parse_datetime(obj["updatedAt"]),
|
|
420
453
|
size=int(obj["size"]),
|
|
421
|
-
uuid=
|
|
454
|
+
uuid=UUID(obj["uuid"]),
|
|
422
455
|
site=_create_site_object(obj["site"]),
|
|
423
456
|
)
|
|
424
457
|
for obj in res
|
|
@@ -437,7 +470,7 @@ def _build_raw_meta_objects(res: list[dict]) -> list[RawMetadata]:
|
|
|
437
470
|
created_at=_parse_datetime(obj["createdAt"]),
|
|
438
471
|
updated_at=_parse_datetime(obj["updatedAt"]),
|
|
439
472
|
size=int(obj["size"]),
|
|
440
|
-
uuid=
|
|
473
|
+
uuid=UUID(obj["uuid"]),
|
|
441
474
|
site=_create_site_object(obj["site"]),
|
|
442
475
|
)
|
|
443
476
|
for obj in res
|
|
@@ -456,7 +489,7 @@ def _build_raw_model_meta_objects(res: list[dict]) -> list[RawModelMetadata]:
|
|
|
456
489
|
created_at=_parse_datetime(obj["createdAt"]),
|
|
457
490
|
updated_at=_parse_datetime(obj["updatedAt"]),
|
|
458
491
|
size=int(obj["size"]),
|
|
459
|
-
uuid=
|
|
492
|
+
uuid=UUID(obj["uuid"]),
|
|
460
493
|
site=_create_site_object(obj["site"]),
|
|
461
494
|
)
|
|
462
495
|
for obj in res
|
|
@@ -498,10 +531,10 @@ def _create_site_object(metadata: dict) -> Site:
|
|
|
498
531
|
|
|
499
532
|
def _create_instrument_object(metadata: dict) -> Instrument:
|
|
500
533
|
return Instrument(
|
|
501
|
-
instrument_id=metadata
|
|
534
|
+
instrument_id=metadata.get("instrumentId"), # not in api/files/:uuid
|
|
502
535
|
model=metadata["model"],
|
|
503
536
|
type=metadata["type"],
|
|
504
|
-
uuid=
|
|
537
|
+
uuid=UUID(metadata["uuid"]),
|
|
505
538
|
pid=metadata["pid"],
|
|
506
539
|
owners=metadata["owners"],
|
|
507
540
|
serial_number=metadata["serialNumber"],
|
|
@@ -35,7 +35,7 @@ class Product:
|
|
|
35
35
|
|
|
36
36
|
@dataclass(frozen=True, slots=True)
|
|
37
37
|
class Instrument:
|
|
38
|
-
instrument_id: str # CLU internal identifier, e.g. "rpg-fmcw-94"
|
|
38
|
+
instrument_id: str | None # CLU internal identifier, e.g. "rpg-fmcw-94"
|
|
39
39
|
model: str # From ACTRIS Vocabulary, e.g. "RPG-FMCW-94 DP"
|
|
40
40
|
type: str # From ACTRIS Vocabulary, e.g. "Doppler non-scanning cloud radar"
|
|
41
41
|
name: str # e.g. "FMI RPG-FMCW-94 (Pallas)"
|
|
@@ -86,3 +86,24 @@ class ProductMetadata(Metadata):
|
|
|
86
86
|
product: Product
|
|
87
87
|
instrument: Instrument | None
|
|
88
88
|
model: Model | None
|
|
89
|
+
volatile: bool
|
|
90
|
+
legacy: bool
|
|
91
|
+
pid: str
|
|
92
|
+
dvas_id: str | None
|
|
93
|
+
error_level: str | None
|
|
94
|
+
coverage: float
|
|
95
|
+
timeliness: str
|
|
96
|
+
format: str
|
|
97
|
+
start_time: datetime.datetime | None
|
|
98
|
+
stop_time: datetime.datetime | None
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@dataclass(frozen=True, slots=True)
|
|
102
|
+
class VersionMetadata:
|
|
103
|
+
uuid: uuid.UUID
|
|
104
|
+
created_at: datetime.datetime
|
|
105
|
+
pid: str
|
|
106
|
+
checksum: str
|
|
107
|
+
legacy: bool
|
|
108
|
+
size: int
|
|
109
|
+
dvas_id: str | None
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import base64
|
|
1
2
|
import hashlib
|
|
2
3
|
from os import PathLike
|
|
3
4
|
from typing import Literal
|
|
@@ -7,13 +8,17 @@ def sha256sum(filename: str | PathLike) -> str:
|
|
|
7
8
|
return _calc_hash_sum(filename, "sha256")
|
|
8
9
|
|
|
9
10
|
|
|
10
|
-
def md5sum(filename: str | PathLike) -> str:
|
|
11
|
-
return _calc_hash_sum(filename, "md5")
|
|
11
|
+
def md5sum(filename: str | PathLike, is_base64: bool = False) -> str:
|
|
12
|
+
return _calc_hash_sum(filename, "md5", is_base64)
|
|
12
13
|
|
|
13
14
|
|
|
14
|
-
def _calc_hash_sum(
|
|
15
|
+
def _calc_hash_sum(
|
|
16
|
+
filename: str | PathLike, method: Literal["sha256", "md5"], is_base64: bool = False
|
|
17
|
+
) -> str:
|
|
15
18
|
hash_sum = getattr(hashlib, method)()
|
|
16
19
|
with open(filename, "rb") as f:
|
|
17
20
|
for byte_block in iter(lambda: f.read(4096), b""):
|
|
18
21
|
hash_sum.update(byte_block)
|
|
22
|
+
if is_base64:
|
|
23
|
+
return base64.b64encode(hash_sum.digest()).decode("utf-8")
|
|
19
24
|
return hash_sum.hexdigest()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.11.0"
|
|
@@ -22,12 +22,15 @@ dependencies = ["aiohttp", "numpy", "requests", "tqdm"]
|
|
|
22
22
|
dynamic = ["version"]
|
|
23
23
|
|
|
24
24
|
[project.optional-dependencies]
|
|
25
|
-
test = ["mypy", "pytest"]
|
|
25
|
+
test = ["mypy", "netCDF4", "pytest", "pytest-asyncio"]
|
|
26
26
|
dev = ["pre-commit", "release-version", "types-requests", "types-tqdm"]
|
|
27
27
|
|
|
28
28
|
[tool.hatch.version]
|
|
29
29
|
path = "cloudnet_api_client/version.py"
|
|
30
30
|
|
|
31
|
+
[tool.pytest.ini_options]
|
|
32
|
+
asyncio_mode = "auto"
|
|
33
|
+
|
|
31
34
|
[tool.release-version]
|
|
32
35
|
filename = "cloudnet_api_client/version.py"
|
|
33
36
|
pattern = ["__version__ = \"(?P<major>\\d+).(?P<minor>\\d+).(?P<patch>\\d+)\""]
|
|
Binary file
|