Skip to content

Commit 0c68eff

Browse files
committed
Started addressing #20 and #21
1 parent 6120c9d commit 0c68eff

File tree

7 files changed

+151
-9
lines changed

7 files changed

+151
-9
lines changed

tests/test_api.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import shutil
66
import unittest
77

8+
import numpy as np
89
import xarray as xr
910

1011
from zappend.api import FileObj
@@ -14,6 +15,7 @@
1415
from .helpers import make_test_dataset
1516

1617

18+
# noinspection PyMethodMayBeStatic
1719
class ApiTest(unittest.TestCase):
1820
def setUp(self):
1921
clear_memory_fs()
@@ -73,6 +75,79 @@ def process_slice(ctx, slice_ds: xr.Dataset) -> SliceSource:
7375
self.assertEqual({"chl"}, set(ds.data_vars))
7476
self.assertEqual({"time", "y", "x"}, set(ds.coords))
7577

78+
def test_some_slices_with_inc_append_step(self):
79+
target_dir = "memory://target.zarr"
80+
slices = [
81+
make_test_dataset(index=0, shape=(1, 50, 100)),
82+
make_test_dataset(index=1, shape=(1, 50, 100)),
83+
make_test_dataset(index=2, shape=(1, 50, 100)),
84+
]
85+
zappend(slices, target_dir=target_dir, append_step="1d")
86+
ds = xr.open_zarr(target_dir)
87+
np.testing.assert_array_equal(
88+
ds.time.values,
89+
np.array(["2024-01-01", "2024-01-02", "2024-01-03"], dtype=np.datetime64),
90+
)
91+
92+
def test_some_slices_with_dec_append_step(self):
93+
target_dir = "memory://target.zarr"
94+
slices = [
95+
make_test_dataset(index=2, shape=(1, 50, 100)),
96+
make_test_dataset(index=1, shape=(1, 50, 100)),
97+
make_test_dataset(index=0, shape=(1, 50, 100)),
98+
]
99+
zappend(slices, target_dir=target_dir, append_step="-1d")
100+
ds = xr.open_zarr(target_dir)
101+
np.testing.assert_array_equal(
102+
ds.time.values,
103+
np.array(["2024-01-03", "2024-01-02", "2024-01-01"], dtype=np.datetime64),
104+
)
105+
106+
def test_some_slices_with_one_missing_append_step(self):
107+
target_dir = "memory://target.zarr"
108+
slices = [
109+
make_test_dataset(index=0, shape=(1, 50, 100)),
110+
make_test_dataset(index=2, shape=(1, 50, 100)),
111+
]
112+
zappend(slices, target_dir=target_dir, append_step="1d")
113+
ds = xr.open_zarr(target_dir)
114+
np.testing.assert_array_equal(
115+
ds.time.values,
116+
np.array(["2024-01-01", "2024-01-02", "2024-01-03"], dtype=np.datetime64),
117+
)
118+
119+
def test_some_slices_with_three_missing_append_steps(self):
120+
target_dir = "memory://target.zarr"
121+
slices = [
122+
make_test_dataset(index=0, shape=(1, 50, 100)),
123+
make_test_dataset(index=4, shape=(1, 50, 100)),
124+
]
125+
zappend(slices, target_dir=target_dir, append_step="1d")
126+
ds = xr.open_zarr(target_dir)
127+
np.testing.assert_array_equal(
128+
ds.time.values,
129+
np.array(
130+
["2024-01-01", "2024-01-02", "2024-01-03", "2024-01-04", "2024-01-05"],
131+
dtype=np.datetime64,
132+
),
133+
)
134+
135+
def test_it_raises_for_wrong_append_step(self):
136+
# TODO: implement me
137+
pass
138+
139+
def test_some_slices_with_inc_append_labels(self):
140+
# TODO: implement me
141+
pass
142+
143+
def test_some_slices_with_dec_append_labels(self):
144+
# TODO: implement me
145+
pass
146+
147+
def test_it_raises_for_none_inc_append_labels(self):
148+
# TODO: implement me
149+
pass
150+
76151
def test_some_slices_with_profiling(self):
77152
target_dir = "memory://target.zarr"
78153
slices = [

tests/test_config.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,13 +233,16 @@ def test_exclude_from_config(self):
233233
with exclude_from_config({"a": 1, "b": 2}, "b", "a") as config:
234234
self.assertEqual({}, config)
235235

236+
237+
class ConfigSchemaTest(unittest.TestCase):
236238
def test_get_config_schema(self):
237239
schema = get_config_schema()
238240
self.assertIn("properties", schema)
239241
self.assertIsInstance(schema["properties"], dict)
240242
self.assertEqual(
241243
{
242244
"append_dim",
245+
"append_step",
243246
"disable_rollback",
244247
"dry_run",
245248
"excluded_variables",

tests/test_context.py

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55
import unittest
66

77
import pytest
8+
import numpy as np
89
import xarray as xr
9-
from zappend.api import zappend
10+
1011
from zappend.context import Context
1112
from zappend.fsutil.fileobj import FileObj
1213
from zappend.metadata import DatasetMetadata
@@ -37,20 +38,32 @@ def test_with_existing_target(self):
3738
def test_append_dim(self):
3839
ctx = Context({"target_dir": "memory://target.zarr"})
3940
self.assertEqual("time", ctx.append_dim_name)
40-
4141
ctx = Context({"target_dir": "memory://target.zarr", "append_dim": "depth"})
4242
self.assertEqual("depth", ctx.append_dim_name)
4343

44+
def test_append_step(self):
45+
make_test_dataset(uri="memory://target.zarr")
46+
ctx = Context({"target_dir": "memory://target.zarr"})
47+
self.assertEqual(None, ctx.append_step_size)
48+
ctx = Context({"target_dir": "memory://target.zarr", "append_step": "1d"})
49+
self.assertEqual("1d", ctx.append_step_size)
50+
51+
def test_last_append_label(self):
52+
make_test_dataset(uri="memory://target.zarr")
53+
ctx = Context({"target_dir": "memory://target.zarr"})
54+
self.assertEqual(None, ctx.last_append_label)
55+
ctx = Context({"target_dir": "memory://TARGET.zarr", "append_step": "1d"})
56+
self.assertEqual(None, ctx.last_append_label)
57+
ctx = Context({"target_dir": "memory://target.zarr", "append_step": "1d"})
58+
self.assertEqual(np.datetime64("2024-01-03"), ctx.last_append_label)
59+
4460
def test_slice_polling(self):
4561
ctx = Context({"target_dir": "memory://target.zarr"})
4662
self.assertEqual((None, None), ctx.slice_polling)
47-
4863
ctx = Context({"target_dir": "memory://target.zarr", "slice_polling": False})
4964
self.assertEqual((None, None), ctx.slice_polling)
50-
5165
ctx = Context({"target_dir": "memory://target.zarr", "slice_polling": True})
5266
self.assertEqual((2, 60), ctx.slice_polling)
53-
5467
ctx = Context(
5568
{
5669
"target_dir": "memory://target.zarr",

zappend/config/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@
33
# https://opensource.org/licenses/MIT.
44

55
from .config import ConfigItem
6-
from .config import ConfigList
76
from .config import ConfigLike
7+
from .config import ConfigList
88
from .config import exclude_from_config
99
from .config import merge_configs
1010
from .config import normalize_config
1111
from .config import validate_config
1212
from .defaults import DEFAULT_APPEND_DIM
13+
from .defaults import DEFAULT_APPEND_STEP
1314
from .defaults import DEFAULT_SLICE_POLLING_INTERVAL
1415
from .defaults import DEFAULT_SLICE_POLLING_TIMEOUT
1516
from .defaults import DEFAULT_ZARR_VERSION

zappend/config/defaults.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
DEFAULT_ZARR_VERSION = 2
66
DEFAULT_APPEND_DIM = "time"
7+
DEFAULT_APPEND_STEP = None
78

89
DEFAULT_SLICE_POLLING_INTERVAL = 2
910
DEFAULT_SLICE_POLLING_TIMEOUT = 60

zappend/config/schema.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from typing import Any, Literal
77

88
from .defaults import DEFAULT_APPEND_DIM
9+
from .defaults import DEFAULT_APPEND_STEP
910
from .defaults import DEFAULT_SLICE_POLLING_INTERVAL
1011
from .defaults import DEFAULT_SLICE_POLLING_TIMEOUT
1112
from .defaults import DEFAULT_ZARR_VERSION
@@ -473,6 +474,31 @@
473474
"minLength": 1,
474475
"default": DEFAULT_APPEND_DIM,
475476
},
477+
append_step={
478+
"description": (
479+
"If set, enforces a step size in the append dimension between two"
480+
" slices or just enforces a direction."
481+
),
482+
"oneOf": [
483+
{
484+
"description": "Arbitrary step size or not applicable.",
485+
"const": None,
486+
},
487+
{"description": "Monotonically increasing.", "const": "+"},
488+
{"description": "Monotonically decreasing.", "const": "-"},
489+
{
490+
"description": "A time delta value.",
491+
"type": "string",
492+
"not": {"const": ""},
493+
},
494+
{
495+
"description": "A numerical delta value.",
496+
"type": "number",
497+
"not": {"const": 0},
498+
},
499+
],
500+
"default": DEFAULT_APPEND_STEP,
501+
},
476502
included_variables={
477503
"description": (
478504
"Specifies the names of variables to be included in"

zappend/context.py

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import xarray as xr
1010

1111
from .config import DEFAULT_APPEND_DIM
12+
from .config import DEFAULT_APPEND_STEP
1213
from .config import DEFAULT_SLICE_POLLING_INTERVAL
1314
from .config import DEFAULT_SLICE_POLLING_TIMEOUT
1415
from .config import DEFAULT_ZARR_VERSION
@@ -36,10 +37,18 @@ def __init__(self, config: Dict[str, Any]):
3637
target_storage_options = config.get("target_storage_options")
3738
self._target_dir = FileObj(target_uri, storage_options=target_storage_options)
3839

40+
self._append_dim_name = config.get("append_dim") or DEFAULT_APPEND_DIM
41+
self._append_step_size = config.get("append_step") or DEFAULT_APPEND_STEP
42+
self._last_append_label = None
43+
3944
try:
4045
with xr.open_zarr(
4146
target_uri, storage_options=target_storage_options
4247
) as target_dataset:
48+
if self.append_step_size is not None:
49+
append_var = target_dataset.get(self._append_dim_name)
50+
if append_var is not None and append_var.size > 0:
51+
self._last_append_label = append_var[-1]
4352
target_metadata = DatasetMetadata.from_dataset(target_dataset, config)
4453
except FileNotFoundError:
4554
target_metadata = None
@@ -74,10 +83,24 @@ def zarr_version(self) -> int:
7483
@property
7584
def append_dim_name(self) -> str:
7685
"""The name of the append dimension along which slice datasets will be
77-
concatenated.
78-
If not configured, it defaults to `"time"`.
86+
concatenated. Defaults to `"time"`.
87+
"""
88+
return self._append_dim_name
89+
90+
@property
91+
def append_step_size(self) -> int | float | str | None:
92+
"""The enforced step size in the append dimension between two slices.
93+
Defaults to `None`.
94+
"""
95+
return self._append_step_size
96+
97+
@property
98+
def last_append_label(self) -> Any | None:
99+
"""The last label found in the coordinate variable that corresponds to
100+
the append dimension. Its value is `None` if no such variable exists or the
101+
variable is empty or if [append_step_size][append_step_size] is `None`.
79102
"""
80-
return self._config.get("append_dim") or DEFAULT_APPEND_DIM
103+
return self._last_append_label
81104

82105
@property
83106
def target_metadata(self) -> DatasetMetadata | None:

0 commit comments

Comments
 (0)