Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for passing wells as List[dict] in write_plate_metadata #157

Merged
merged 7 commits into from
Jan 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 44 additions & 15 deletions ome_zarr/writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,18 @@ def _validate_axes(axes: List[str], fmt: Format = CurrentFormat()) -> None:
raise ValueError("5D data must have axes ('t', 'c', 'z', 'y', 'x')")


def _validate_well_images(images: List, fmt: Format = CurrentFormat()) -> None:
def _validate_well_images(
images: List[Union[str, dict]], fmt: Format = CurrentFormat()
) -> List[dict]:

VALID_KEYS = [
"acquisition",
"path",
]
for index, image in enumerate(images):
validated_images = []
for image in images:
if isinstance(image, str):
images[index] = {"path": str(image)}
validated_images.append({"path": str(image)})
elif isinstance(image, dict):
if any(e not in VALID_KEYS for e in image.keys()):
LOGGER.debug("f{image} contains unspecified keys")
Expand All @@ -88,13 +91,15 @@ def _validate_well_images(images: List, fmt: Format = CurrentFormat()) -> None:
raise ValueError(f"{image} path must be of string type")
if "acquisition" in image and not isinstance(image["acquisition"], int):
raise ValueError(f"{image} acquisition must be of int type")
validated_images.append(image)
joshmoore marked this conversation as resolved.
Show resolved Hide resolved
else:
raise ValueError(f"Unrecognized type for {image}")
return validated_images


def _validate_plate_acquisitions(
acquisitions: List[Dict], fmt: Format = CurrentFormat()
) -> None:
) -> List[Dict]:

VALID_KEYS = [
"id",
Expand All @@ -104,8 +109,7 @@ def _validate_plate_acquisitions(
"starttime",
"endtime",
]
if acquisitions is None:
return

for acquisition in acquisitions:
if not isinstance(acquisition, dict):
raise ValueError(f"{acquisition} must be a dictionary")
Expand All @@ -115,6 +119,33 @@ def _validate_plate_acquisitions(
raise ValueError(f"{acquisition} must contain an id key")
if not isinstance(acquisition["id"], int):
raise ValueError(f"{acquisition} id must be of int type")
return acquisitions


def _validate_plate_wells(
wells: List[Union[str, dict]], fmt: Format = CurrentFormat()
) -> List[dict]:

VALID_KEYS = [
"path",
]
validated_wells = []
if wells is None or len(wells) == 0:
raise ValueError("Empty wells list")
for well in wells:
if isinstance(well, str):
validated_wells.append({"path": str(well)})
elif isinstance(well, dict):
if any(e not in VALID_KEYS for e in well.keys()):
LOGGER.debug("f{well} contains unspecified keys")
if "path" not in well:
raise ValueError(f"{well} must contain a path key")
if not isinstance(well["path"], str):
raise ValueError(f"{well} path must be of str type")
validated_wells.append(well)
else:
raise ValueError(f"Unrecognized type for {well}")
return validated_wells


def write_multiscale(
Expand Down Expand Up @@ -198,7 +229,7 @@ def write_plate_metadata(
group: zarr.Group,
rows: List[str],
columns: List[str],
wells: List[str],
wells: List[Union[str, dict]],
fmt: Format = CurrentFormat(),
acquisitions: List[dict] = None,
field_count: int = None,
Expand All @@ -215,7 +246,7 @@ def write_plate_metadata(
The list of names for the plate rows
columns: list of str
The list of names for the plate columns
wells: list of str
wells: list of str or dict
The list of paths for the well groups
fmt: Format
The format of the ome_zarr data which should be used.
Expand All @@ -231,22 +262,21 @@ def write_plate_metadata(
plate: Dict[str, Union[str, int, List[Dict]]] = {
"columns": [{"name": str(c)} for c in columns],
"rows": [{"name": str(r)} for r in rows],
"wells": [{"path": str(wp)} for wp in wells],
"wells": _validate_plate_wells(wells),
"version": fmt.version,
}
if name is not None:
plate["name"] = name
if field_count is not None:
plate["field_count"] = field_count
if acquisitions is not None:
_validate_plate_acquisitions(acquisitions)
plate["acquisitions"] = acquisitions
plate["acquisitions"] = _validate_plate_acquisitions(acquisitions)
group.attrs["plate"] = plate


def write_well_metadata(
group: zarr.Group,
images: Union[List[str], List[dict]],
images: List[Union[str, dict]],
fmt: Format = CurrentFormat(),
) -> None:
"""
Expand All @@ -256,7 +286,7 @@ def write_well_metadata(
----------
group: zarr.Group
the group within the zarr store to write the metadata in.
image_paths: list of str
image_paths: list of str or dict
The list of paths for the well images
image_acquisitions: list of int
The list of acquisitions for the well images
Expand All @@ -265,9 +295,8 @@ def write_well_metadata(
Defaults to the most current.
"""

_validate_well_images(images)
well = {
"images": images,
"images": _validate_well_images(images),
"version": fmt.version,
}
group.attrs["well"] = well
Expand Down
45 changes: 42 additions & 3 deletions tests/test_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -343,14 +343,53 @@ def test_acquisitions_maximal(self):
(
[0, 1],
[{"name": "0"}, {"name": "1"}],
[{"id": 0, "invalid_key": "0"}],
[{"id": "0"}, {"id": "1"}],
),
)
def test_unspecified_acquisition_keys(self, acquisitions):
a = [{"id": 0, "invalid_key": "0"}]
def test_invalid_acquisition_keys(self, acquisitions):
with pytest.raises(ValueError):
write_plate_metadata(
self.root, ["A"], ["1"], ["A/1"], acquisitions=acquisitions
)

def test_unspecified_acquisition_keys(self):
a = [{"id": 0, "unspecified_key": "0"}]
write_plate_metadata(self.root, ["A"], ["1"], ["A/1"], acquisitions=a)
assert "plate" in self.root.attrs
assert self.root.attrs["plate"]["acquisitions"] == a

@pytest.mark.parametrize(
"wells",
(None, [], [1]),
)
def test_invalid_well_list(self, wells):
with pytest.raises(ValueError):
write_plate_metadata(self.root, ["A"], ["1"], wells)

@pytest.mark.parametrize(
"wells",
(
[{"path": 0}],
[{"id": "test"}],
[{"path": "A/1"}, {"path": None}],
),
)
def test_invalid_well_keys(self, wells):
with pytest.raises(ValueError):
write_plate_metadata(self.root, ["A"], ["1"], wells)

def test_unspecified_well_keys(self):
wells = [
{"path": "A/1", "unspecified_key": "alpha"},
{"path": "A/2", "unspecified_key": "beta"},
{"path": "B/1", "unspecified_key": "gamma"},
]
write_plate_metadata(self.root, ["A", "B"], ["1", "2"], wells)
assert "plate" in self.root.attrs
assert self.root.attrs["plate"]["columns"] == [{"name": "1"}, {"name": "2"}]
assert self.root.attrs["plate"]["rows"] == [{"name": "A"}, {"name": "B"}]
assert self.root.attrs["plate"]["version"] == CurrentFormat().version
assert self.root.attrs["plate"]["wells"] == wells


class TestWellMetadata:
Expand Down