Source code for nxtomomill.models.h52nx.FrameGroup

from __future__ import annotations

from enum import Enum

from pydantic import (
    BaseModel,
    field_validator,
    field_serializer,
    ConfigDict,
    Field,
)

from silx.io.url import DataUrl

from ._acquisitionstep import AcquisitionStep

from nxtomomill.models.utils import filter_str_def
from nxtomomill.models.utils import (
    remove_parenthesis_or_brackets,
    convert_str_to_bool,
)


[docs]class FrameGroup(BaseModel): model_config = ConfigDict( arbitrary_types_allowed=True, validate_assignment=True, validate_by_name=True ) url: DataUrl | None = None frame_type: AcquisitionStep = AcquisitionStep.PROJECTION copy_data: bool | None = Field( default=None, description="Should the frame dataset be copied or not. If not set will fallback on the 'frame_type_section.default_data_copy'", )
[docs] class Info(Enum): URL_ENTRY = "entry" FRAME_TYPE = "frame_type" COPY = "copy"
@field_validator( "frame_type", mode="plain", ) @classmethod def cast_to_AcquisitionStep(cls, value: str | AcquisitionStep) -> AcquisitionStep: return AcquisitionStep.from_value(value) @field_validator( "copy_data", mode="plain", ) @classmethod def cast_to_copy_data(cls, value: bool | None) -> bool | None: if value in (None, "", "None"): return None else: return convert_str_to_bool(value) @field_validator( "url", mode="plain", ) @classmethod def cast_to_url(cls, value: str | DataUrl | None) -> DataUrl: if value is None: return value elif isinstance(value, DataUrl): return value else: if "path=" not in value: # by default we expect the user to only give an entry on the file. So only the dataset path return DataUrl(data_path=value) else: return DataUrl(path=value) @field_serializer( "url", when_used="always", ) @classmethod def serialize_data_url(cls, value: DataUrl | None) -> str: if value is None: return "" elif value.file_path() is None: return value.data_path() else: return value.path()
[docs] @staticmethod def frm_str(input_str: str) -> FrameGroup: """ Create an instance of FrameGroup from it string representation. """ if not isinstance(input_str, str): raise TypeError(f"{input_str} should be a string") input_str = remove_parenthesis_or_brackets(input_str) elmts = input_str.split(",") elmts = filter(None, [elmt.lstrip(" ").rstrip(" ") for elmt in elmts]) cst_inputs = {} for elmt in elmts: try: info_type, value = FrameGroup._treat_elmt(elmt) except ValueError: url_example = DataUrl( file_path="/path/to/my/file/file.h5", data_path="/data/path", scheme="h5py", ) _example_frame = FrameGroup( url=url_example, copy_data=True, frame_type="projection" ) err_msg = ( f"Unable to interpret string ('{elmt}'). Please insure this is a " "either a frame type, a boolean for copy or an entry " "(DataUrl).\n " "Please prefix the value by the information type like: " f"{_example_frame}. Invalid element is {input_str}" ) raise ValueError(err_msg) else: cst_inputs[info_type] = value inputs = {} if FrameGroup.Info.FRAME_TYPE not in cst_inputs: raise ValueError(f"Unable to find frame type from {input_str}") else: inputs["frame_type"] = cst_inputs[FrameGroup.Info.FRAME_TYPE] if FrameGroup.Info.URL_ENTRY not in cst_inputs: raise ValueError(f"Unable to find entry from {input_str}") else: inputs["url"] = cst_inputs[FrameGroup.Info.URL_ENTRY] if FrameGroup.Info.COPY in cst_inputs: inputs["copy_data"] = cst_inputs[FrameGroup.Info.COPY] if "copy_data" in inputs: inputs["copy_data"] = inputs["copy_data"] return FrameGroup(**inputs)
@staticmethod def _treat_elmt(elmt: str) -> tuple[str, str]: assert isinstance(elmt, str) # try to interpret it as a Frame group info for info in FrameGroup.Info: key = f"{info.value}=" if elmt.startswith(key): return info, filter_str_def(elmt.replace(key, "", 1)) # try to interpret it as an acquisition step elmt = filter_str_def(elmt) try: acquisition_step = AcquisitionStep.from_value(elmt) except ValueError: pass else: return FrameGroup.Info.FRAME_TYPE, acquisition_step # try to interpret it as a boolean # is this a copy element if elmt.startswith(("copy=", "copy_data=")): return FrameGroup.Info.COPY, elmt.split("=")[1] if elmt in ("True", "true"): return FrameGroup.Info.COPY, True if elmt in ("False", "false"): return FrameGroup.Info.COPY, False try: elmt = filter_str_def(elmt) DataUrl(path=elmt) except ValueError: pass else: return FrameGroup.Info.URL_ENTRY, elmt raise ValueError def __str__(self) -> str: return self.str_representation( only_data_path=False, with_copy=True, with_prefix_key=True )
[docs] def str_representation( self, only_data_path: bool, with_copy: bool, with_prefix_key: bool ) -> str: """ Util function to print the possible input string for this FrameGroup. :param only_data_path: if True consider the input file frame group is contained in the input file and the string representing the url can be only the data path :param with_copy: if true display the copy information :param with_prefix_key: if true provide the string with the keys as prefix (frame_type=XXX, copy=...) """ if self.url is None: url_str = "" elif only_data_path or self.url.file_path() is None: url_str = self.url.data_path() else: url_str = self.url.path() if with_prefix_key: if with_copy: return "({ft_key}={frame_type}, {url_key}={url}, {copy_key}={copy})".format( ft_key=self.Info.FRAME_TYPE.value, frame_type=self.frame_type.value, url_key=self.Info.URL_ENTRY.value, url=url_str, copy_key=self.Info.COPY.value, copy=self.copy_data, ) else: return "({ft_key}={frame_type}, {url_key}={url})".format( ft_key=self.Info.FRAME_TYPE.value, frame_type=self.frame_type.value, url_key=self.Info.URL_ENTRY.value, url=url_str, ) else: if with_copy: return "({frame_type}, {url}, {copy})".format( frame_type=self.frame_type.value, url=url_str, copy=self.copy_data, ) else: return "({frame_type}, {url})".format( frame_type=self.frame_type.value, url=url_str, )
[docs]def filter_acqui_frame_type( init: FrameGroup, sequences: tuple, frame_type: AcquisitionStep ) -> tuple: """compute the list of urls representing projections from init until the next Initialization step :param init: frame group creating the beginning of the acquisition sequence :param sequences: list of FrameGroup representing the sequence :param frame_type: type of frame to filer (cannot be Initialization step) """ frame_type = AcquisitionStep.from_value(frame_type) if frame_type is AcquisitionStep.INITIALIZATION: raise ValueError(f"{AcquisitionStep.INITIALIZATION.value} is not handled") if init not in sequences: raise ValueError(f"{init} cannot be find in the provided sequence") frame_types = [frm_grp.frame_type for frm_grp in sequences] current_acqui_idx = sequences.index(init) if len(sequences) == current_acqui_idx - 1: # in case the initialization sequence is the last element of the # sequence (if people make strange stuff...) return () sequence_target = sequences[current_acqui_idx + 1 :] frame_types = frame_types[current_acqui_idx + 1 :] try: next_acqui = frame_types.index(AcquisitionStep.INITIALIZATION) - 1 except ValueError: next_acqui = -1 sequence_target = sequence_target[:next_acqui] filter_fct = lambda a: a.frame_type is frame_type return tuple(filter(filter_fct, sequence_target))