Skip to content
Snippets Groups Projects
RunDetails.py 7.52 KiB
Newer Older
from __future__ import (absolute_import, division, print_function)

from isis_powder.routines import common, yaml_parser
def create_run_details_object(run_number_string, inst_settings, is_vanadium_run, empty_run_call=None,
                              grouping_file_name_call=None, vanadium_run_call=None,
                              splined_name_list=None, van_abs_file_name=None):
    cal_map_dict = WrappedFunctionsRunDetails.get_cal_mapping_dict(
        run_number_string=run_number_string, inst_settings=inst_settings)
    run_number = common.get_first_run_number(run_number_string=run_number_string)
    # Get names of files we will be using
    calibration_dir = os.path.normpath(os.path.expanduser(inst_settings.calibration_dir))
    label = common.cal_map_dictionary_key_helper(dictionary=cal_map_dict, key="label")
    offset_file_name = common.cal_map_dictionary_key_helper(dictionary=cal_map_dict, key="offset_file_name")
    # Always make sure the offset file name is included
    if splined_name_list:
        # Force Python to make a copy so we don't modify original
        new_splined_list = list(splined_name_list)
        new_splined_list.append(offset_file_name)
        new_splined_list = [offset_file_name]
    # These can either be generic or custom so defer to another method
    results_dict = _get_customisable_attributes(
        cal_dict=cal_map_dict, inst_settings=inst_settings, empty_run_call=empty_run_call,
        grouping_name_call=grouping_file_name_call, vanadium_run_call=vanadium_run_call,
        splined_name_list=new_splined_list)

    vanadium_run_string = results_dict["vanadium_runs"]

    if is_vanadium_run:
        # The run number should be the vanadium number in this case
        run_number = vanadium_run_string
        output_run_string = vanadium_run_string
    else:
        output_run_string = run_number_string

    # Sample empty if there is one
    sample_empty = inst_settings.sample_empty if hasattr(inst_settings, "sample_empty") else None

    # Generate the paths
    grouping_file_path = os.path.join(calibration_dir, results_dict["grouping_file_name"])
    # Offset  and splined vanadium is within the correct label folder
    offset_file_path = os.path.join(calibration_dir, label, offset_file_name)
    splined_van_path = os.path.join(calibration_dir, label, results_dict["splined_van_name"])
    van_absorb_path = os.path.join(calibration_dir, van_abs_file_name) if van_abs_file_name else None

    return _RunDetails(empty_run_number=results_dict["empty_runs"], run_number=run_number,
                       output_run_string=output_run_string, label=label, offset_file_path=offset_file_path,
                       grouping_file_path=grouping_file_path, splined_vanadium_path=splined_van_path,
                       vanadium_run_number=vanadium_run_string, sample_empty=sample_empty,
                       vanadium_abs_path=van_absorb_path)


def _get_customisable_attributes(cal_dict, inst_settings, empty_run_call, grouping_name_call, vanadium_run_call,
                                 splined_name_list):
    dict_to_return = {}
    if empty_run_call:
        empty_runs = empty_run_call.get_result()
    else:
        empty_runs = common.cal_map_dictionary_key_helper(dictionary=cal_dict, key="empty_run_numbers")
    dict_to_return["empty_runs"] = empty_runs

    if vanadium_run_call:
        vanadium_runs = vanadium_run_call.get_result()
    else:
        vanadium_runs = common.cal_map_dictionary_key_helper(dictionary=cal_dict, key="vanadium_run_numbers")
    dict_to_return["vanadium_runs"] = vanadium_runs

    if grouping_name_call:
        grouping_name = grouping_name_call.get_result()
    else:
        grouping_name = inst_settings.grouping_file_name
    dict_to_return["grouping_file_name"] = grouping_name

    dict_to_return["splined_van_name"] = common.generate_splined_name(vanadium_runs, splined_name_list)

    return dict_to_return


class WrappedFunctionsRunDetails(object):
    def __init__(self):
        pass

    @staticmethod
    def get_cal_mapping_dict(run_number_string, inst_settings):
        # Get the python dictionary from the YAML mapping
        run_number = common.get_first_run_number(run_number_string=run_number_string)
        cal_mapping_dict = yaml_parser.get_run_dictionary(run_number_string=run_number,
                                                          file_path=inst_settings.cal_mapping_path)
        return cal_mapping_dict

    @staticmethod
    def cal_dictionary_key_helper(key, append_to_error_message=None, **kwargs):
        forwarded_value = kwargs.pop("forwarded_value")
        return common.cal_map_dictionary_key_helper(dictionary=forwarded_value, key=key,
                                                    append_to_error_message=append_to_error_message)


class RunDetailsFuncWrapper(object):
    # Holds a callable method, associated args and return value so we can pass it in
    # as a single method

    def __init__(self, function=None, *args, **func_kwargs):
        if args:
            # If we allow args with position Python gets confused as the forwarded value can be in the first place too
            raise RuntimeError("Cannot use un-named arguments with callable methods")

        self.function = function
        self.function_kwargs = func_kwargs

        self._previous_callable = None
        self._returned_value = None
        self._function_is_executed = False

    def _exec_func(self):
        forwarded_value = self._previous_callable.get_result() if self._previous_callable else None

        if not self.function:
            # We maybe are the 0th case just return any values we hold
            return forwarded_value

        if forwarded_value:
            self.function_kwargs["forwarded_value"] = forwarded_value
            self._returned_value = self.function(**self.function_kwargs)
            self._returned_value = self.function(**self.function_kwargs)

        self._function_is_executed = True

    def _set_previous_callable(self, previous_callable):
        if not previous_callable:
            return None
        elif not isinstance(previous_callable, RunDetailsFuncWrapper):
            raise ValueError("Previous callable is not a RunDetailsFuncWrapper type")
    def add_to_func_chain(self, function, *args, **func_kwargs):
        # Construct a new object that will be the next in line
        next_in_chain = RunDetailsFuncWrapper(function=function, *args, **func_kwargs)
        next_in_chain._set_previous_callable(self)
        return next_in_chain

    def get_result(self):
        if not self._function_is_executed:
            self._exec_func()
        return self._returned_value

    """
    This class holds the full file paths associated with each run and various other useful attributes
    """

    def __init__(self, empty_run_number, run_number, output_run_string, label,
                 offset_file_path, grouping_file_path, splined_vanadium_path, vanadium_run_number,
        self.output_run_string = output_run_string

        self.label = label
        self.offset_file_path = offset_file_path
        self.grouping_file_path = grouping_file_path
        self.splined_vanadium_file_path = splined_vanadium_path
        self.vanadium_run_numbers = vanadium_run_number
        # Optional
        self.sample_empty = sample_empty
        self.vanadium_absorption_path = vanadium_abs_path