Skip to content

Commit

Permalink
Merge pull request #17 from BioImageTools/tutorial
Browse files Browse the repository at this point in the history
finish basic tutorial
  • Loading branch information
dstansby authored Nov 21, 2024
2 parents 54cb582 + 2fe75dc commit 413f937
Show file tree
Hide file tree
Showing 10 changed files with 95 additions and 34 deletions.
1 change: 1 addition & 0 deletions .readthedocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ python:
path: .
extra_requirements:
- docs
- pydantic

mkdocs:
configuration: mkdocs.yml
73 changes: 51 additions & 22 deletions docs/tutorial.py
Original file line number Diff line number Diff line change
@@ -1,55 +1,84 @@
# # Tutorial

# from ome_zarr_models.v04 import Image as OMEZarrImage
from rich.pretty import pprint

# my_image = OMEZarrImage(path="path/to/ome/zarr/directory.ome.zarr")
# print(my_image.multiscales)

# ## Creating
#
# TODO: exmaple of creating a model from a remote store
# TODO: example of

import gcsfs
import zarr
import zarr.storage

from ome_zarr_models.v04 import Image
from ome_zarr_models.v04.coordinate_transformations import (
VectorTranslation,
)

# ## Creating models
#
# We can create an Image model from a zarr group, that points to an
# OME-zarr dataset:

# Setup zarr group from a remote store
bucket = "ucl-hip-ct-35a68e99feaae8932b1d44da0358940b"
fs = gcsfs.GCSFileSystem(project=bucket, token="anon", access="read_only")
store = zarr.storage.FSStore(url=bucket, fs=fs)
group = zarr.open_group(
store=store, path="S-20-28/heart/25.27um_complete-organ_bm05.ome.zarr"
)
print(group)
exit()

ome_zarr_image = Image(group)

# Oh no, it failed! One of the key goals of this package is to eagerly validatemetadata, so you can realise it's wrong.
#
# Lets try that again, but with some valid OME-zarr data

group = zarr.open("https://uk1s3.embassy.ebi.ac.uk/idr/zarr/v0.4/idr0062A/6001240.zarr")
ome_zarr_image = Image(group)
pprint(ome_zarr_image)

# This image contains both the zarr group, and a model of the multiscales metadata

multiscales_meta = ome_zarr_image.multiscales
pprint(multiscales_meta)

# ## Updating models
#
# All the fields in the models can be updated in place.
# When you do this, any validation on the individual field
# you are updating will take place.
# All the fields in the models can be updated in place. When you do this, any validation on the individual field you are updating will take place.
#
# For example, we can [do something valid]:
# For example, there is no name for the first multiscales entry, so lets add it

multiscales_meta[0].name = "The first multiscales entry"
pprint(multiscales_meta)

# One constraint in the OME-zarr spec is that the coordiante transforms have to be a scale, or a scale then tranlsation (strictly in that order). So if we try and make a transformation just a translation, it will raise an error.

multiscales_meta[0].datasets[0].coordinateTransformations = VectorTranslation(
type="translation", translation=[1, 2, 3]
)

# but if you try and [do something invalid] it raises an error:

# This means validation happens early, allowing you to catch errors
# before getting too far.

# ## Writing metadata
#
# TODO: example of writing out the metadata again
# To save the metadata after editing, we can use the ``save_attrs()`` method.
# TODO: Use a local file for testing that we have write access to, so we
# can demonstrate this.
#

# +
# ome_zarr_image.save_attrs()
# -

# ## Accessing data
#
# Although these models do not handle reading or writing data,
# they do expose the zarr arrays.
# Although these models do not handle reading or writing data, they do expose the zarr arrays.

zarr_arr = ome_zarr_image.group[multiscales_meta[0].datasets[0].path]
pprint(zarr_arr)

# ## Not using validation
#
# If you *really* want to create models that are not validated against
# the OME-zarr specifciation, you can use the ``model_construct`` method.
# For example:
# If you want to create models that are not validated against the OME-zarr specifciation, you can use the ``model_construct`` method on the models.


# Put some bad code here
4 changes: 3 additions & 1 deletion mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ theme:
name: material

plugins:
- mkdocs-jupyter
- mkdocs-jupyter:
execute: true
allow_errors: true
- mkdocstrings:
handlers:
python:
Expand Down
7 changes: 6 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ docs = [
"mkdocstrings-python>=1.12.2",
"mkdocs-material",
"mkdocs-jupyter",
"gcsfs",
"rich",
"zarr<3",
]
pydantic = ["pydantic"]

