Source code for nxtomomill.io.config.confighandler

# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2015-2020 European Synchrotron Radiation Facility
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# ###########################################################################*/

"""
contains the HDF5ConfigHandler
"""

__authors__ = ["H. Payno"]
__license__ = "MIT"
__date__ = "08/03/2021"


import logging
import os
from typing import Union

from nxtomomill import utils
from nxtomomill.io.utils import filter_str_def
from nxtomomill.utils import Format
from nxtomomill.converter.hdf5.utils import get_default_output_file

from .hdf5config import TomoHDF5Config, XRD3DHDF5Config

_logger = logging.getLogger(__name__)

SETTABLE_PARAMETERS_UNITS = {
    "energy": "kev",
    "x_pixel_size": "m",
    "y_pixel_size": "m",
    "detector_sample_distance": "m",
}

SETTABLE_PARAMETERS_TYPE = {
    "energy": float,
    "x_pixel_size": float,
    "y_pixel_size": float,
    "detector_sample_distance": float,
}

SETTABLE_PARAMETERS = SETTABLE_PARAMETERS_UNITS.keys()


def _extract_param_value(key_values):
    """extract all the key / values elements from the str_list. Expected
    format is `param_1_name param_1_value param_2_name param_2_value ...`

    :param str str_list: raw input string as `param_1_name param_1_value
                         param_2_name param_2_value ...`
    :return: dict of tuple (param_name, param_value)
    :rtype: dict
    """
    if len(key_values) % 2 != 0:
        raise ValueError(
            "Expect a pair `param_name, param_value` for each " "parameters"
        )

    def pairwise(it):
        it = iter(it)
        while True:
            try:
                yield next(it), next(it)
            except StopIteration:
                # no more elements in the iterator
                return

    res = {}
    for name, value in pairwise(key_values):
        if name not in SETTABLE_PARAMETERS:
            raise ValueError(f"parameters {name} is not managed")
        if name in SETTABLE_PARAMETERS_TYPE:
            type_ = SETTABLE_PARAMETERS_TYPE[name]
            if type_ is not None:
                res[name] = type_(value)
                continue
        res[name] = value
    return res


