climate-ref 0.6.5__py3-none-any.whl → 0.7.0__py3-none-any.whl
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.
- climate_ref/cli/__init__.py +12 -3
- climate_ref/cli/_utils.py +56 -2
- climate_ref/cli/datasets.py +48 -9
- climate_ref/cli/executions.py +351 -24
- climate_ref/cli/providers.py +1 -2
- climate_ref/config.py +4 -4
- climate_ref/database.py +62 -4
- climate_ref/dataset_registry/obs4ref_reference.txt +0 -9
- climate_ref/dataset_registry/sample_data.txt +269 -107
- climate_ref/datasets/__init__.py +3 -3
- climate_ref/datasets/base.py +121 -20
- climate_ref/datasets/cmip6.py +2 -0
- climate_ref/datasets/obs4mips.py +26 -15
- climate_ref/executor/__init__.py +8 -1
- climate_ref/executor/hpc.py +7 -1
- climate_ref/executor/result_handling.py +151 -64
- climate_ref/migrations/env.py +12 -10
- climate_ref/migrations/versions/2025-07-20T1521_94beace57a9c_cmip6_finalised.py +1 -1
- climate_ref/migrations/versions/2025-08-05T0327_a1b2c3d4e5f6_finalised_on_base_dataset.py +1 -1
- climate_ref/migrations/versions/2025-09-05T2019_8d28e5e0f9c3_add_indexes.py +108 -0
- climate_ref/migrations/versions/2025-09-10T1358_2f6e36738e06_use_version_as_version_facet_for_.py +35 -0
- climate_ref/migrations/versions/2025-09-22T2359_20cd136a5b04_add_pmp_version.py +35 -0
- climate_ref/models/__init__.py +1 -6
- climate_ref/models/base.py +4 -18
- climate_ref/models/dataset.py +10 -6
- climate_ref/models/diagnostic.py +2 -1
- climate_ref/models/execution.py +225 -12
- climate_ref/models/metric_value.py +27 -112
- climate_ref/models/mixins.py +144 -0
- climate_ref/models/provider.py +2 -1
- climate_ref/provider_registry.py +4 -4
- climate_ref/slurm.py +2 -2
- climate_ref/testing.py +1 -1
- {climate_ref-0.6.5.dist-info → climate_ref-0.7.0.dist-info}/METADATA +2 -2
- climate_ref-0.7.0.dist-info/RECORD +58 -0
- climate_ref-0.6.5.dist-info/RECORD +0 -54
- {climate_ref-0.6.5.dist-info → climate_ref-0.7.0.dist-info}/WHEEL +0 -0
- {climate_ref-0.6.5.dist-info → climate_ref-0.7.0.dist-info}/entry_points.txt +0 -0
- {climate_ref-0.6.5.dist-info → climate_ref-0.7.0.dist-info}/licenses/LICENCE +0 -0
- {climate_ref-0.6.5.dist-info → climate_ref-0.7.0.dist-info}/licenses/NOTICE +0 -0
climate_ref/cli/providers.py
CHANGED
|
@@ -7,14 +7,12 @@ from typing import Annotated
|
|
|
7
7
|
import pandas as pd
|
|
8
8
|
import typer
|
|
9
9
|
from loguru import logger
|
|
10
|
-
from rich.console import Console
|
|
11
10
|
|
|
12
11
|
from climate_ref.cli._utils import pretty_print_df
|
|
13
12
|
from climate_ref.provider_registry import ProviderRegistry
|
|
14
13
|
from climate_ref_core.providers import CondaDiagnosticProvider, DiagnosticProvider
|
|
15
14
|
|
|
16
15
|
app = typer.Typer(help=__doc__)
|
|
17
|
-
console = Console()
|
|
18
16
|
|
|
19
17
|
|
|
20
18
|
@app.command(name="list")
|
|
@@ -24,6 +22,7 @@ def list_(ctx: typer.Context) -> None:
|
|
|
24
22
|
"""
|
|
25
23
|
config = ctx.obj.config
|
|
26
24
|
db = ctx.obj.database
|
|
25
|
+
console = ctx.obj.console
|
|
27
26
|
provider_registry = ProviderRegistry.build_from_config(config, db)
|
|
28
27
|
|
|
29
28
|
def get_env(provider: DiagnosticProvider) -> str:
|
climate_ref/config.py
CHANGED
|
@@ -364,10 +364,10 @@ class Config:
|
|
|
364
364
|
- `complete`: Use the complete parser, which parses the dataset based on all available metadata.
|
|
365
365
|
"""
|
|
366
366
|
|
|
367
|
-
paths: PathConfig = Factory(PathConfig)
|
|
368
|
-
db: DbConfig = Factory(DbConfig)
|
|
369
|
-
executor: ExecutorConfig = Factory(ExecutorConfig)
|
|
370
|
-
diagnostic_providers: list[DiagnosticProviderConfig] = Factory(default_providers) # noqa
|
|
367
|
+
paths: PathConfig = Factory(PathConfig)
|
|
368
|
+
db: DbConfig = Factory(DbConfig)
|
|
369
|
+
executor: ExecutorConfig = Factory(ExecutorConfig)
|
|
370
|
+
diagnostic_providers: list[DiagnosticProviderConfig] = Factory(default_providers) # noqa: RUF009, RUF100
|
|
371
371
|
_raw: TOMLDocument | None = field(init=False, default=None, repr=False)
|
|
372
372
|
_config_file: Path | None = field(init=False, default=None, repr=False)
|
|
373
373
|
|
climate_ref/database.py
CHANGED
|
@@ -8,6 +8,7 @@ The `Database` class is the main entry point for interacting with the database.
|
|
|
8
8
|
It provides a session object that can be used to interact with the database and run queries.
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
|
+
import enum
|
|
11
12
|
import importlib.resources
|
|
12
13
|
import shutil
|
|
13
14
|
from datetime import datetime
|
|
@@ -23,6 +24,7 @@ from loguru import logger
|
|
|
23
24
|
from sqlalchemy.orm import Session
|
|
24
25
|
|
|
25
26
|
from climate_ref.models import MetricValue, Table
|
|
27
|
+
from climate_ref.models.execution import ExecutionOutput
|
|
26
28
|
from climate_ref_core.pycmec.controlled_vocabulary import CV
|
|
27
29
|
|
|
28
30
|
if TYPE_CHECKING:
|
|
@@ -135,6 +137,16 @@ def validate_database_url(database_url: str) -> str:
|
|
|
135
137
|
return database_url
|
|
136
138
|
|
|
137
139
|
|
|
140
|
+
class ModelState(enum.Enum):
|
|
141
|
+
"""
|
|
142
|
+
State of a model instance
|
|
143
|
+
"""
|
|
144
|
+
|
|
145
|
+
CREATED = "created"
|
|
146
|
+
UPDATED = "updated"
|
|
147
|
+
DELETED = "deleted"
|
|
148
|
+
|
|
149
|
+
|
|
138
150
|
class Database:
|
|
139
151
|
"""
|
|
140
152
|
Manage the database connection and migrations
|
|
@@ -234,11 +246,57 @@ class Database:
|
|
|
234
246
|
# This will add new columns to the db if the CVs have changed
|
|
235
247
|
MetricValue.register_cv_dimensions(cv)
|
|
236
248
|
|
|
249
|
+
# Register the CV dimensions with the ExecutionOutput model
|
|
250
|
+
# This enables dimension-based filtering of outputs
|
|
251
|
+
ExecutionOutput.register_cv_dimensions(cv)
|
|
252
|
+
|
|
237
253
|
return db
|
|
238
254
|
|
|
255
|
+
def update_or_create(
|
|
256
|
+
self, model: type[Table], defaults: dict[str, Any] | None = None, **kwargs: Any
|
|
257
|
+
) -> tuple[Table, ModelState | None]:
|
|
258
|
+
"""
|
|
259
|
+
Update an existing instance or create a new one
|
|
260
|
+
|
|
261
|
+
This doesn't commit the transaction,
|
|
262
|
+
so you will need to call `session.commit()` after this method
|
|
263
|
+
or use a transaction context manager.
|
|
264
|
+
|
|
265
|
+
Parameters
|
|
266
|
+
----------
|
|
267
|
+
model
|
|
268
|
+
The model to update or create
|
|
269
|
+
defaults
|
|
270
|
+
Default values to use when creating a new instance, or values to update on existing instance
|
|
271
|
+
kwargs
|
|
272
|
+
The filter parameters to use when querying for an instance
|
|
273
|
+
|
|
274
|
+
Returns
|
|
275
|
+
-------
|
|
276
|
+
:
|
|
277
|
+
A tuple containing the instance and a state enum indicating if the instance was created or updated
|
|
278
|
+
"""
|
|
279
|
+
instance = self.session.query(model).filter_by(**kwargs).first()
|
|
280
|
+
state: ModelState | None = None
|
|
281
|
+
if instance:
|
|
282
|
+
# Update existing instance with defaults
|
|
283
|
+
if defaults:
|
|
284
|
+
for key, value in defaults.items():
|
|
285
|
+
if getattr(instance, key) != value:
|
|
286
|
+
logger.debug(f"Updating {model.__name__} {key} to {value}")
|
|
287
|
+
setattr(instance, key, value)
|
|
288
|
+
state = ModelState.UPDATED
|
|
289
|
+
return instance, state
|
|
290
|
+
else:
|
|
291
|
+
# Create new instance
|
|
292
|
+
params = {**kwargs, **(defaults or {})}
|
|
293
|
+
instance = model(**params)
|
|
294
|
+
self.session.add(instance)
|
|
295
|
+
return instance, ModelState.CREATED
|
|
296
|
+
|
|
239
297
|
def get_or_create(
|
|
240
298
|
self, model: type[Table], defaults: dict[str, Any] | None = None, **kwargs: Any
|
|
241
|
-
) -> tuple[Table,
|
|
299
|
+
) -> tuple[Table, ModelState | None]:
|
|
242
300
|
"""
|
|
243
301
|
Get or create an instance of a model
|
|
244
302
|
|
|
@@ -258,13 +316,13 @@ class Database:
|
|
|
258
316
|
Returns
|
|
259
317
|
-------
|
|
260
318
|
:
|
|
261
|
-
A tuple containing the instance and
|
|
319
|
+
A tuple containing the instance and enum indicating if the instance was created
|
|
262
320
|
"""
|
|
263
321
|
instance = self.session.query(model).filter_by(**kwargs).first()
|
|
264
322
|
if instance:
|
|
265
|
-
return instance,
|
|
323
|
+
return instance, None
|
|
266
324
|
else:
|
|
267
325
|
params = {**kwargs, **(defaults or {})}
|
|
268
326
|
instance = model(**params)
|
|
269
327
|
self.session.add(instance)
|
|
270
|
-
return instance,
|
|
328
|
+
return instance, ModelState.CREATED
|
|
@@ -5,15 +5,6 @@ obs4REF/ColumbiaU/WECANN-1-0/mon/hfls/gn/20250516/hfls_mon_WECANN-1-0_REF_gn_200
|
|
|
5
5
|
obs4REF/ColumbiaU/WECANN-1-0/mon/hfss/gn/20250516/hfss_mon_WECANN-1-0_REF_gn_200701-201512.nc md5:b7a911e0fc164d07d3ab42a86d09b18b
|
|
6
6
|
obs4REF/ECMWF/ERA-20C/mon/psl/gn/v20210727/psl_mon_ERA-20C_PCMDI_gn_190001-201012.nc md5:c100cf25d5681c375cd6c1ee60b678ba
|
|
7
7
|
obs4REF/ECMWF/ERA-20C/mon/ts/gn/v20210727/ts_mon_ERA-20C_PCMDI_gn_190001-201012.nc md5:9ed8dfbb805ed4caa282ed70f873a3a0
|
|
8
|
-
obs4REF/ECMWF/ERA-5/mon/ta/gn/v20250220/ta_mon_ERA-5_PCMDI_gn_200701-200712.nc md5:695633a2b401cfb66c8addbf58073dbc
|
|
9
|
-
obs4REF/ECMWF/ERA-5/mon/ta/gn/v20250220/ta_mon_ERA-5_PCMDI_gn_200801-200812.nc md5:404f1e1f111859be06c00bcb8d740ff2
|
|
10
|
-
obs4REF/ECMWF/ERA-5/mon/ta/gn/v20250220/ta_mon_ERA-5_PCMDI_gn_200901-200912.nc md5:a1bb8584d60cdd71154c01a692fa1fb4
|
|
11
|
-
obs4REF/ECMWF/ERA-5/mon/ta/gn/v20250220/ta_mon_ERA-5_PCMDI_gn_201001-201012.nc md5:b78016a3c61d99dc0fd29563aa344ca1
|
|
12
|
-
obs4REF/ECMWF/ERA-5/mon/ta/gn/v20250220/ta_mon_ERA-5_PCMDI_gn_201101-201112.nc md5:d64c231a7f798a255997ffe196613ea1
|
|
13
|
-
obs4REF/ECMWF/ERA-5/mon/ta/gn/v20250220/ta_mon_ERA-5_PCMDI_gn_201201-201212.nc md5:7d90ce60b872dc4f044b9b0101114983
|
|
14
|
-
obs4REF/ECMWF/ERA-5/mon/ta/gn/v20250220/ta_mon_ERA-5_PCMDI_gn_201301-201312.nc md5:2fc032707cb8a31ac60fa4abe9efe183
|
|
15
|
-
obs4REF/ECMWF/ERA-5/mon/ta/gn/v20250220/ta_mon_ERA-5_PCMDI_gn_201401-201412.nc md5:6022d17e11df7818f5b0429d6e401d17
|
|
16
|
-
obs4REF/ECMWF/ERA-5/mon/ta/gn/v20250220/ta_mon_ERA-5_PCMDI_gn_201501-201512.nc md5:c68fdabf6eeb4813befceace089c9494
|
|
17
8
|
obs4REF/ECMWF/ERA-INT/mon/hfls/gn/v20210727/hfls_mon_ERA-INT_PCMDI_gn_197901-201903.nc md5:1ae4587143f05ee81432b3d9960aab63
|
|
18
9
|
obs4REF/ECMWF/ERA-INT/mon/hfss/gn/v20210727/hfss_mon_ERA-INT_PCMDI_gn_197901-201903.nc md5:261f02b8cbce18486548882a11f9aa34
|
|
19
10
|
obs4REF/ECMWF/ERA-INT/mon/hur/gn/v20210727/hur_mon_ERA-INT_PCMDI_gn_198901-201001.nc md5:56fcd2df8ed2879f18b5e8c78134a148
|