[tool.hatch.version]
source = "vcs"
Expand All @@ -28,6 +32,7 @@ docs = [
"mkdocs-material",
"mkdocs-jupyter",
"gcsfs",
"rich",
"zarr<3",
]
dev = [
Expand All @@ -40,7 +45,7 @@ dev = [

test = ["pytest"]

pydantic=["pydantic"]
pydantic = ["pydantic"]

[tool.uv]
default-groups = ["docs", "dev", "pydantic", "test"]
Expand Down
3 changes: 3 additions & 0 deletions src/ome_zarr_models/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@ class Base(pydantic.BaseModel):
"""
The base pydantic model for all metadata classes
"""

class Config:
validate_assignment = True
9 changes: 4 additions & 5 deletions src/ome_zarr_models/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,25 @@
from pydantic import create_model

from typing import TypeVar

T = TypeVar("T")


def _unique_items_validator(values: list[T]) -> list[T]:
for ind, value in enumerate(values, start=1):
if value in values[ind:]:
raise ValueError(f"Non-unique values in {values}.")
return values


def dataclass_to_pydantic(dataclass_type: type) -> type[pydantic.BaseModel]:
"""Convert a dataclass to a Pydantic model.
Parameters
----------
dataclass_type : type
The dataclass to convert to a Pydantic model.
Returns
-------
type[pydantic.BaseModel] a Pydantic model class.
Expand All @@ -38,8 +41,4 @@ def dataclass_to_pydantic(dataclass_type: type) -> type[pydantic.BaseModel]:
# No default value
field_definitions[_field.name] = (_field.type, Ellipsis)

<<<<<<< HEAD
return create_model(dataclass_type.__name__, **field_definitions)
=======
return create_model(dataclass_type.__name__, **field_definitions)
>>>>>>> 470f4f1a33aef4ecf2ebf0906c912a3621c8957b
1 change: 1 addition & 0 deletions src/ome_zarr_models/v04/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
VectorScale,
VectorTranslation,
)
from ome_zarr_models.v04.image import Image
from ome_zarr_models.v04.multiscales import (
Dataset,
Multiscale,
Expand Down
21 changes: 21 additions & 0 deletions src/ome_zarr_models/v04/image.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import zarr

from ome_zarr_models.v04.multiscales import Multiscale, MultiscaleGroupAttrs
from ome_zarr_models.v04.omero import Omero


class Image:
def __init__(self, group: zarr.Group):
self.group = group
self._attrs = MultiscaleGroupAttrs(**group.attrs.asdict())

@property
def multiscales(self) -> list[Multiscale]:
return self._attrs.multiscales

@property
def omero(self) -> Omero | None:
return self._attrs.omero

def save_attrs(self) -> None:
self.group.attrs.put(self._attrs.model_dump_json())
6 changes: 3 additions & 3 deletions src/ome_zarr_models/v04/multiscales.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from pydantic import Field, field_validator

from ome_zarr_models.base import Base
from ome_zarr_models.utils import unique_items_validator
from ome_zarr_models.utils import _unique_items_validator
from ome_zarr_models.v04.axes import Axis
from ome_zarr_models.v04.coordinate_transformations import (
PathScale,
Expand Down Expand Up @@ -50,7 +50,7 @@ class Multiscale(Base):
metadata: Any = None
name: Any | None = None
type: Any = None
_check_unique = field_validator("axes")(unique_items_validator)
_check_unique = field_validator("axes")(_unique_items_validator)


class MultiscaleGroupAttrs(Base):
Expand All @@ -66,4 +66,4 @@ class MultiscaleGroupAttrs(Base):
min_length=1,
)
omero: Omero | None = None
_check_unique = field_validator("multiscales")(unique_items_validator)
_check_unique = field_validator("multiscales")(_unique_items_validator)
4 changes: 2 additions & 2 deletions src/ome_zarr_models/v04/well.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from pydantic import Field, field_validator

from ome_zarr_models.base import Base
from ome_zarr_models.utils import unique_items_validator
from ome_zarr_models.utils import _unique_items_validator


class ImageInWell(Base):
Expand Down Expand Up @@ -37,7 +37,7 @@ class Well(Base):
..., description="The images included in this well", min_length=1
)
version: str | None = Field(None, description="The version of the specification")
_check_unique = field_validator("images")(unique_items_validator)
_check_unique = field_validator("images")(_unique_items_validator)


class NgffWellMeta(Base):
Expand Down

0 comments on commit 413f937

Please sign in to comment.