Skip to content

Commit

Permalink
sane refactor and thaw frozen base model
Browse files Browse the repository at this point in the history
  • Loading branch information
d-v-b committed Nov 21, 2024
1 parent 68144db commit 21bea2b
Show file tree
Hide file tree
Showing 25 changed files with 1,366 additions and 208 deletions.
7 changes: 7 additions & 0 deletions src/ome_zarr_models/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import pydantic


class Base(pydantic.BaseModel):
"""
The base pydantic model for all metadata classes
"""
205 changes: 205 additions & 0 deletions src/ome_zarr_models/tmp/v04/bikeshed.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
"""
Pydantic models related to OME-NGFF 0.4 specs, as implemented in
fractal-tasks-core.
"""
import logging
from typing import Optional
from typing import TypeVar
from typing import Union

from pydantic import Field
from pydantic import field_validator

from ome_zarr_models.base import Base
from ome_zarr_models.utils import unique_items_validator
from ome_zarr_models.zarr_models.v04.multiscales import Dataset


logger = logging.getLogger(__name__)


T = TypeVar("T")

class Axis(Base):
"""
Model for an element of `Multiscale.axes`.
See https://ngff.openmicroscopy.org/0.4/#axes-md.
"""

name: str
type: str | None = None
unit: str | None = None


class ImageInWell(Base):
"""
Model for an element of `Well.images`.
**Note 1:** The NGFF image is defined in a different model
(`NgffImageMeta`), while the `Image` model only refere to an item of
`Well.images`.
**Note 2:** We deviate from NGFF specs, since we allow `path` to be an
arbitrary string.
TODO: include a check like `constr(regex=r'^[A-Za-z0-9]+$')`, through a
Pydantic validator.
See https://ngff.openmicroscopy.org/0.4/#well-md.
"""

acquisition: int | None = Field(
None, description="A unique identifier within the context of the plate"
)
path: str = Field(
..., description="The path for this field of view subgroup"
)


class Well(Base):
"""
Model for `NgffWellMeta.well`.
See https://ngff.openmicroscopy.org/0.4/#well-md.
"""

images: list[ImageInWell] = Field(
..., 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)


class NgffWellMeta(Base):
"""
Model for the metadata of a NGFF well.
See https://ngff.openmicroscopy.org/0.4/#well-md.
"""

well: Well | None = None

def get_acquisition_paths(self) -> dict[int, list[str]]:
"""
Create mapping from acquisition indices to corresponding paths.
Runs on the well zarr attributes and loads the relative paths in the
well.
Returns:
Dictionary with `(acquisition index: [image_path])` key/value
pairs.
Raises:
ValueError:
If an element of `self.well.images` has no `acquisition`
attribute.
"""
acquisition_dict = {}
for image in self.well.images:
if image.acquisition is None:
raise ValueError(
"Cannot get acquisition paths for Zarr files without "
"'acquisition' metadata at the well level"
)
if image.acquisition not in acquisition_dict:
acquisition_dict[image.acquisition] = []
acquisition_dict[image.acquisition].append(image.path)
return acquisition_dict


######################
# Plate models
######################


class AcquisitionInPlate(Base):
"""
Model for an element of `Plate.acquisitions`.
See https://ngff.openmicroscopy.org/0.4/#plate-md.
"""

id: int = Field(
description="A unique identifier within the context of the plate"
)
maximumfieldcount: int | None = Field(
None,
description=(
"Int indicating the maximum number of fields of view for the "
"acquisition"
),
)
name: str | None = Field(
None, description="a string identifying the name of the acquisition"
)
description: str | None = Field(
None,
description="The description of the acquisition",
)
# TODO: Add starttime & endtime
# starttime: str | None = Field()
# endtime: str | None = Field()


class WellInPlate(Base):
"""
Model for an element of `Plate.wells`.
See https://ngff.openmicroscopy.org/0.4/#plate-md.
"""

path: str
rowIndex: int
columnIndex: int


class ColumnInPlate(Base):
"""
Model for an element of `Plate.columns`.
See https://ngff.openmicroscopy.org/0.4/#plate-md.
"""

name: str


class RowInPlate(Base):
"""
Model for an element of `Plate.rows`.
See https://ngff.openmicroscopy.org/0.4/#plate-md.
"""

name: str


class Plate(Base):
"""
Model for `NgffPlateMeta.plate`.
See https://ngff.openmicroscopy.org/0.4/#plate-md.
"""

