pastastore 1.10.2__py3-none-any.whl → 1.12.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.
- docs/conf.py +10 -97
- pastastore/__init__.py +5 -1
- pastastore/base.py +875 -272
- pastastore/connectors.py +359 -816
- pastastore/datasets.py +23 -33
- pastastore/extensions/__init__.py +7 -3
- pastastore/extensions/hpd.py +39 -17
- pastastore/plotting.py +71 -38
- pastastore/store.py +205 -186
- pastastore/styling.py +4 -2
- pastastore/typing.py +12 -0
- pastastore/util.py +322 -88
- pastastore/validator.py +524 -0
- pastastore/version.py +2 -3
- pastastore/yaml_interface.py +37 -39
- {pastastore-1.10.2.dist-info → pastastore-1.12.0.dist-info}/METADATA +17 -11
- pastastore-1.12.0.dist-info/RECORD +31 -0
- {pastastore-1.10.2.dist-info → pastastore-1.12.0.dist-info}/WHEEL +1 -1
- tests/conftest.py +156 -59
- tests/test_001_import.py +2 -1
- tests/test_002_connectors.py +40 -3
- tests/test_003_pastastore.py +60 -29
- tests/test_005_maps_plots.py +12 -0
- tests/test_006_benchmark.py +1 -1
- tests/test_007_hpdextension.py +46 -8
- tests/test_009_parallel.py +393 -0
- pastastore-1.10.2.dist-info/RECORD +0 -28
- {pastastore-1.10.2.dist-info → pastastore-1.12.0.dist-info}/licenses/LICENSE +0 -0
- {pastastore-1.10.2.dist-info → pastastore-1.12.0.dist-info}/top_level.txt +0 -0
pastastore/yaml_interface.py
CHANGED
|
@@ -6,6 +6,7 @@ import os
|
|
|
6
6
|
import tempfile
|
|
7
7
|
from contextlib import contextmanager
|
|
8
8
|
from copy import deepcopy
|
|
9
|
+
from pathlib import Path
|
|
9
10
|
from typing import Any, Dict, List, Optional, Union
|
|
10
11
|
|
|
11
12
|
import numpy as np
|
|
@@ -13,8 +14,6 @@ import pandas as pd
|
|
|
13
14
|
import pastas as ps
|
|
14
15
|
import yaml
|
|
15
16
|
|
|
16
|
-
from pastastore.version import PASTAS_LEQ_022
|
|
17
|
-
|
|
18
17
|
logger = logging.getLogger(__name__)
|
|
19
18
|
|
|
20
19
|
|
|
@@ -38,9 +37,9 @@ def _convert_dict_dtypes_for_yaml(d: Dict[str, Any]):
|
|
|
38
37
|
elif isinstance(v, datetime.datetime):
|
|
39
38
|
d[k] = pd.to_datetime(v).strftime("%Y-%m-%d %H:%M:%S")
|
|
40
39
|
elif isinstance(v, pd.Timedelta):
|
|
41
|
-
d[k] = v.to_timedelta64()
|
|
40
|
+
d[k] = str(v.to_timedelta64())
|
|
42
41
|
elif isinstance(v, datetime.timedelta):
|
|
43
|
-
d[k] = pd.to_timedelta(v).to_timedelta64()
|
|
42
|
+
d[k] = str(pd.to_timedelta(v).to_timedelta64())
|
|
44
43
|
elif isinstance(v, np.int64):
|
|
45
44
|
d[k] = int(v)
|
|
46
45
|
elif isinstance(v, np.float64):
|
|
@@ -109,7 +108,7 @@ def reduce_to_minimal_dict(d, keys=None):
|
|
|
109
108
|
"stress",
|
|
110
109
|
"prec",
|
|
111
110
|
"evap",
|
|
112
|
-
"
|
|
111
|
+
"class",
|
|
113
112
|
]
|
|
114
113
|
|
|
115
114
|
# also keep stressmodels by adding names to keys list
|
|
@@ -133,7 +132,7 @@ def temporary_yaml_from_str(yaml):
|
|
|
133
132
|
temp.write(yaml.encode("utf-8"))
|
|
134
133
|
temp.close()
|
|
135
134
|
try:
|
|
136
|
-
yield temp.name
|
|
135
|
+
yield Path(temp.name)
|
|
137
136
|
finally:
|
|
138
137
|
os.unlink(temp.name)
|
|
139
138
|
|
|
@@ -230,7 +229,7 @@ class PastastoreYAML:
|
|
|
230
229
|
else:
|
|
231
230
|
kind = "prec"
|
|
232
231
|
pnam = self.pstore.get_nearest_stresses(onam, kind=kind).iloc[0, 0]
|
|
233
|
-
logger.info(
|
|
232
|
+
logger.info(" | using nearest stress with kind='%s': '%s'", kind, pnam)
|
|
234
233
|
p, pmeta = self.pstore.get_stresses(pnam, return_metadata=True)
|
|
235
234
|
prec = {
|
|
236
235
|
"name": pnam,
|
|
@@ -266,7 +265,7 @@ class PastastoreYAML:
|
|
|
266
265
|
else:
|
|
267
266
|
kind = "evap"
|
|
268
267
|
enam = self.pstore.get_nearest_stresses(onam, kind=kind).iloc[0, 0]
|
|
269
|
-
logger.info(
|
|
268
|
+
logger.info(" | using nearest stress with kind='%s': '%s'", kind, enam)
|
|
270
269
|
e, emeta = self.pstore.get_stresses(enam, return_metadata=True)
|
|
271
270
|
evap = {
|
|
272
271
|
"name": enam,
|
|
@@ -291,11 +290,11 @@ class PastastoreYAML:
|
|
|
291
290
|
if "rfunc" not in d:
|
|
292
291
|
logger.info(" | no 'rfunc' provided, using 'Exponential'")
|
|
293
292
|
# for pastas >= 0.23.0, convert rfunc value to dictionary with 'class' key
|
|
294
|
-
elif not isinstance(d["rfunc"], dict)
|
|
293
|
+
elif not isinstance(d["rfunc"], dict):
|
|
295
294
|
d["rfunc"] = {"class": d["rfunc"]}
|
|
296
295
|
|
|
297
296
|
# stressmodel
|
|
298
|
-
classkey = "
|
|
297
|
+
classkey = "class"
|
|
299
298
|
if classkey not in d:
|
|
300
299
|
d[classkey] = "RechargeModel"
|
|
301
300
|
|
|
@@ -303,7 +302,7 @@ class PastastoreYAML:
|
|
|
303
302
|
if ("recharge" not in d) and (d[classkey] == "RechargeModel"):
|
|
304
303
|
logger.info(" | no 'recharge' type provided, using 'Linear'")
|
|
305
304
|
# if pastas >= 0.23.0, recharge value must be dict with class key
|
|
306
|
-
elif not isinstance(d["recharge"], dict)
|
|
305
|
+
elif not isinstance(d["recharge"], dict):
|
|
307
306
|
d["recharge"] = {"class": d["recharge"]}
|
|
308
307
|
|
|
309
308
|
# tarsomodel logic
|
|
@@ -314,7 +313,8 @@ class PastastoreYAML:
|
|
|
314
313
|
if ((dmin is None) or (dmax is None)) and (oseries is None):
|
|
315
314
|
logger.info(
|
|
316
315
|
" | no 'dmin/dmax' or 'oseries' provided,"
|
|
317
|
-
|
|
316
|
+
" filling in 'oseries': '%s'",
|
|
317
|
+
onam,
|
|
318
318
|
)
|
|
319
319
|
d["oseries"] = onam
|
|
320
320
|
|
|
@@ -365,7 +365,7 @@ class PastastoreYAML:
|
|
|
365
365
|
snam = self.pstore.get_nearest_oseries(onam).iloc[0, 0]
|
|
366
366
|
else:
|
|
367
367
|
snam = self.pstore.get_nearest_stresses(onam, kind=kind).iloc[0, 0]
|
|
368
|
-
logger.info(
|
|
368
|
+
logger.info(" | using nearest stress with kind='%s': %s", kind, snam)
|
|
369
369
|
|
|
370
370
|
s, smeta = self.pstore.get_stresses(snam, return_metadata=True)
|
|
371
371
|
s = {
|
|
@@ -374,7 +374,7 @@ class PastastoreYAML:
|
|
|
374
374
|
"metadata": smeta,
|
|
375
375
|
"series": s.squeeze(),
|
|
376
376
|
}
|
|
377
|
-
d["stress"] =
|
|
377
|
+
d["stress"] = s
|
|
378
378
|
|
|
379
379
|
# use stress name if not provided
|
|
380
380
|
if "name" not in d:
|
|
@@ -383,9 +383,9 @@ class PastastoreYAML:
|
|
|
383
383
|
# rfunc
|
|
384
384
|
if "rfunc" not in d:
|
|
385
385
|
logger.info(" | no 'rfunc' provided, using 'Gamma'")
|
|
386
|
-
d["rfunc"] =
|
|
386
|
+
d["rfunc"] = {"class": "Gamma"}
|
|
387
387
|
# for pastas >= 0.23.0, convert rfunc value to dictionary with 'class' key
|
|
388
|
-
elif not isinstance(d["rfunc"], dict)
|
|
388
|
+
elif not isinstance(d["rfunc"], dict):
|
|
389
389
|
d["rfunc"] = {"class": d["rfunc"]}
|
|
390
390
|
|
|
391
391
|
return d
|
|
@@ -441,7 +441,10 @@ class PastastoreYAML:
|
|
|
441
441
|
.values
|
|
442
442
|
)
|
|
443
443
|
logger.info(
|
|
444
|
-
|
|
444
|
+
" | using %d nearest stress(es) with kind='%s': %s",
|
|
445
|
+
n,
|
|
446
|
+
kind,
|
|
447
|
+
snames,
|
|
445
448
|
)
|
|
446
449
|
else:
|
|
447
450
|
snames = [snames]
|
|
@@ -472,11 +475,9 @@ class PastastoreYAML:
|
|
|
472
475
|
# rfunc
|
|
473
476
|
if "rfunc" not in d:
|
|
474
477
|
logger.info(" | no 'rfunc' provided, using 'HantushWellModel'")
|
|
475
|
-
d["rfunc"] =
|
|
476
|
-
"HantushWellModel" if PASTAS_LEQ_022 else {"class": "HantushWellModel"}
|
|
477
|
-
)
|
|
478
|
+
d["rfunc"] = {"class": "HantushWellModel"}
|
|
478
479
|
# for pastas >= 0.23.0, convert rfunc value to dictionary with 'class' key
|
|
479
|
-
elif not isinstance(d["rfunc"], dict)
|
|
480
|
+
elif not isinstance(d["rfunc"], dict):
|
|
480
481
|
d["rfunc"] = {"class": d["rfunc"]}
|
|
481
482
|
|
|
482
483
|
if "up" not in d:
|
|
@@ -510,7 +511,7 @@ class PastastoreYAML:
|
|
|
510
511
|
else:
|
|
511
512
|
onam = str(mlyml.pop("oseries"))
|
|
512
513
|
|
|
513
|
-
logger.info(
|
|
514
|
+
logger.info("Building model '%s' for oseries '%s'", mlnam, onam)
|
|
514
515
|
o, ometa = self.pstore.get_oseries(onam, return_metadata=True)
|
|
515
516
|
|
|
516
517
|
# create model to obtain default model settings
|
|
@@ -528,14 +529,11 @@ class PastastoreYAML:
|
|
|
528
529
|
name = smyml.get("name", smnam)
|
|
529
530
|
else:
|
|
530
531
|
name = smnam
|
|
531
|
-
logger.info(
|
|
532
|
+
logger.info("| parsing stressmodel: '%s'", name)
|
|
532
533
|
|
|
533
534
|
# check whether smtyp is defined
|
|
534
|
-
classkey = "
|
|
535
|
+
classkey = "class"
|
|
535
536
|
if smyml is not None:
|
|
536
|
-
if PASTAS_LEQ_022:
|
|
537
|
-
if "class" in smyml:
|
|
538
|
-
smyml["stressmodel"] = smyml.pop("class")
|
|
539
537
|
if classkey in smyml:
|
|
540
538
|
smtyp = True
|
|
541
539
|
else:
|
|
@@ -637,10 +635,10 @@ class PastastoreYAML:
|
|
|
637
635
|
"""
|
|
638
636
|
if "\n" in fyaml or "\r" in fyaml:
|
|
639
637
|
with temporary_yaml_from_str(fyaml) as fyaml:
|
|
640
|
-
with open(
|
|
638
|
+
with fyaml.open("r", encoding="utf-8") as f:
|
|
641
639
|
yml = yaml.load(f, Loader=yaml.CFullLoader)
|
|
642
|
-
elif
|
|
643
|
-
with open(
|
|
640
|
+
elif Path(fyaml).exists():
|
|
641
|
+
with Path(fyaml).open("r", encoding="utf-8") as f:
|
|
644
642
|
yml = yaml.load(f, Loader=yaml.CFullLoader)
|
|
645
643
|
else:
|
|
646
644
|
raise ValueError(
|
|
@@ -655,8 +653,8 @@ class PastastoreYAML:
|
|
|
655
653
|
|
|
656
654
|
mldict = self.construct_mldict(mlyml, mlnam)
|
|
657
655
|
|
|
658
|
-
#
|
|
659
|
-
ml = ps.io.base._load_model(mldict)
|
|
656
|
+
# Use pastas' internal _load_model - required for model reconstruction
|
|
657
|
+
ml = ps.io.base._load_model(mldict) # noqa: SLF001
|
|
660
658
|
models.append(ml)
|
|
661
659
|
|
|
662
660
|
return models
|
|
@@ -664,7 +662,7 @@ class PastastoreYAML:
|
|
|
664
662
|
def export_stored_models_per_oseries(
|
|
665
663
|
self,
|
|
666
664
|
oseries: Optional[Union[List[str], str]] = None,
|
|
667
|
-
outdir: Optional[str] = ".",
|
|
665
|
+
outdir: Optional[Path | str] = ".",
|
|
668
666
|
minimal_yaml: Optional[bool] = False,
|
|
669
667
|
use_nearest: Optional[bool] = False,
|
|
670
668
|
):
|
|
@@ -690,7 +688,7 @@ class PastastoreYAML:
|
|
|
690
688
|
the time series are actually the nearest ones! Only used
|
|
691
689
|
when minimal_yaml=True. Default is False.
|
|
692
690
|
"""
|
|
693
|
-
onames = self.pstore.conn.
|
|
691
|
+
onames = self.pstore.conn.parse_names(oseries, "oseries")
|
|
694
692
|
|
|
695
693
|
for onam in onames:
|
|
696
694
|
try:
|
|
@@ -716,7 +714,7 @@ class PastastoreYAML:
|
|
|
716
714
|
name = d.pop("name")
|
|
717
715
|
model_dicts[name] = d
|
|
718
716
|
|
|
719
|
-
with
|
|
717
|
+
with (Path(outdir) / f"{onam}.yaml").open("w", encoding="utf-8") as f:
|
|
720
718
|
yaml.dump(model_dicts, f, Dumper=yaml.CDumper)
|
|
721
719
|
|
|
722
720
|
def export_models(
|
|
@@ -758,7 +756,7 @@ class PastastoreYAML:
|
|
|
758
756
|
filename for YAML file, only used if `split=False`
|
|
759
757
|
"""
|
|
760
758
|
if models is None:
|
|
761
|
-
modelnames = self.pstore.conn.
|
|
759
|
+
modelnames = self.pstore.conn.parse_names(modelnames, "models")
|
|
762
760
|
model_list = self.pstore.get_models(
|
|
763
761
|
modelnames, return_dict=True, squeeze=False
|
|
764
762
|
)
|
|
@@ -783,13 +781,13 @@ class PastastoreYAML:
|
|
|
783
781
|
name = d.pop("name")
|
|
784
782
|
model_dicts[name] = d
|
|
785
783
|
|
|
786
|
-
with
|
|
784
|
+
with (Path(outdir) / filename).open("w", encoding="utf-8") as f:
|
|
787
785
|
yaml.dump(model_dicts, f, Dumper=yaml.CDumper)
|
|
788
786
|
|
|
789
787
|
@staticmethod
|
|
790
788
|
def export_model(
|
|
791
789
|
ml: Union[ps.Model, dict],
|
|
792
|
-
outdir: Optional[str] = ".",
|
|
790
|
+
outdir: Optional[Path | str] = ".",
|
|
793
791
|
minimal_yaml: Optional[bool] = False,
|
|
794
792
|
use_nearest: Optional[bool] = False,
|
|
795
793
|
):
|
|
@@ -816,7 +814,7 @@ class PastastoreYAML:
|
|
|
816
814
|
name = ml["name"]
|
|
817
815
|
else:
|
|
818
816
|
name = ml.name
|
|
819
|
-
with
|
|
817
|
+
with (Path(outdir) / f"{name}.yaml").open("w", encoding="utf-8") as f:
|
|
820
818
|
if isinstance(ml, ps.Model):
|
|
821
819
|
mldict = deepcopy(ml.to_dict(series=False))
|
|
822
820
|
elif isinstance(ml, dict):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pastastore
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.12.0
|
|
4
4
|
Summary: Tools for managing Pastas time series models.
|
|
5
5
|
Author: D.A. Brakenhoff
|
|
6
6
|
Maintainer-email: "D.A. Brakenhoff" <d.brakenhoff@artesia-water.nl>, "R. Calje" <r.calje@artesia-water.nl>, "M.A. Vonk" <m.vonk@artesia-water.nl>
|
|
@@ -46,12 +46,14 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
46
46
|
Classifier: Programming Language :: Python :: 3.13
|
|
47
47
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
48
48
|
Classifier: Topic :: Scientific/Engineering :: Hydrology
|
|
49
|
-
Requires-Python: >=3.
|
|
49
|
+
Requires-Python: >=3.11
|
|
50
50
|
Description-Content-Type: text/markdown
|
|
51
51
|
License-File: LICENSE
|
|
52
52
|
Requires-Dist: pastas>=0.13
|
|
53
53
|
Requires-Dist: tqdm>=4.36
|
|
54
54
|
Requires-Dist: pyyaml
|
|
55
|
+
Requires-Dist: colorama
|
|
56
|
+
Requires-Dist: pandas<3.0
|
|
55
57
|
Provides-Extra: full
|
|
56
58
|
Requires-Dist: pastastore[arcticdb,optional]; extra == "full"
|
|
57
59
|
Requires-Dist: hydropandas; extra == "full"
|
|
@@ -63,7 +65,6 @@ Requires-Dist: pyproj; extra == "optional"
|
|
|
63
65
|
Requires-Dist: adjustText; extra == "optional"
|
|
64
66
|
Provides-Extra: arcticdb
|
|
65
67
|
Requires-Dist: arcticdb; extra == "arcticdb"
|
|
66
|
-
Requires-Dist: protobuf<6,>=3.5.0.post1; extra == "arcticdb"
|
|
67
68
|
Provides-Extra: lint
|
|
68
69
|
Requires-Dist: ruff; extra == "lint"
|
|
69
70
|
Provides-Extra: pytest
|
|
@@ -74,16 +75,17 @@ Requires-Dist: pytest-cov; extra == "pytest"
|
|
|
74
75
|
Requires-Dist: pytest-dependency; extra == "pytest"
|
|
75
76
|
Requires-Dist: pytest-benchmark; extra == "pytest"
|
|
76
77
|
Requires-Dist: codacy-coverage; extra == "pytest"
|
|
77
|
-
Provides-Extra: test
|
|
78
|
-
Requires-Dist: pastastore[arcticdb,lint,optional,pytest]; extra == "test"
|
|
79
|
-
Requires-Dist: hydropandas[full]; extra == "test"
|
|
80
78
|
Provides-Extra: docs
|
|
81
79
|
Requires-Dist: pastastore[optional]; extra == "docs"
|
|
82
80
|
Requires-Dist: sphinx_rtd_theme; extra == "docs"
|
|
83
81
|
Requires-Dist: Ipython; extra == "docs"
|
|
84
82
|
Requires-Dist: ipykernel; extra == "docs"
|
|
85
|
-
Requires-Dist:
|
|
86
|
-
|
|
83
|
+
Requires-Dist: myst_nb; extra == "docs"
|
|
84
|
+
Provides-Extra: test
|
|
85
|
+
Requires-Dist: pastastore[arcticdb,optional,pytest]; extra == "test"
|
|
86
|
+
Requires-Dist: hydropandas[full]; extra == "test"
|
|
87
|
+
Provides-Extra: dev
|
|
88
|
+
Requires-Dist: pastastore[docs,lint,test]; extra == "dev"
|
|
87
89
|
Dynamic: license-file
|
|
88
90
|
|
|
89
91
|

|
|
@@ -106,8 +108,12 @@ left off without having to reload everything.
|
|
|
106
108
|
|
|
107
109
|
Install the module with `pip install pastastore`.
|
|
108
110
|
|
|
109
|
-
For
|
|
110
|
-
|
|
111
|
+
For development, clone the repository and install all development, testing, and
|
|
112
|
+
documentation dependencies with:
|
|
113
|
+
|
|
114
|
+
```sh
|
|
115
|
+
pip install -e .[dev]
|
|
116
|
+
```
|
|
111
117
|
|
|
112
118
|
For plotting background maps, the `contextily` and `pyproj` packages are
|
|
113
119
|
required. For a full install, including optional dependencies for plotting and
|
|
@@ -180,4 +186,4 @@ pstore.to_zip("my_backup.zip")
|
|
|
180
186
|
```
|
|
181
187
|
|
|
182
188
|
For more elaborate examples, refer to the
|
|
183
|
-
[Notebooks](https://pastastore.readthedocs.io/latest/examples.html#example-notebooks).
|
|
189
|
+
[Notebooks](https://pastastore.readthedocs.io/en/latest/examples.html#example-notebooks).
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
docs/conf.py,sha256=RFWVsGSGtqXc-N0ett9Z5XVrTKDzj3ukbXPQZM9wQIM,4186
|
|
2
|
+
pastastore/__init__.py,sha256=It_5KcVu7tMdo0QL9DXUNWtMKUDSLNfjzcuomkRdnUE,547
|
|
3
|
+
pastastore/base.py,sha256=KCfv13XnY7ryXqZ1Oz02ROvitJ_NndxhkrtXL5FINSQ,72407
|
|
4
|
+
pastastore/connectors.py,sha256=ArR053kp8ogwbtRgSCOC8f5wyMw16WNSLRzb_Kx1b6k,35291
|
|
5
|
+
pastastore/datasets.py,sha256=0ZjeXRX5uRTAbZPfAVoLjfR2sh0MDpMSatfKu7Imeg0,6007
|
|
6
|
+
pastastore/plotting.py,sha256=Zvesn7yY_z4LXdTLNT5TDui1YJ1wcm_yTvgqw6h2iQg,55697
|
|
7
|
+
pastastore/store.py,sha256=ymXP9UyXPqARlJ6lNPasucFe1qURf3bn5Sc1oBZx6xY,70004
|
|
8
|
+
pastastore/styling.py,sha256=aw7rTrllLyL574EcLqMSwNTuQaT3jpFAK92MyJzhLL8,2423
|
|
9
|
+
pastastore/typing.py,sha256=wsn50wH_LV3J6WXNpAUcRnSJppmGqYsX6kGCg49uGzY,376
|
|
10
|
+
pastastore/util.py,sha256=VrtJUYaFGnYApbcSBZZbc2NRdy8S93v-FyuT1bq3fjY,35578
|
|
11
|
+
pastastore/validator.py,sha256=Kwl0QoLCOYk1eLbBhaaZJNq0XMtsRwM2Uo1qlWu_f5w,20236
|
|
12
|
+
pastastore/version.py,sha256=Af4ycb4-UpzBjuM58vVI_e7Nw5K5IZJovwNmfp4B3xg,1148
|
|
13
|
+
pastastore/yaml_interface.py,sha256=x05edO_N-F9ia-nn5LlKzGdGATFHaGs9rwQAGvuDrnM,30577
|
|
14
|
+
pastastore/extensions/__init__.py,sha256=pHZQha6yhq3fwsoDWvXW-lYEbUUmlfCcHMrYoK_1Hxs,505
|
|
15
|
+
pastastore/extensions/accessor.py,sha256=kftQM6dqMDoySbyTKcvmkjC5gJRp465KA18G4NVXUO0,367
|
|
16
|
+
pastastore/extensions/hpd.py,sha256=bTX3UebPKAVZikIgDfHh6xrK7sHVAT2EbiImh1e7_OE,29728
|
|
17
|
+
pastastore-1.12.0.dist-info/licenses/LICENSE,sha256=MB_6p4kXDCUsYNjslcMByBu6i7wMNRKPC36JnhzpN4o,1087
|
|
18
|
+
tests/conftest.py,sha256=5fko9oJ7vsFkxhvldHSRHa0XA6TzUgYOHZMSSn9f6ho,7943
|
|
19
|
+
tests/test_001_import.py,sha256=o31MWpMNxngnytJeEnPz_0UTcT-XbG1JqggvrWLvSUY,238
|
|
20
|
+
tests/test_002_connectors.py,sha256=o5uXrylGUiHvOAaQ-IqhM8LJ4WC1ktHBecmZ0TUreqg,9442
|
|
21
|
+
tests/test_003_pastastore.py,sha256=7Ebtk-f963b4QAOG8TAJxipM5IEpxfFuLbaG0i0MMlA,11456
|
|
22
|
+
tests/test_004_yaml.py,sha256=3hMNjb9s0S2rbmpyEjW6FDRAxfUZS_U1qoPl4wB-cCo,4440
|
|
23
|
+
tests/test_005_maps_plots.py,sha256=HVfkTQhtpONl8N8Uv39nJMrkLYOmMYy9T2AjpzYmUp4,2152
|
|
24
|
+
tests/test_006_benchmark.py,sha256=hEk2kpYKpghI7RDQaSuWkSzkF9lNphm8VgBxouVtWAU,4940
|
|
25
|
+
tests/test_007_hpdextension.py,sha256=QKXct8sBKIypAYmI7URypYPaAtCJgM464u7D3yJMNow,4304
|
|
26
|
+
tests/test_008_stressmodels.py,sha256=733fyCvuzjKcaLjvSMt5dTTLp-T4alzNJAToSxTIUug,4003
|
|
27
|
+
tests/test_009_parallel.py,sha256=aDFEcLSz9cwbgKXpbRmNwIKA2IQ3rAPLYnA-U1nDZ1o,15039
|
|
28
|
+
pastastore-1.12.0.dist-info/METADATA,sha256=qHAscjDQ4XPmM2gVLRDBqYtHLuo8OPkRrFwc5qGZbXA,7696
|
|
29
|
+
pastastore-1.12.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
30
|
+
pastastore-1.12.0.dist-info/top_level.txt,sha256=1bgyMk1p23f04RK83Jju2_YAQBwyoQD_fInxoPB4YRw,22
|
|
31
|
+
pastastore-1.12.0.dist-info/RECORD,,
|
tests/conftest.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# ruff: noqa: D100 D103
|
|
2
2
|
import importlib
|
|
3
|
+
import inspect
|
|
3
4
|
from importlib import metadata
|
|
4
5
|
|
|
5
6
|
import pandas as pd
|
|
@@ -8,54 +9,94 @@ import pytest
|
|
|
8
9
|
|
|
9
10
|
import pastastore as pst
|
|
10
11
|
|
|
11
|
-
params = [
|
|
12
|
+
params = [
|
|
13
|
+
"dict",
|
|
14
|
+
"pas",
|
|
15
|
+
"arcticdb",
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@pytest.fixture(scope="module")
|
|
20
|
+
def data1():
|
|
21
|
+
d = {
|
|
22
|
+
"oseries1": pd.read_csv("./tests/data/obs.csv", index_col=0, parse_dates=True),
|
|
23
|
+
"oseries1_meta": {"x": 165000, "y": 424000},
|
|
24
|
+
"prec1": pd.read_csv("./tests/data/rain.csv", index_col=0, parse_dates=True),
|
|
25
|
+
"prec1_meta": {"x": 165050, "y": 424050},
|
|
26
|
+
"evap1": pd.read_csv("./tests/data/evap.csv", index_col=0, parse_dates=True),
|
|
27
|
+
"evap1_meta": {"x": 164500, "y": 424000},
|
|
28
|
+
}
|
|
29
|
+
return d
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@pytest.fixture(scope="module")
|
|
33
|
+
def data2():
|
|
34
|
+
d = {
|
|
35
|
+
"oseries2": pd.read_csv(
|
|
36
|
+
"./tests/data/head_nb1.csv", index_col=0, parse_dates=True
|
|
37
|
+
),
|
|
38
|
+
"oseries2_meta": {"x": 164000, "y": 423000},
|
|
39
|
+
"prec2": pd.read_csv(
|
|
40
|
+
"./tests/data/rain_nb1.csv", index_col=0, parse_dates=True
|
|
41
|
+
),
|
|
42
|
+
"prec2_meta": {"x": 164010, "y": 423000},
|
|
43
|
+
"evap2": pd.read_csv(
|
|
44
|
+
"./tests/data/evap_nb1.csv", index_col=0, parse_dates=True
|
|
45
|
+
),
|
|
46
|
+
"evap2_meta": {"x": 164000, "y": 423030},
|
|
47
|
+
}
|
|
48
|
+
return d
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@pytest.fixture(scope="module")
|
|
52
|
+
def data3():
|
|
53
|
+
w = pd.read_csv("./tests/data/well_month_end.csv", index_col=0, parse_dates=True)
|
|
54
|
+
w = ps.ts.timestep_weighted_resample(
|
|
55
|
+
w,
|
|
56
|
+
pd.date_range(w.index[0] - pd.offsets.MonthBegin(), w.index[-1], freq="D"),
|
|
57
|
+
).bfill()
|
|
58
|
+
|
|
59
|
+
d = {
|
|
60
|
+
"oseries3": pd.read_csv(
|
|
61
|
+
"./tests/data/gw_obs.csv", index_col=0, parse_dates=True
|
|
62
|
+
),
|
|
63
|
+
"oseries3_meta": {"x": 165554, "y": 422685},
|
|
64
|
+
"well1": w,
|
|
65
|
+
"well1_meta": {"x": 164691, "y": 423579},
|
|
66
|
+
"well2": w + 10,
|
|
67
|
+
"well2_meta": {"x": 164691 + 2000, "y": 423579 + 2000}, # far away
|
|
68
|
+
}
|
|
69
|
+
return d
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def initialize_project(conn, data1, data2, data3):
|
|
73
|
+
pstore = pst.PastaStore(conn, "test_project")
|
|
12
74
|
|
|
75
|
+
# dataset 1
|
|
76
|
+
pstore.add_oseries(data1["oseries1"], "oseries1", metadata=data1["oseries1_meta"])
|
|
77
|
+
pstore.add_stress(
|
|
78
|
+
data1["prec1"], "prec1", kind="prec", metadata=data1["prec1_meta"]
|
|
79
|
+
)
|
|
80
|
+
pstore.add_stress(
|
|
81
|
+
data1["evap1"], "evap1", kind="evap", metadata=data1["evap1_meta"]
|
|
82
|
+
)
|
|
13
83
|
|
|
14
|
-
|
|
15
|
-
pstore
|
|
84
|
+
# dataset 2
|
|
85
|
+
pstore.add_oseries(data2["oseries2"], "oseries2", metadata=data2["oseries2_meta"])
|
|
86
|
+
pstore.add_stress(
|
|
87
|
+
data2["prec2"], "prec2", kind="prec", metadata=data2["prec2_meta"]
|
|
88
|
+
)
|
|
89
|
+
pstore.add_stress(
|
|
90
|
+
data2["evap2"], "evap2", kind="evap", metadata=data2["evap2_meta"]
|
|
91
|
+
)
|
|
16
92
|
|
|
17
|
-
#
|
|
18
|
-
|
|
19
|
-
pstore.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
o = pd.read_csv("./tests/data/head_nb1.csv", index_col=0, parse_dates=True)
|
|
23
|
-
pstore.add_oseries(o, "oseries2", metadata={"x": 164000, "y": 423000})
|
|
24
|
-
|
|
25
|
-
# oseries 3
|
|
26
|
-
o = pd.read_csv("./tests/data/gw_obs.csv", index_col=0, parse_dates=True)
|
|
27
|
-
pstore.add_oseries(o, "oseries3", metadata={"x": 165554, "y": 422685})
|
|
28
|
-
|
|
29
|
-
# prec 1
|
|
30
|
-
s = pd.read_csv("./tests/data/rain.csv", index_col=0, parse_dates=True)
|
|
31
|
-
pstore.add_stress(s, "prec1", kind="prec", metadata={"x": 165050, "y": 424050})
|
|
32
|
-
|
|
33
|
-
# prec 2
|
|
34
|
-
s = pd.read_csv("./tests/data/rain_nb1.csv", index_col=0, parse_dates=True)
|
|
35
|
-
pstore.add_stress(s, "prec2", kind="prec", metadata={"x": 164010, "y": 423000})
|
|
36
|
-
|
|
37
|
-
# evap 1
|
|
38
|
-
s = pd.read_csv("./tests/data/evap.csv", index_col=0, parse_dates=True)
|
|
39
|
-
pstore.add_stress(s, "evap1", kind="evap", metadata={"x": 164500, "y": 424000})
|
|
40
|
-
|
|
41
|
-
# evap 2
|
|
42
|
-
s = pd.read_csv("./tests/data/evap_nb1.csv", index_col=0, parse_dates=True)
|
|
43
|
-
pstore.add_stress(s, "evap2", kind="evap", metadata={"x": 164000, "y": 423030})
|
|
44
|
-
|
|
45
|
-
# well 1
|
|
46
|
-
s = pd.read_csv("./tests/data/well_month_end.csv", index_col=0, parse_dates=True)
|
|
47
|
-
try:
|
|
48
|
-
s = ps.ts.timestep_weighted_resample(
|
|
49
|
-
s,
|
|
50
|
-
pd.date_range(s.index[0] - pd.offsets.MonthBegin(), s.index[-1], freq="D"),
|
|
51
|
-
).bfill()
|
|
52
|
-
except AttributeError:
|
|
53
|
-
# pastas<=0.22.0
|
|
54
|
-
pass
|
|
55
|
-
pstore.add_stress(s, "well1", kind="well", metadata={"x": 164691, "y": 423579})
|
|
56
|
-
# add second well
|
|
93
|
+
# dataset 3
|
|
94
|
+
pstore.add_oseries(data3["oseries3"], "oseries3", metadata=data3["oseries3_meta"])
|
|
95
|
+
pstore.add_stress(
|
|
96
|
+
data3["well1"], "well1", kind="well", metadata=data3["well1_meta"]
|
|
97
|
+
)
|
|
57
98
|
pstore.add_stress(
|
|
58
|
-
|
|
99
|
+
data3["well2"], "well2", kind="well", metadata=data3["well2_meta"]
|
|
59
100
|
)
|
|
60
101
|
|
|
61
102
|
return pstore
|
|
@@ -67,7 +108,7 @@ def conn(request):
|
|
|
67
108
|
name = f"test_{request.param}"
|
|
68
109
|
# connect to dbase
|
|
69
110
|
if request.param == "arcticdb":
|
|
70
|
-
uri = "lmdb://./
|
|
111
|
+
uri = "lmdb://./tests/data/arcticdb/"
|
|
71
112
|
conn = pst.ArcticDBConnector(name, uri)
|
|
72
113
|
elif request.param == "dict":
|
|
73
114
|
conn = pst.DictConnector(name)
|
|
@@ -80,33 +121,25 @@ def conn(request):
|
|
|
80
121
|
|
|
81
122
|
|
|
82
123
|
@pytest.fixture(scope="module", params=params)
|
|
83
|
-
def pstore(request):
|
|
124
|
+
def pstore(request, data1, data2, data3):
|
|
84
125
|
if request.param == "arcticdb":
|
|
85
|
-
name = "
|
|
86
|
-
uri = "lmdb://./
|
|
126
|
+
name = "testdb"
|
|
127
|
+
uri = "lmdb://./tests/data/arcticdb/"
|
|
87
128
|
connector = pst.ArcticDBConnector(name, uri)
|
|
88
129
|
elif request.param == "dict":
|
|
89
|
-
name = "
|
|
130
|
+
name = "testdb"
|
|
90
131
|
connector = pst.DictConnector(name)
|
|
91
132
|
elif request.param == "pas":
|
|
92
|
-
name = "
|
|
133
|
+
name = "testdb"
|
|
93
134
|
connector = pst.PasConnector(name, "./tests/data/pas")
|
|
94
135
|
else:
|
|
95
136
|
raise ValueError("Unrecognized parameter!")
|
|
96
|
-
pstore = initialize_project(connector)
|
|
137
|
+
pstore = initialize_project(connector, data1, data2, data3)
|
|
97
138
|
pstore.type = request.param # added here for defining test dependencies
|
|
98
139
|
yield pstore
|
|
99
140
|
pst.util.delete_pastastore(pstore)
|
|
100
141
|
|
|
101
142
|
|
|
102
|
-
def delete_arcticdb_test_db():
|
|
103
|
-
connstr = "lmdb://./arctic_db/"
|
|
104
|
-
name = "test_project"
|
|
105
|
-
connector = pst.ArcticDBConnector(name, connstr)
|
|
106
|
-
pst.util.delete_arcticdb_connector(connector)
|
|
107
|
-
print("ArcticDBConnector 'test_project' deleted.")
|
|
108
|
-
|
|
109
|
-
|
|
110
143
|
_has_pkg_cache = {}
|
|
111
144
|
|
|
112
145
|
|
|
@@ -163,3 +196,67 @@ def requires_pkg(*pkgs):
|
|
|
163
196
|
reason=f"missing package{'s' if len(missing) != 1 else ''}: "
|
|
164
197
|
+ ", ".join(missing),
|
|
165
198
|
)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def for_connectors(connectors=None):
|
|
202
|
+
"""Decorate to run tests only for specified connector types.
|
|
203
|
+
|
|
204
|
+
Parameters
|
|
205
|
+
----------
|
|
206
|
+
connectors : list of str, optional
|
|
207
|
+
List of connector types for which the test should run.
|
|
208
|
+
If None, defaults to all connector types: ["dict", "pas", "arcticdb"].
|
|
209
|
+
|
|
210
|
+
Returns
|
|
211
|
+
-------
|
|
212
|
+
function
|
|
213
|
+
Decorated test function that runs only for specified connectors.
|
|
214
|
+
"""
|
|
215
|
+
if connectors is None:
|
|
216
|
+
connectors = ["dict", "pas", "arcticdb"]
|
|
217
|
+
|
|
218
|
+
def decorator(func):
|
|
219
|
+
def wrapper(self, *args, **kwargs):
|
|
220
|
+
# Get the connector type from fixtures passed
|
|
221
|
+
# Could be in args or kwargs depending on how pytest passes it
|
|
222
|
+
store = None
|
|
223
|
+
|
|
224
|
+
# Check kwargs first (fixture passed by name)
|
|
225
|
+
for fixture_name in ["pstore_with_models", "pstore", "conn"]:
|
|
226
|
+
if fixture_name in kwargs:
|
|
227
|
+
store = kwargs[fixture_name]
|
|
228
|
+
break
|
|
229
|
+
|
|
230
|
+
# If not in kwargs, check first positional arg
|
|
231
|
+
if store is None and len(args) > 0:
|
|
232
|
+
store = args[0]
|
|
233
|
+
|
|
234
|
+
if store is None:
|
|
235
|
+
# Call without checking (shouldn't happen)
|
|
236
|
+
return func(self, *args, **kwargs)
|
|
237
|
+
|
|
238
|
+
# Check if we should skip based on connector type
|
|
239
|
+
if hasattr(store, "conn"):
|
|
240
|
+
conn_type = store.conn.conn_type
|
|
241
|
+
elif hasattr(store, "conn_type"):
|
|
242
|
+
conn_type = store.conn_type
|
|
243
|
+
else:
|
|
244
|
+
# If we can't determine connector type, just run the test
|
|
245
|
+
return func(self, *args, **kwargs)
|
|
246
|
+
|
|
247
|
+
if conn_type not in connectors:
|
|
248
|
+
pytest.skip(
|
|
249
|
+
f"Test skipped for {conn_type} connector "
|
|
250
|
+
f"(only runs for: {connectors})"
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
return func(self, *args, **kwargs)
|
|
254
|
+
|
|
255
|
+
# Preserve the original function signature and metadata for pytest
|
|
256
|
+
wrapper.__signature__ = inspect.signature(func)
|
|
257
|
+
wrapper.__name__ = func.__name__
|
|
258
|
+
wrapper.__doc__ = func.__doc__
|
|
259
|
+
|
|
260
|
+
return wrapper
|
|
261
|
+
|
|
262
|
+
return decorator
|
tests/test_001_import.py
CHANGED