[docs]class BaseHDF5ConfigHandler: """Class to handle inputs to HDF5Config. And insure there is no opposite Information """ @staticmethod def get_tuple_of_keys_from_cmd(cmd_value): return utils.get_tuple_of_keys_from_cmd(cmd_value) @staticmethod def conv_str_to_bool(bstr): return bstr in ("True", True) @staticmethod def conv_log_level(bool_debug): if bool_debug is True: return "debug" else: return "warning" @staticmethod def conv_xrd_ct_to_format(str_bool): if str_bool in ("True", True): return Format.XRD_CT elif str_bool in ("False", False): return Format.STANDARD else: return None
[docs] def __init__(self, argparse_options, raise_error=True): self._argparse_options = argparse_options self._config = None self.build_configuration(raise_error=raise_error)
@property def configuration(self) -> Union[None, TomoHDF5Config]: return self._config @property def argparse_options(self): return self._argparse_options def _check_argparse_options(self, raise_error): raise NotImplementedError("BaseClass") def _create_HDF5_config(self): raise NotImplementedError("BAse class")
[docs] def build_configuration(self, raise_error) -> bool: """ :param bool raise_error: raise error if encounter some errors. Else display a log message :return: True if the settings are valid """ self._check_argparse_options(raise_error=raise_error) options = self.argparse_options config = self._create_HDF5_config() # check input and output file if config.input_file is None: config.input_file = options.input_file elif options.input_file is not None and config.input_file != options.input_file: raise ValueError( "Two different input files provided from " "command line and from the configuration file" ) if config.input_file is None: err = "No input file provided" if raise_error: raise ValueError(err) else: _logger.error(err) if config.output_file is None: config.output_file = options.output_file elif ( options.output_file is not None and config.output_file != options.output_file ): raise ValueError( "Two different output files provided from " "command line and from the configuration file" ) if config.output_file is None: if config.file_extension is None: err = "If no outputfile provided you should provide the extension" raise ValueError(err) config.output_file = get_default_output_file( input_file=config.input_file, output_file_extension=config.file_extension.value, ) elif os.path.isdir(config.output_file): # if the output file is only a directory: consider we want the same file basename with default extension # in this directory input_file, _ = os.path.splitext(config.input_file) config.output_file = os.path.join( os.path.abspath(config.output_file), os.path.basename(input_file) + config.file_extension.value, ) # set parameter from the arg parse options # key is the name of the argparse option. # value is a tuple: (name of the setter in the HDF5Config, # function to format the input) self._config = self._map_option_to_config_param(config, options)
def _map_option_to_config_param(self, config, options): raise NotImplementedError("Base class") def __str__(self): raise NotImplementedError("")
[docs]class TomoHDF5ConfigHandler(BaseHDF5ConfigHandler): def _create_HDF5_config(self): if self.argparse_options.config: return TomoHDF5Config.from_cfg_file(self.argparse_options.config) else: return TomoHDF5Config() def _map_option_to_config_param(self, config, options): mapping = { "valid_camera_names": ( "valid_camera_names", self.get_tuple_of_keys_from_cmd, ), "overwrite": ("overwrite", self.conv_str_to_bool), "file_extension": ("file_extension", filter_str_def), "single_file": ("single_file", self.conv_str_to_bool), "debug": ("log_level", self.conv_log_level), "entries": ("entries", self.get_tuple_of_keys_from_cmd), "ignore_sub_entries": ( "sub_entries_to_ignore", self.get_tuple_of_keys_from_cmd, ), "duplicate_data": ("default_copy_behavior", self.conv_str_to_bool), "raises_error": ("raises_error", self.conv_str_to_bool), "field_of_view": ("field_of_view", filter_str_def), "request_input": ("request_input", self.conv_str_to_bool), "x_trans_keys": ("x_trans_keys", self.get_tuple_of_keys_from_cmd), "y_trans_keys": ("y_trans_keys", self.get_tuple_of_keys_from_cmd), "z_trans_keys": ("z_trans_keys", self.get_tuple_of_keys_from_cmd), "rot_angle_keys": ("rotation_angle_keys", self.get_tuple_of_keys_from_cmd), "sample_detector_distance_paths": ( "sample_detector_distance_paths", self.get_tuple_of_keys_from_cmd, ), "acq_expo_time_keys": ( "exposition_time_keys", self.get_tuple_of_keys_from_cmd, ), "x_pixel_size_key": ("x_pixel_size_paths", self.get_tuple_of_keys_from_cmd), "y_pixel_size_key": ("y_pixel_size_paths", self.get_tuple_of_keys_from_cmd), "init_titles": ("init_titles", self.get_tuple_of_keys_from_cmd), "init_zserie_titles": ( "zserie_init_titles", self.get_tuple_of_keys_from_cmd, ), "init_pcotomo_titles": ( "pcotomo_init_titles", self.get_tuple_of_keys_from_cmd, ), "dark_titles": ("dark_titles", self.get_tuple_of_keys_from_cmd), "flat_titles": ("flat_titles", self.get_tuple_of_keys_from_cmd), "proj_titles": ("projections_titles", self.get_tuple_of_keys_from_cmd), "align_titles": ("alignment_titles", self.get_tuple_of_keys_from_cmd), "set_params": ("param_already_defined", _extract_param_value), } for argparse_name, (config_name, format_fct) in mapping.items(): argparse_value = getattr(options, argparse_name) if argparse_value is not None: value = format_fct(argparse_value) # pylint: disable=E1102 setattr(config, config_name, value) return config def _check_argparse_options(self, raise_error): if self.argparse_options is None: err = "No argparse options provided" if raise_error: raise ValueError(err) else: _logger.error(err) return False options = self.argparse_options if options.config is not None: # check no other option are provided duplicated_inputs = [] for opt in ( "set_params", "align_titles", "proj_titles", "flat_titles", "dark_titles", "init_zserie_titles", "init_titles", "init_pcotomo_titles", "x_pixel_size_key", "y_pixel_size_key", "acq_expo_time_keys", "rot_angle_keys", "valid_camera_names", "z_trans_keys", "y_trans_keys", "x_trans_keys", "request_input", "raises_error", "ignore_sub_entries", "entries", "debug", "overwrite", "single_file", "file_extension", "field_of_view", "duplicate_data", ): if getattr(options, opt): duplicated_inputs.append(opt) if len(duplicated_inputs) > 0: err = f"You provided a configuration file and inputs for {duplicated_inputs}" if raise_error: raise ValueError(err) else: _logger.error(err) return False
[docs]class XRD3DHDF5ConfigHandler(TomoHDF5ConfigHandler): def _create_HDF5_config(self): if self.argparse_options.config: return XRD3DHDF5Config.from_cfg_file(self.argparse_options.config) else: return XRD3DHDF5Config() def _map_option_to_config_param(self, config, options): config = super()._map_option_to_config_param(config, options) mapping = { "rocking_keys": ("rocking_keys", self.get_tuple_of_keys_from_cmd), } for argparse_name, (config_name, format_fct) in mapping.items(): argparse_value = getattr(options, argparse_name) if argparse_value is not None: value = format_fct(argparse_value) # pylint: disable=E1102 setattr(config, config_name, value) return config def _check_argparse_options(self, raise_error): super()._check_argparse_options(raise_error) options = self.argparse_options if options.config is not None: # check no other option are provided duplicated_inputs = [] for opt in ("rocking_keys",): if getattr(options, opt): duplicated_inputs.append(opt) if len(duplicated_inputs) > 0: err = f"You provided a configuration file and inputs for {duplicated_inputs}" if raise_error: raise ValueError(err) else: _logger.error(err) return False