acquisitions: list[AcquisitionInPlate] | None = None
columns: list[ColumnInPlate]
field_count: int | None = None
name: str | None = None
rows: list[RowInPlate]
# version will become required in 0.5
version: str | None = Field(
None, description="The version of the specification"
)
wells: list[WellInPlate]


class NgffPlateMeta(Base):
"""
Model for the metadata of a NGFF plate.
See https://ngff.openmicroscopy.org/0.4/#plate-md.
"""

plate: Plate
67 changes: 67 additions & 0 deletions src/ome_zarr_models/tmp/v04/coordinate_transformations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
from ome_zarr_models.base import Base


from pydantic import Field


from typing import Literal

class Identity(Base):
"""
Model for an identity transformation.
See https://ngff.openmicroscopy.org/0.4/#trafo-md
"""
type: Literal["identity"]

class VectorScale(Base):
"""
Model for a scale transformation parametrized by a vector of numbers.
This corresponds to scale-type elements of
`Dataset.coordinateTransformations` or
`Multiscale.coordinateTransformations`.
See https://ngff.openmicroscopy.org/0.4/#trafo-md
"""

type: Literal["scale"]
scale: list[float] = Field(..., min_length=2)

class PathScale(Base):
"""
Model for a scale transformation parametrized by a path.
This corresponds to scale-type elements of
`Dataset.coordinateTransformations` or
`Multiscale.coordinateTransformations`.
See https://ngff.openmicroscopy.org/0.4/#trafo-md
"""

type: Literal["scale"]
path: str

class VectorTranslation(Base):
"""
Model for a translation transformation parametrized by a vector of numbers.
This corresponds to translation-type elements of
`Dataset.coordinateTransformations` or
`Multiscale.coordinateTransformations`.
See https://ngff.openmicroscopy.org/0.4/#trafo-md
"""

type: Literal["translation"]
translation: list[float] = Field(..., min_length=2)

class PathTranslation(Base):
"""
Model for a translation transformation parametrized by a path.
This corresponds to translation-type elements of
`Dataset.coordinateTransformations` or
`Multiscale.coordinateTransformations`.
See https://ngff.openmicroscopy.org/0.4/#trafo-md
"""

type: Literal["translation"]
translation: str
76 changes: 76 additions & 0 deletions src/ome_zarr_models/tmp/v04/multiscales.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
from ome_zarr_models.base import Base
from ome_zarr_models.utils import unique_items_validator
from ome_zarr_models.zarr_models.v04.bikeshed import Axis
from ome_zarr_models.zarr_models.v04.coordinate_transformations import PathScale, PathTranslation, VectorScale, VectorTranslation


from pydantic import Field, field_validator

from ome_zarr_models.zarr_models.v04.omero import Omero


class Dataset(Base):
"""
Model for an element of `Multiscale.datasets`.
See https://ngff.openmicroscopy.org/0.4/#multiscale-md
"""
# TODO: validate that path resolves to an actual zarr array
path: str
# TODO: validate that transforms are consistent w.r.t dimensionality
coordinateTransformations: tuple[VectorScale | PathScale] | tuple[VectorScale | PathScale, VectorTranslation | PathTranslation]


class Multiscale(Base):
"""
Model for an element of `NgffImageMeta.multiscales`.
See https://ngff.openmicroscopy.org/0.4/#multiscale-md.
"""

name: str | None = None
datasets: list[Dataset] = Field(..., min_length=1)
version: str | None = None
axes: list[Axis] = Field(..., max_length=5, min_length=2)
coordinateTransformations: Optional[
list[
Union[
PathScale,
VectorTranslation,
]
]
] = None
_check_unique = field_validator("axes")(unique_items_validator)

@field_validator("coordinateTransformations", mode="after")
@classmethod
def _no_global_coordinateTransformations(
cls, v: list | None
) -> list | None:
"""
Fail if Multiscale has a (global) coordinateTransformations attribute.
"""
if v is None:
return v
else:
raise NotImplementedError(
"Global coordinateTransformations at the multiscales "
"level are not currently supported in the fractal-tasks-core "
"model for the NGFF multiscale."
)


class MultiscaleGroupAttrs(Base):
"""
Model for the metadata of a NGFF image.
See https://ngff.openmicroscopy.org/0.4/#image-layout.
"""

multiscales: list[Multiscale] = Field(
...,
description="The multiscale datasets for this image",
min_length=1,
)
omero: Omero | None = None
_check_unique = field_validator("multiscales")(unique_items_validator)
Loading

0 comments on commit 21bea2b

Please sign in to comment.