diff --git a/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/SANS/SANSBeamCentreFinder.py b/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/SANS/SANSBeamCentreFinder.py index 892bf9a7181e97990e2c0be6ca14f6102db6168f..7e1c386951f3849aef57d6483831f8c0c3f5eff9 100644 --- a/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/SANS/SANSBeamCentreFinder.py +++ b/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/SANS/SANSBeamCentreFinder.py @@ -14,7 +14,7 @@ import numpy as np from mantid import AnalysisDataService from mantid.api import (DataProcessorAlgorithm, MatrixWorkspaceProperty, AlgorithmFactory, PropertyMode, Progress) -from mantid.kernel import (Direction, PropertyManagerProperty, StringListValidator, Logger) +from mantid.kernel import (Direction, StringListValidator, Logger) from mantid.simpleapi import CloneWorkspace, GroupWorkspaces from sans.algorithm_detail.beamcentrefinder_plotting import can_plot_beamcentrefinder, plot_workspace_quartiles from sans.algorithm_detail.crop_helper import get_component_name @@ -25,7 +25,7 @@ from sans.common.enums import (DetectorType, MaskingQuadrant, FindDirectionEnum) from sans.common.file_information import get_instrument_paths_for_sans_file from sans.common.general_functions import create_child_algorithm from sans.common.xml_parsing import get_named_elements_from_ipf_file -from sans.state.state_base import create_deserialized_sans_state_from_property_manager +from sans.state.Serializer import Serializer class SANSBeamCentreFinder(DataProcessorAlgorithm): @@ -40,8 +40,8 @@ class SANSBeamCentreFinder(DataProcessorAlgorithm): # INPUT # ---------- # Workspace which is to be cropped - self.declareProperty(PropertyManagerProperty('SANSState'), - doc='A property manager which fulfills the SANSState contract.') + self.declareProperty('SANSState', '', + doc='A JSON string which fulfills the SANSState contract.') self.declareProperty(MatrixWorkspaceProperty("SampleScatterWorkspace", '', optional=PropertyMode.Mandatory, direction=Direction.Input), @@ -112,7 +112,6 @@ class SANSBeamCentreFinder(DataProcessorAlgorithm): def PyExec(self): state = self._get_state() - state_serialized = state.property_manager self.logger = Logger("CentreFinder") self.logger.notice("Starting centre finder routine...") progress = self._get_progress() @@ -174,11 +173,11 @@ class SANSBeamCentreFinder(DataProcessorAlgorithm): progress.report("Reducing ... Pos1 " + str(centre1) + " Pos2 " + str(centre2)) sample_quartiles = self._run_quartile_reduction(sample_scatter, sample_transmission, sample_direct, "Sample", sample_scatter_monitor, component, - state_serialized, centre1, centre2, r_min, r_max) + centre1, centre2, r_min, r_max) if can_scatter: can_quartiles = self._run_quartile_reduction(can_scatter, can_transmission, can_direct, "Can", - can_scatter_monitor, component, state_serialized, centre1, + can_scatter_monitor, component, centre1, centre2, r_min, r_max) for key in sample_quartiles: sample_quartiles[key] = perform_can_subtraction(sample_quartiles[key], can_quartiles[key], self) @@ -284,14 +283,17 @@ class SANSBeamCentreFinder(DataProcessorAlgorithm): return '' def _run_quartile_reduction(self, scatter_workspace, transmission_workspace, direct_workspace, data_type, - scatter_monitor_workspace, component, state, centre1, centre2, r_min, r_max): + scatter_monitor_workspace, component, centre1, centre2, r_min, r_max): + + serialized_state = self.getProperty("SANSState").value + algorithm_name = "SANSBeamCentreFinderCore" alg_options = {"ScatterWorkspace": scatter_workspace, "ScatterMonitorWorkspace": scatter_monitor_workspace, "TransmissionWorkspace": transmission_workspace, "DirectWorkspace": direct_workspace, "Component": component, - "SANSState": state, + "SANSState": serialized_state, "DataType": data_type, "Centre1": centre1, "Centre2": centre2, @@ -316,9 +318,9 @@ class SANSBeamCentreFinder(DataProcessorAlgorithm): return get_component_name(workspace, component) def _get_state(self): - state_property_manager = self.getProperty("SANSState").value - state = create_deserialized_sans_state_from_property_manager(state_property_manager) - state.property_manager = state_property_manager + state_json = self.getProperty("SANSState").value + state = Serializer.from_json(state_json) + return state def _calculate_residuals(self, quartile1, quartile2): diff --git a/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/SANS/SANSBeamCentreFinderCore.py b/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/SANS/SANSBeamCentreFinderCore.py index bf1050837d382b416549377c3d4edcd5b6b5ec2e..b3356723c4d895e96152f23c1c70b8680f946bc3 100644 --- a/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/SANS/SANSBeamCentreFinderCore.py +++ b/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/SANS/SANSBeamCentreFinderCore.py @@ -12,7 +12,7 @@ from __future__ import (absolute_import, division, print_function) from mantid.api import (DataProcessorAlgorithm, MatrixWorkspaceProperty, AlgorithmFactory, PropertyMode, Progress, IEventWorkspace) -from mantid.kernel import (Direction, PropertyManagerProperty, StringListValidator) +from mantid.kernel import (Direction, StringListValidator) from sans.algorithm_detail.CreateSANSAdjustmentWorkspaces import CreateSANSAdjustmentWorkspaces from sans.algorithm_detail.convert_to_q import convert_workspace from sans.algorithm_detail.crop_helper import get_component_name @@ -24,7 +24,7 @@ from sans.algorithm_detail.xml_shapes import quadrant_xml from sans.common.constants import EMPTY_NAME from sans.common.enums import (DetectorType, DataType, MaskingQuadrant) from sans.common.general_functions import create_child_algorithm, append_to_sans_file_tag -from sans.state.state_base import create_deserialized_sans_state_from_property_manager +from sans.state.Serializer import Serializer class SANSBeamCentreFinderCore(DataProcessorAlgorithm): @@ -38,8 +38,8 @@ class SANSBeamCentreFinderCore(DataProcessorAlgorithm): # ---------- # INPUT # ---------- - self.declareProperty(PropertyManagerProperty('SANSState'), - doc='A property manager which fulfills the SANSState contract.') + self.declareProperty('SANSState', '', + doc='A JSON string which fulfills the SANSState contract.') # WORKSPACES # Scatter Workspaces @@ -410,9 +410,9 @@ class SANSBeamCentreFinderCore(DataProcessorAlgorithm): return errors def _get_state(self): - state_property_manager = self.getProperty("SANSState").value - state = create_deserialized_sans_state_from_property_manager(state_property_manager) - state.property_manager = state_property_manager + state_json = self.getProperty("SANSState").value + state = Serializer.from_json(state_json) + return state def _get_transmission_workspace(self): diff --git a/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/SANS/SANSBeamCentreFinderMassMethod.py b/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/SANS/SANSBeamCentreFinderMassMethod.py index d07bdb3797e7e44afe149917849a0e9bf5c5d8da..b04430c6ccb1eff47cf8b82f11725f8b7f37e9e4 100644 --- a/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/SANS/SANSBeamCentreFinderMassMethod.py +++ b/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/SANS/SANSBeamCentreFinderMassMethod.py @@ -12,7 +12,7 @@ from __future__ import (absolute_import, division, print_function) from mantid.api import (DataProcessorAlgorithm, MatrixWorkspaceProperty, AlgorithmFactory, PropertyMode, Progress, IEventWorkspace) -from mantid.kernel import (Direction, PropertyManagerProperty, StringListValidator) +from mantid.kernel import (Direction, StringListValidator) from sans.algorithm_detail.crop_helper import get_component_name from sans.algorithm_detail.mask_sans_workspace import mask_workspace from sans.algorithm_detail.move_sans_instrument_component import move_component, MoveTypes @@ -21,7 +21,7 @@ from sans.algorithm_detail.slice_sans_event import slice_sans_event from sans.common.constants import EMPTY_NAME from sans.common.enums import (DetectorType) from sans.common.general_functions import create_child_algorithm, append_to_sans_file_tag -from sans.state.state_base import create_deserialized_sans_state_from_property_manager +from sans.state.Serializer import Serializer class SANSBeamCentreFinderMassMethod(DataProcessorAlgorithm): @@ -36,8 +36,8 @@ class SANSBeamCentreFinderMassMethod(DataProcessorAlgorithm): # INPUT # ---------- # Workspace which is to be cropped - self.declareProperty(PropertyManagerProperty('SANSState'), - doc='A property manager which fulfills the SANSState contract.') + self.declareProperty('SANSState', '', + doc='A JSON string which fulfills the SANSState contract.') self.declareProperty(MatrixWorkspaceProperty("SampleScatterWorkspace", '', optional=PropertyMode.Mandatory, direction=Direction.Input), @@ -266,9 +266,9 @@ class SANSBeamCentreFinderMassMethod(DataProcessorAlgorithm): return workspace def _get_state(self): - state_property_manager = self.getProperty("SANSState").value - state = create_deserialized_sans_state_from_property_manager(state_property_manager) - state.property_manager = state_property_manager + state_json = self.getProperty("SANSState").value + state = Serializer.from_json(state_json) + return state def _get_monitor_workspace(self): diff --git a/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/SANS/SANSCreateAdjustmentWorkspaces.py b/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/SANS/SANSCreateAdjustmentWorkspaces.py index c9351a18a22367e54891295af5480a271b363278..69d4cbe9af5bd8241713e92ca619e5f8ebb19692 100644 --- a/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/SANS/SANSCreateAdjustmentWorkspaces.py +++ b/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/SANS/SANSCreateAdjustmentWorkspaces.py @@ -14,13 +14,13 @@ from __future__ import (absolute_import, division, print_function) from mantid.api import (DistributedDataProcessorAlgorithm, MatrixWorkspaceProperty, AlgorithmFactory, PropertyMode, WorkspaceUnitValidator) -from mantid.kernel import (Direction, PropertyManagerProperty, StringListValidator, CompositeValidator) +from mantid.kernel import (Direction, StringListValidator, CompositeValidator) from sans.algorithm_detail.calculate_sans_transmission import calculate_transmission from sans.algorithm_detail.normalize_to_sans_monitor import normalize_to_monitor from sans.common.constants import EMPTY_NAME from sans.common.enums import (DataType, DetectorType) from sans.common.general_functions import create_unmanaged_algorithm -from sans.state.state_base import create_deserialized_sans_state_from_property_manager +from sans.state.Serializer import Serializer class SANSCreateAdjustmentWorkspaces(DistributedDataProcessorAlgorithm): @@ -36,8 +36,8 @@ class SANSCreateAdjustmentWorkspaces(DistributedDataProcessorAlgorithm): # INPUT # --------------- # State - self.declareProperty(PropertyManagerProperty('SANSState'), - doc='A property manager which fulfills the SANSState contract.') + self.declareProperty('SANSState', '', + doc='A JSON string which fulfills the SANSState contract.') # Input workspaces self.declareProperty(MatrixWorkspaceProperty('TransmissionWorkspace', '', @@ -103,8 +103,8 @@ class SANSCreateAdjustmentWorkspaces(DistributedDataProcessorAlgorithm): def PyExec(self): # Read the state - state_property_manager = self.getProperty("SANSState").value - state = create_deserialized_sans_state_from_property_manager(state_property_manager) + state_json = self.getProperty("SANSState").value + state = Serializer.from_json(state_json) # -------------------------------------- # Get the monitor normalization workspace @@ -127,8 +127,7 @@ class SANSCreateAdjustmentWorkspaces(DistributedDataProcessorAlgorithm): # Get the full wavelength and pixel adjustment # -------------------------------------------- wave_length_adjustment_workspace, \ - pixel_length_adjustment_workspace = self._get_wavelength_and_pixel_adjustment_workspaces(state, - monitor_normalization_workspace, + pixel_length_adjustment_workspace = self._get_wavelength_and_pixel_adjustment_workspaces(monitor_normalization_workspace, # noqa calculated_transmission_workspace) # noqa @@ -142,13 +141,13 @@ class SANSCreateAdjustmentWorkspaces(DistributedDataProcessorAlgorithm): self.setProperty("CalculatedTransmissionWorkspace", calculated_transmission_workspace) self.setProperty("UnfittedTransmissionWorkspace", unfitted_transmission_workspace) - def _get_wavelength_and_pixel_adjustment_workspaces(self, state, + def _get_wavelength_and_pixel_adjustment_workspaces(self, monitor_normalization_workspace, calculated_transmission_workspace): component = self.getProperty("Component").value wave_pixel_adjustment_name = "SANSCreateWavelengthAndPixelAdjustment" - serialized_state = state.property_manager + serialized_state = self.getProperty("SANSState").value wave_pixel_adjustment_options = {"SANSState": serialized_state, "NormalizeToMonitorWorkspace": monitor_normalization_workspace, "OutputWorkspaceWavelengthAdjustment": EMPTY_NAME, @@ -222,8 +221,7 @@ class SANSCreateAdjustmentWorkspaces(DistributedDataProcessorAlgorithm): # Check that the input can be converted into the right state object state_property_manager = self.getProperty("SANSState").value try: - state = create_deserialized_sans_state_from_property_manager(state_property_manager) - state.property_manager = state_property_manager + state = Serializer.from_json(state_property_manager) state.validate() except ValueError as err: errors.update({"SANSCreateAdjustmentWorkspaces": str(err)}) diff --git a/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/SANS/SANSLoad.py b/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/SANS/SANSLoad.py index bdbe213a5b89bafd55d84c54b4f0b7e8306c7937..252521b89afdd6f8c443b028c4db5dfba63b9c85 100644 --- a/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/SANS/SANSLoad.py +++ b/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/SANS/SANSLoad.py @@ -11,13 +11,13 @@ from __future__ import (absolute_import, division, print_function) from mantid.api import (ParallelDataProcessorAlgorithm, MatrixWorkspaceProperty, AlgorithmFactory, PropertyMode, - Progress, - WorkspaceProperty) -from mantid.kernel import (Direction, PropertyManagerProperty, FloatArrayProperty) + Progress, WorkspaceProperty) +from mantid.kernel import (Direction, FloatArrayProperty) from sans.algorithm_detail.load_data import SANSLoadDataFactory from sans.algorithm_detail.move_sans_instrument_component import move_component, MoveTypes from sans.common.enums import SANSDataType -from sans.state.state_base import create_deserialized_sans_state_from_property_manager + +from sans.state.Serializer import Serializer class SANSLoad(ParallelDataProcessorAlgorithm): @@ -31,8 +31,8 @@ class SANSLoad(ParallelDataProcessorAlgorithm): # ---------- # INPUT # ---------- - self.declareProperty(PropertyManagerProperty('SANSState'), - doc='A property manager which fulfills the SANSState contract.') + self.declareProperty('SANSState', "", + doc='A JSON String which fulfills the SANSState contract.') self.declareProperty("PublishToCache", True, direction=Direction.Input, doc="Publish the calibration workspace to a cache, in order to avoid reloading " @@ -117,7 +117,7 @@ class SANSLoad(ParallelDataProcessorAlgorithm): def PyExec(self): # Read the state state_property_manager = self.getProperty("SANSState").value - state = create_deserialized_sans_state_from_property_manager(state_property_manager) + state = Serializer.from_json(state_property_manager) # Run the appropriate SANSLoader and get the workspaces and the workspace monitors # Note that cache optimization is only applied to the calibration workspace since it is not available as a @@ -153,13 +153,13 @@ class SANSLoad(ParallelDataProcessorAlgorithm): def validateInputs(self): errors = dict() # Check that the input can be converted into the right state object - state_property_manager = self.getProperty("SANSState").value + state_json = self.getProperty("SANSState").value try: - state = create_deserialized_sans_state_from_property_manager(state_property_manager) - state.property_manager = state_property_manager + state = Serializer.from_json(state_json) state.validate() except ValueError as err: errors.update({"SANSState": str(err)}) + return errors # We need to validate that the for each expected output workspace of the SANSState a output workspace name # was supplied in the PyInit @@ -178,7 +178,6 @@ class SANSLoad(ParallelDataProcessorAlgorithm): # ------------------------------------ # Check the optional output workspaces # If they are specified in the SANSState, then we require them to be set on the output as well. - state = create_deserialized_sans_state_from_property_manager(state_property_manager) data_info = state.data # For sample transmission diff --git a/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/SANS/SANSReductionCoreBase.py b/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/SANS/SANSReductionCoreBase.py index 8c7f43c7f3eb926238b1ba8d97bf310e6c7fbd5f..c46946979ea8ec694716f0538bd273659fd3d74e 100644 --- a/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/SANS/SANSReductionCoreBase.py +++ b/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/SANS/SANSReductionCoreBase.py @@ -11,7 +11,7 @@ from __future__ import (absolute_import, division, print_function) from mantid.api import (DistributedDataProcessorAlgorithm, MatrixWorkspaceProperty, PropertyMode, IEventWorkspace) -from mantid.kernel import (Direction, PropertyManagerProperty, StringListValidator) +from mantid.kernel import (Direction, StringListValidator) from sans.algorithm_detail.CreateSANSAdjustmentWorkspaces import CreateSANSAdjustmentWorkspaces from sans.algorithm_detail.convert_to_q import convert_workspace from sans.algorithm_detail.crop_helper import get_component_name @@ -22,7 +22,7 @@ from sans.algorithm_detail.slice_sans_event import slice_sans_event from sans.common.constants import EMPTY_NAME from sans.common.enums import (DetectorType, DataType) from sans.common.general_functions import (create_child_algorithm, append_to_sans_file_tag) -from sans.state.state_base import create_deserialized_sans_state_from_property_manager +from sans.state.Serializer import Serializer class SANSReductionCoreBase(DistributedDataProcessorAlgorithm): @@ -30,8 +30,8 @@ class SANSReductionCoreBase(DistributedDataProcessorAlgorithm): # ---------- # INPUT # ---------- - self.declareProperty(PropertyManagerProperty('SANSState'), - doc='A property manager which fulfills the SANSState contract.') + self.declareProperty('SANSState', '', + doc='A JSON String which fulfills the SANSState contract.') # WORKSPACES # Scatter Workspaces @@ -236,9 +236,8 @@ class SANSReductionCoreBase(DistributedDataProcessorAlgorithm): return output_workspace, sum_of_counts, sum_of_norms def _get_state(self): - state_property_manager = self.getProperty("SANSState").value - state = create_deserialized_sans_state_from_property_manager(state_property_manager) - state.property_manager = state_property_manager + json_state = self.getProperty("SANSState").value + state = Serializer.from_json(json_state) return state def _get_transmission_workspace(self): diff --git a/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/SANS/SANSReductionCoreEventSlice.py b/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/SANS/SANSReductionCoreEventSlice.py index 77f101549b7fab0d3cf183f697b9c58b206dc925..eea62a214ced21f9d8557a8cba816310e756f836 100644 --- a/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/SANS/SANSReductionCoreEventSlice.py +++ b/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/SANS/SANSReductionCoreEventSlice.py @@ -15,7 +15,7 @@ from SANSReductionCoreBase import SANSReductionCoreBase from mantid.api import (MatrixWorkspaceProperty, AlgorithmFactory, PropertyMode, Progress) -from mantid.kernel import (Direction, PropertyManagerProperty, StringListValidator) +from mantid.kernel import (Direction, StringListValidator) from sans.common.enums import (DetectorType, DataType) @@ -31,8 +31,8 @@ class SANSReductionCoreEventSlice(SANSReductionCoreBase): # ---------- # INPUT # ---------- - self.declareProperty(PropertyManagerProperty('SANSState'), - doc='A property manager which fulfills the SANSState contract.') + self.declareProperty('SANSState', '', + doc='A JSON string which fulfills the SANSState contract.') # WORKSPACES # Scatter Workspaces diff --git a/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/SANS/SANSSingleReduction.py b/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/SANS/SANSSingleReduction.py index f1c94d6c6dc9cae4f463cf4b907920f46ba0acee..a17d8ab41a8f273faafcbc6db43e7544d257a4d6 100644 --- a/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/SANS/SANSSingleReduction.py +++ b/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/SANS/SANSSingleReduction.py @@ -283,7 +283,7 @@ class SANSSingleReduction(SANSSingleReductionBase): def set_transmission_workspaces_on_output(self, transmission_bundles, fit_state): for transmission_bundle in transmission_bundles: - fit_performed = fit_state[transmission_bundle.data_type].fit_type != FitType.NO_FIT + fit_performed = fit_state[transmission_bundle.data_type.value].fit_type != FitType.NO_FIT calculated_transmission_workspace = transmission_bundle.calculated_transmission_workspace unfitted_transmission_workspace = transmission_bundle.unfitted_transmission_workspace if transmission_bundle.data_type is DataType.CAN: diff --git a/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/SANS/SANSSingleReduction2.py b/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/SANS/SANSSingleReduction2.py index aa6b8222e74596aaa755259514bee08c555d09ef..0ca3fb774dbe530a0ba6ecefc1c0453c93f78adc 100644 --- a/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/SANS/SANSSingleReduction2.py +++ b/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/SANS/SANSSingleReduction2.py @@ -433,7 +433,7 @@ class SANSSingleReduction(SANSSingleReductionBase): def set_transmission_workspaces_on_output(self, transmission_bundles, fit_state): for transmission_bundle in transmission_bundles: - fit_performed = fit_state[transmission_bundle.data_type].fit_type != FitType.NO_FIT + fit_performed = fit_state[transmission_bundle.data_type.value].fit_type != FitType.NO_FIT calculated_transmission_workspace = transmission_bundle.calculated_transmission_workspace unfitted_transmission_workspace = transmission_bundle.unfitted_transmission_workspace if transmission_bundle.data_type is DataType.CAN: diff --git a/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/SANS/SANSSingleReductionBase.py b/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/SANS/SANSSingleReductionBase.py index e2bb4ba5c5a8a7289a8faad319264cc3c1bbd1ad..f5bc11cc8a42e2a9719e10ebdc71c0b24a805f13 100644 --- a/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/SANS/SANSSingleReductionBase.py +++ b/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/SANS/SANSSingleReductionBase.py @@ -14,14 +14,14 @@ from collections import defaultdict from mantid.api import (DistributedDataProcessorAlgorithm, MatrixWorkspaceProperty, Progress, PropertyMode) -from mantid.kernel import (Direction, PropertyManagerProperty) +from mantid.kernel import Direction from sans.algorithm_detail.bundles import ReductionSettingBundle from sans.algorithm_detail.single_execution import (get_final_output_workspaces, get_merge_bundle_for_merge_request) from sans.algorithm_detail.strip_end_nans_and_infs import strip_end_nans from sans.common.enums import (DataType, ReductionMode) from sans.common.general_functions import create_child_algorithm -from sans.state.state_base import create_deserialized_sans_state_from_property_manager +from sans.state.Serializer import Serializer class SANSSingleReductionBase(DistributedDataProcessorAlgorithm): @@ -29,8 +29,8 @@ class SANSSingleReductionBase(DistributedDataProcessorAlgorithm): # ---------- # INPUT # ---------- - self.declareProperty(PropertyManagerProperty('SANSState'), - doc='A property manager which fulfills the SANSState contract.') + self.declareProperty('SANSState', '', + doc='A JSON string which fulfills the SANSState contract.') self.declareProperty("UseOptimizations", True, direction=Direction.Input, doc="When enabled the ADS is being searched for already loaded and reduced workspaces. " @@ -233,9 +233,9 @@ class SANSSingleReductionBase(DistributedDataProcessorAlgorithm): return errors def _get_state(self): - state_property_manager = self.getProperty("SANSState").value - state = create_deserialized_sans_state_from_property_manager(state_property_manager) - state.property_manager = state_property_manager + state_json = self.getProperty("SANSState").value + state = Serializer.from_json(state_json) + return state @staticmethod diff --git a/Testing/SystemTests/tests/analysis/SANSBeamCentreFinderCoreTest.py b/Testing/SystemTests/tests/analysis/SANSBeamCentreFinderCoreTest.py index 99dfdb2e7a537cf766ad88bf0bb92a4c37e48196..dac357ff81a1b3dcb4769b342fc901ecbd03c903 100644 --- a/Testing/SystemTests/tests/analysis/SANSBeamCentreFinderCoreTest.py +++ b/Testing/SystemTests/tests/analysis/SANSBeamCentreFinderCoreTest.py @@ -13,6 +13,7 @@ import systemtesting import mantid from mantid.api import AlgorithmManager +from sans.state.Serializer import Serializer from sans.state.data import get_data_builder from sans.common.enums import (DetectorType, DataType, SANSFacility) @@ -31,7 +32,7 @@ class SANSBeamCentreFinderCoreTest(unittest.TestCase): load_alg.setChild(True) load_alg.initialize() - state_dict = state.property_manager + state_dict = Serializer.to_json(state) load_alg.setProperty("SANSState", state_dict) load_alg.setProperty("PublishToCache", False) load_alg.setProperty("UseCached", False) @@ -64,7 +65,7 @@ class SANSBeamCentreFinderCoreTest(unittest.TestCase): beam_centre_core_alg.setChild(True) beam_centre_core_alg.initialize() - state_dict = state.property_manager + state_dict = Serializer.to_json(state) beam_centre_core_alg.setProperty("SANSState", state_dict) beam_centre_core_alg.setProperty("ScatterWorkspace", workspace) beam_centre_core_alg.setProperty("ScatterMonitorWorkspace", monitor) diff --git a/Testing/SystemTests/tests/analysis/SANSLoadTest.py b/Testing/SystemTests/tests/analysis/SANSLoadTest.py index 12ced9eeaeaa1b23417a6bc3cb638627bc2e42ed..4ed7a0ccef866806f47f7ae7338f6276fc603f13 100644 --- a/Testing/SystemTests/tests/analysis/SANSLoadTest.py +++ b/Testing/SystemTests/tests/analysis/SANSLoadTest.py @@ -20,6 +20,7 @@ from sans.common.constants import (CALIBRATION_WORKSPACE_TAG, SANS_FILE_TAG) # Not clear why the names in the module are not found by Pylint, but it seems to get confused. Hence this check # needs to be disabled here. # pylint: disable=no-name-in-module +from sans.state.Serializer import Serializer from sans.test_helper.test_director import TestDirector from sans.common.enums import SANSFacility from sans.state.data import get_data_builder @@ -200,7 +201,7 @@ class SANSLoadTest(unittest.TestCase): load_alg.setRethrows(True) load_alg.initialize() - state_dict = state.property_manager + state_dict = Serializer.to_json(state) load_alg.setProperty("SANSState", state_dict) load_alg.setProperty("PublishToCache", publish_to_cache) load_alg.setProperty("UseCached", use_cached) diff --git a/Testing/SystemTests/tests/analysis/SANSReductionCoreTest.py b/Testing/SystemTests/tests/analysis/SANSReductionCoreTest.py index e6c8fe65ad38b2468e43875a259f346ea6ffc848..bc10d87cfe452a438115935aa977db7bd4492f78 100644 --- a/Testing/SystemTests/tests/analysis/SANSReductionCoreTest.py +++ b/Testing/SystemTests/tests/analysis/SANSReductionCoreTest.py @@ -13,6 +13,7 @@ import systemtesting import mantid from mantid.api import AlgorithmManager +from sans.state.Serializer import Serializer from sans.state.data import get_data_builder from sans.common.enums import (DetectorType, DataType, SANSFacility) @@ -31,7 +32,7 @@ class SANSReductionCoreTest(unittest.TestCase): load_alg.setChild(True) load_alg.initialize() - state_dict = state.property_manager + state_dict = Serializer.to_json(state) load_alg.setProperty("SANSState", state_dict) load_alg.setProperty("PublishToCache", False) load_alg.setProperty("UseCached", False) @@ -63,7 +64,7 @@ class SANSReductionCoreTest(unittest.TestCase): reduction_core_alg.setChild(True) reduction_core_alg.initialize() - state_dict = state.property_manager + state_dict = Serializer.to_json(state) reduction_core_alg.setProperty("SANSState", state_dict) reduction_core_alg.setProperty("ScatterWorkspace", workspace) reduction_core_alg.setProperty("ScatterMonitorWorkspace", monitor) diff --git a/Testing/SystemTests/tests/analysis/SANSSingleReductionTest.py b/Testing/SystemTests/tests/analysis/SANSSingleReductionTest.py index bac2bbb2603bd4f56aaaab5e27ee6fa44f7900eb..eaf4273e917f2301c7d3352855aa247623c07b91 100644 --- a/Testing/SystemTests/tests/analysis/SANSSingleReductionTest.py +++ b/Testing/SystemTests/tests/analysis/SANSSingleReductionTest.py @@ -13,6 +13,7 @@ import unittest import mantid # noqa from mantid.api import AlgorithmManager +from sans.state.Serializer import Serializer from sans.user_file.state_director import StateDirectorISIS from sans.state.data import get_data_builder from sans.common.enums import (SANSFacility, ReductionMode, ReductionDimensionality, FitModeForMerge) @@ -30,7 +31,7 @@ class SingleReductionTest(unittest.TestCase): load_alg.setChild(True) load_alg.initialize() - state_dict = state.property_manager + state_dict = Serializer.to_json(state) load_alg.setProperty("SANSState", state_dict) load_alg.setProperty("PublishToCache", False) load_alg.setProperty("UseCached", False) @@ -101,7 +102,7 @@ class SingleReductionTest(unittest.TestCase): output_settings=None, event_slice_optimisation=False, save_can=False, use_optimizations=False): single_reduction_name = "SANSSingleReduction" ver = 1 if not event_slice_optimisation else 2 - state_dict = state.property_manager + state_dict = Serializer.to_json(state) single_reduction_options = {"SANSState": state_dict, "SampleScatterWorkspace": sample_scatter, diff --git a/dev-docs/source/ISISSANSReductionBackend.rst b/dev-docs/source/ISISSANSReductionBackend.rst index c8c6ea13ce5a307f9a94daffc3a7d20b56c5a14f..4df10a40583bced8f7269ebe8d4fb4aed1a616d0 100644 --- a/dev-docs/source/ISISSANSReductionBackend.rst +++ b/dev-docs/source/ISISSANSReductionBackend.rst @@ -152,37 +152,18 @@ the state construction. *state_base.py* ^^^^^^^^^^^^^^^ -The *state_base.py* module contains the essential ingredients for defining a -state object. These are the *StateBase* class which allows for serialization -and a set of *TypedParameter*. - -The *StateBase*'s *property_manager* property is responsible for serialization. -Due to the nature of the *PropertyManagerProperty* of algorithms it serializes -the state object to a Python dictionary and receives a Mantid *PropertyManager* -object. This asymmetry is unfortunate, but mirrors the asymmetry of the -algorithm inputs. - -States which want to fulfill the *StateBase* contract must override the -*validate* method. This method is used to ensure internal consistency -of the *TypedParameters* on the state. It is important to have comprehensive -and tight checks here. - -The entries on the state objects are all descriptors of type *TypedParameter* which allows -for type checking, ensuring consistency early on. It is easy to -build custom types. The current list of types are: - -- *StringParameter* -- *BoolParameter* -- *FloatParameter* -- *PositiveFloatParameter* -- *PositiveIntegerParameter* -- *DictParameter* -- *FloatWithNoneParameter* -- *StringWithNoneParameter* -- *PositiveFloatWithNoneParameter* -- *FloatListParameter* -- *StringListParameter* -- *PositiveIntegerListParameter* +The *JsonSerializable* metaclass contains the essential ingredients for +serializing a state object. Additionally it provides a decorator for any +Enum types which need to be JSON serializable. + +Any classes which use the metaclass must place any attributes they intend +to be serialized into JSON string in the instance. I.e. class level variables +are not recommended since they may not end up in the instances internal +dictionary. + +The *Serializer* is responsible for serialization using the JSON library and +provides static methods to (de)serialize to a string or file. + Individual states ^^^^^^^^^^^^^^^^^ diff --git a/docs/source/release/v4.3.0/sans.rst b/docs/source/release/v4.3.0/sans.rst index 683c3414fcbd6b6d681e24ec0df211380d7d02f6..649868e6eafcaa97c5fd9043462bcadaee924d53 100644 --- a/docs/source/release/v4.3.0/sans.rst +++ b/docs/source/release/v4.3.0/sans.rst @@ -15,5 +15,8 @@ Improved - :ref:`MaskBTP <algm-MaskBTP>` now handles both old and new instrument definitions for BIOSANS and GPSANS - Data with invalid proton charge logs will now be fixed before performing slicing. A warning is emitted when this happens. +- ISIS SANS history for top level algorithms now works correctly. A user + can copy the history of a workspace to their clipboard or a file and the data + will be reproduced on that machine without requiring editing of the script. :ref:`Release 4.3.0 <v4.3.0>` diff --git a/scripts/Interface/ui/sans_isis/sans_data_processor_window.ui b/scripts/Interface/ui/sans_isis/sans_data_processor_window.ui index 716857a7123ccf2c9272f1e5e8e4ef3664b777e9..4d13462bde0bfd113bd88c9686649bc6e9701692 100644 --- a/scripts/Interface/ui/sans_isis/sans_data_processor_window.ui +++ b/scripts/Interface/ui/sans_isis/sans_data_processor_window.ui @@ -648,7 +648,7 @@ QGroupBox::title { <item> <widget class="QTabWidget" name="settings_tab_widget"> <property name="currentIndex"> - <number>2</number> + <number>0</number> </property> <widget class="QWidget" name="general_tab"> <attribute name="title"> @@ -856,7 +856,7 @@ QGroupBox::title { <bool>true</bool> </property> <property name="toolTip"> - <string> + <string> <html><head/><body><p>In the case of data which was measured in event-mode, it is possible to perform time-of-flight slices of the data and reduce these separately.</p><p>Input can be:</p><p>-<span style=" font-style:italic;"> start:step:stop</span> specifies time slices from the <span style=" font-style:italic;">start</span> value to the <span style=" font-style:italic;">stop </span>value in steps of <span style=" font-style:italic;">step</span></p><p>- <span style=" font-style:italic;">start-stop </span>which specifies a time slice from the <span style=" font-style:italic;">start</span> value to the <span style=" font-style:italic;">stop</span> value</p><p>- <span style=" font-style:italic;">&gt;start</span> specifies a slice from the <span style=" font-style:italic;">start </span>value to the end of the data set</p><p>- <span style=" font-style:italic;">&lt;stop</span> specifes a slice from the start of the data set to the <span style=" font-style:italic;">stop </span>value</p><p>In addition it is possible to concatenate these specifications using comma-separation. An example is:</p><p><span style=" font-style:italic;">5-10,12:2:16,20-30</span></p></body></html> </string> </property> diff --git a/scripts/Interface/ui/sans_isis/settings_diagnostic_tab.py b/scripts/Interface/ui/sans_isis/settings_diagnostic_tab.py index 0a88c566e8c6ba9126618c054c0d63b0d357b771..b2cf5cca0b01a39fc1b42994b5ec6245432f9f7b 100644 --- a/scripts/Interface/ui/sans_isis/settings_diagnostic_tab.py +++ b/scripts/Interface/ui/sans_isis/settings_diagnostic_tab.py @@ -12,7 +12,6 @@ and helps the developer to identify issues. """ from __future__ import (absolute_import, division, print_function) - from abc import ABCMeta, abstractmethod import os from qtpy import QtWidgets @@ -23,7 +22,6 @@ from mantidqt.utils.qt import load_ui from mantid import UsageService from mantid.kernel import FeatureType from sans.gui_logic.gui_common import (GENERIC_SETTINGS, JSON_SUFFIX, load_file) -from sans.state.state_base import ENUM_TYPE_TAG if PY3: unicode = str @@ -150,19 +148,9 @@ class SettingsDiagnosticTab(QtWidgets.QWidget, Ui_SettingsDiagnosticTab): item.addChild(child) else: child = QtWidgets.QTreeWidgetItem() - value = self.clean_class_type(value) child.setText(1, unicode(value)) item.addChild(child) - def clean_class_type(self, value): - # TODO the UI should not be doing logic like this - if isinstance(value, str) and ENUM_TYPE_TAG in value: - # Only the last element is of interest - split_values = value.split("#") - return split_values[-1] - else: - return value - def set_row(self, index): found_index = self.select_row_combo_box.findText(str(index)) if found_index and found_index != -1: diff --git a/scripts/SANS/sans/algorithm_detail/batch_execution.py b/scripts/SANS/sans/algorithm_detail/batch_execution.py index 12fd96e3dfc1cb9b7897ae9bafc41b4033fb2376..a7f3932de0bff005450855bb9981e3f540c06b65 100644 --- a/scripts/SANS/sans/algorithm_detail/batch_execution.py +++ b/scripts/SANS/sans/algorithm_detail/batch_execution.py @@ -22,6 +22,7 @@ from sans.common.constants import (TRANS_SUFFIX, SANS_SUFFIX, ALL_PERIODS, CAN_AND_SAMPLE_WORKSPACE) from sans.common.file_information import (get_extension_for_file_type, SANSFileInformationFactory) from sans.gui_logic.plotting import get_plotting_module +from sans.state.Serializer import Serializer from sans.state.data import StateData @@ -443,7 +444,7 @@ def provide_loaded_data(state, use_optimizations, workspace_to_name, workspace_t :return: a list fo workspaces and a list of monitor workspaces """ # Load the data - state_serialized = state.property_manager + state_serialized = Serializer.to_json(state) load_name = "SANSLoad" load_options = {"SANSState": state_serialized, "PublishToCache": use_optimizations, @@ -847,7 +848,7 @@ def set_properties_for_reduction_algorithm(reduction_alg, reduction_package, wor # Go through the elements of the reduction package and set them on the reduction algorithm # Set the SANSState state = reduction_package.state - state_dict = state.property_manager + state_dict = Serializer.to_json(state) reduction_alg.setProperty("SANSState", state_dict) # Set the input workspaces diff --git a/scripts/SANS/sans/algorithm_detail/calculate_sans_transmission.py b/scripts/SANS/sans/algorithm_detail/calculate_sans_transmission.py index b602f55919957c070c157e3032749325f0c1ba60..c4d8b79f23c5036069f05c769d724c71ac45165a 100644 --- a/scripts/SANS/sans/algorithm_detail/calculate_sans_transmission.py +++ b/scripts/SANS/sans/algorithm_detail/calculate_sans_transmission.py @@ -114,7 +114,7 @@ def _perform_fit(transmission_workspace, direct_workspace, raise RuntimeError("No transmission monitor has been provided.") # Get the fit setting for the correct data type, ie either for the Sample of the Can - fit_type = calculate_transmission_state.fit[data_type].fit_type + fit_type = calculate_transmission_state.fit[data_type.value].fit_type if fit_type is FitType.LOGARITHMIC: fit_string = "Log" elif fit_type is FitType.POLYNOMIAL: @@ -124,7 +124,7 @@ def _perform_fit(transmission_workspace, direct_workspace, trans_options.update({"FitMethod": fit_string}) if fit_type is FitType.POLYNOMIAL: - polynomial_order = calculate_transmission_state.fit[data_type].polynomial_order + polynomial_order = calculate_transmission_state.fit[data_type.value].polynomial_order trans_options.update({"PolynomialOrder": polynomial_order}) trans_alg = create_unmanaged_algorithm(trans_name, **trans_options) @@ -256,7 +256,7 @@ def _get_corrected_wavelength_workspace(workspace, detector_ids, calculate_trans wavelength_low = calculate_transmission_state.wavelength_full_range_low wavelength_high = calculate_transmission_state.wavelength_full_range_high else: - fit_state = calculate_transmission_state.fit[data_type] + fit_state = calculate_transmission_state.fit[data_type.value] wavelength_low = fit_state.wavelength_low if fit_state.wavelength_low \ else calculate_transmission_state.wavelength_low[0] wavelength_high = fit_state.wavelength_high if fit_state.wavelength_high \ diff --git a/scripts/SANS/sans/algorithm_detail/centre_finder_new.py b/scripts/SANS/sans/algorithm_detail/centre_finder_new.py index 0dc9d962beb0ffba6bf32aa8649428335418fc01..e4e1bc19daba04baa6c40ccd002c6c14022952f3 100644 --- a/scripts/SANS/sans/algorithm_detail/centre_finder_new.py +++ b/scripts/SANS/sans/algorithm_detail/centre_finder_new.py @@ -15,6 +15,9 @@ from mantid.simpleapi import CreateEmptyTableWorkspace # ---------------------------------------------------------------------------------------------------------------------- # Functions for the execution of a single batch iteration # ---------------------------------------------------------------------------------------------------------------------- +from sans.state.Serializer import Serializer + + def centre_finder_new(state, r_min = 0.06, r_max = 0.26, iterations = 10, position_1_start = 0.0, position_2_start = 0.0 , tolerance = 0.0001251, find_direction = FindDirectionEnum.ALL, verbose=False, component=DetectorType.LAB): """ @@ -153,8 +156,8 @@ def set_properties_for_beam_centre_algorithm(beam_centre_alg, reduction_package, # Go through the elements of the reduction package and set them on the beam centre algorithm # Set the SANSState state = reduction_package.state - state_dict = state.property_manager - beam_centre_alg.setProperty("SANSState", state_dict) + state_json = Serializer.to_json(state) + beam_centre_alg.setProperty("SANSState", state_json) # Set the input workspaces workspaces = reduction_package.workspaces diff --git a/scripts/SANS/sans/algorithm_detail/single_execution.py b/scripts/SANS/sans/algorithm_detail/single_execution.py index 6b389b67b75e5fc055db85f18da3511a29b34e8f..3e48361b465cb5b85b91e6d9ff9cc83de3c42d38 100644 --- a/scripts/SANS/sans/algorithm_detail/single_execution.py +++ b/scripts/SANS/sans/algorithm_detail/single_execution.py @@ -18,6 +18,7 @@ from sans.common.enums import (DetectorType, ReductionMode, OutputParts, Transmi from sans.common.general_functions import (create_child_algorithm, get_reduced_can_workspace_from_ads, get_transmission_workspaces_from_ads, write_hash_into_reduced_can_workspace) +from sans.state.Serializer import Serializer def run_initial_event_slice_reduction(reduction_alg, reduction_setting_bundle): @@ -32,7 +33,7 @@ def run_initial_event_slice_reduction(reduction_alg, reduction_setting_bundle): # Get component to reduce component = get_component_to_reduce(reduction_setting_bundle) # Set the properties on the reduction algorithms - serialized_state = reduction_setting_bundle.state.property_manager + serialized_state = Serializer.to_json(reduction_setting_bundle.state) reduction_alg.setProperty("SANSState", serialized_state) reduction_alg.setProperty("Component", component) reduction_alg.setProperty("ScatterWorkspace", reduction_setting_bundle.scatter_workspace) @@ -74,7 +75,7 @@ def run_core_event_slice_reduction(reduction_alg, reduction_setting_bundle): # Get component to reduce component = get_component_to_reduce(reduction_setting_bundle) # Set the properties on the reduction algorithms - serialized_state = reduction_setting_bundle.state.property_manager + serialized_state = Serializer.to_json(reduction_setting_bundle.state) reduction_alg.setProperty("SANSState", serialized_state) reduction_alg.setProperty("Component", component) reduction_alg.setProperty("ScatterWorkspace", reduction_setting_bundle.scatter_workspace) @@ -133,7 +134,7 @@ def run_core_reduction(reduction_alg, reduction_setting_bundle): # Get component to reduce component = get_component_to_reduce(reduction_setting_bundle) # Set the properties on the reduction algorithms - serialized_state = reduction_setting_bundle.state.property_manager + serialized_state = Serializer.to_json(reduction_setting_bundle.state) reduction_alg.setProperty("SANSState", serialized_state) reduction_alg.setProperty("Component", component) reduction_alg.setProperty("ScatterWorkspace", reduction_setting_bundle.scatter_workspace) diff --git a/scripts/SANS/sans/common/enums.py b/scripts/SANS/sans/common/enums.py index 7e690c32e482487415617e715c5749e89d253c0c..0e7a5bde4f7b9ea4de5953ce71e45154c8b118a9 100644 --- a/scripts/SANS/sans/common/enums.py +++ b/scripts/SANS/sans/common/enums.py @@ -8,8 +8,10 @@ from __future__ import (absolute_import, division, print_function) from mantid.py3compat import Enum +from sans.state.JsonSerializable import json_serializable +@json_serializable class SANSInstrument(Enum): NO_INSTRUMENT = "No Instrument" @@ -19,6 +21,7 @@ class SANSInstrument(Enum): ZOOM = "ZOOM" +@json_serializable class SANSFacility(Enum): NO_FACILITY = "No Facility" ISIS = "ISIS" @@ -38,12 +41,14 @@ class SANSDataType(Enum): SAMPLE_TRANSMISSION = "Sample Transmission" +@json_serializable class CanonicalCoordinates(Enum): X = "X" Y = "Y" Z = "Z" +@json_serializable class ReductionMode(Enum): NOT_SET = "Not Set" ALL = "All" @@ -52,6 +57,7 @@ class ReductionMode(Enum): LAB = "LAB" +@json_serializable class ReductionDimensionality(Enum): ONE_DIM = "OneDim" TWO_DIM = "TwoDim" @@ -83,6 +89,7 @@ class OutputParts(Enum): NORM = "Norm" +@json_serializable class FitModeForMerge(Enum): """ Defines which fit operation to use during the merge of two reductions. @@ -109,6 +116,7 @@ class TransmissionType(Enum): UNFITTED = "Unfitted" +@json_serializable class RangeStepType(Enum): """ Defines the step type of a range @@ -120,11 +128,13 @@ class RangeStepType(Enum): RANGE_LOG = "RangeLog" +@json_serializable class RebinType(Enum): INTERPOLATING_REBIN = "InterpolatingRebin" REBIN = "Rebin" +@json_serializable class SaveType(Enum): CAN_SAS = "CanSAS" CSV = "CSV" @@ -135,6 +145,7 @@ class SaveType(Enum): RKH = "RKH" +@json_serializable class FitType(Enum): """ Defines possible fit types for the transmission calculation @@ -145,6 +156,7 @@ class FitType(Enum): NO_FIT = "NotFit" +@json_serializable class SampleShape(Enum): """ Defines the sample shape types diff --git a/scripts/SANS/sans/common/file_information.py b/scripts/SANS/sans/common/file_information.py index 3a2bf92c1b89b564cda35387112acde509083bc7..6ca8eafe54add6467845137393dbcf7f3b270d3a 100644 --- a/scripts/SANS/sans/common/file_information.py +++ b/scripts/SANS/sans/common/file_information.py @@ -108,9 +108,7 @@ def find_sans_file(file_name): # TODO: If we only provide a run number for example 98843 for LOQ measurments, but have LARMOR specified as the # Mantid instrument, then the FileFinder will search itself to death. This is a general Mantid issue. # One way to handle this graceful would be a timeout option. - file_name_as_bytes = str.encode(file_name) - assert (type(file_name_as_bytes) == bytes) - runs = FileFinder.findRuns(file_name_as_bytes) + runs = FileFinder.findRuns(file_name) if runs: full_path = runs[0] except RuntimeError: diff --git a/scripts/SANS/sans/common/general_functions.py b/scripts/SANS/sans/common/general_functions.py index d79cf3335aadc083abb21b0300851a9fd36fe26f..e18f1687cf3b32e437581f5fe3c3151fc93c818c 100644 --- a/scripts/SANS/sans/common/general_functions.py +++ b/scripts/SANS/sans/common/general_functions.py @@ -26,6 +26,8 @@ from sans.common.enums import (DetectorType, RangeStepType, ReductionDimensional # ------------------------------------------- # Constants # ------------------------------------------- +from sans.state.Serializer import Serializer + ALTERNATIVE_SANS2D_NAME = "SAN" @@ -768,7 +770,7 @@ def get_transmission_output_name(state, data_type=DataType.SAMPLE, multi_reducti short_run_number_as_string = str(short_run_number) calculated_transmission_state = state.adjustment.calculate_transmission - fit = calculated_transmission_state.fit[DataType.SAMPLE] + fit = calculated_transmission_state.fit[DataType.SAMPLE.value] wavelength_range_string = "_" + str(fit.wavelength_low) + "_" + str(fit.wavelength_high) trans_suffix = "_trans_Sample" if data_type == DataType.SAMPLE else "_trans_Can" @@ -932,7 +934,7 @@ def get_state_hash_for_can_reduction(state, reduction_mode, partial_type=None): return state_to_hash new_state = remove_sample_related_information(state) - new_state_serialized = new_state.property_manager + new_state_serialized = Serializer.to_json(new_state) new_state_serialized = json.dumps(new_state_serialized, sort_keys=True, indent=4) # Add a tag for the reduction mode diff --git a/scripts/SANS/sans/gui_logic/presenter/masking_table_presenter.py b/scripts/SANS/sans/gui_logic/presenter/masking_table_presenter.py index d189039f697c311cd03593931a59c23bfc90e0a2..69f1620ed6d08319adfc1f88e01d5e94abc8f05e 100644 --- a/scripts/SANS/sans/gui_logic/presenter/masking_table_presenter.py +++ b/scripts/SANS/sans/gui_logic/presenter/masking_table_presenter.py @@ -15,6 +15,7 @@ from mantid.kernel import Logger from mantid.api import (AnalysisDataService) from sans.algorithm_detail.mask_sans_workspace import mask_workspace from sans.algorithm_detail.move_sans_instrument_component import move_component, MoveTypes +from sans.state.Serializer import Serializer from ui.sans_isis.masking_table import MaskingTable from sans.common.enums import DetectorType from sans.common.constants import EMPTY_NAME @@ -43,7 +44,7 @@ def load_workspace(state, workspace_name): prepare_to_load_scatter_sample_only(state) handle_multi_period_data(state) - serialized_state = state.property_manager + serialized_state = Serializer.to_json(state) workspace = perform_load(serialized_state) perform_move(state, workspace) diff --git a/scripts/SANS/sans/gui_logic/presenter/property_manager_service.py b/scripts/SANS/sans/gui_logic/presenter/property_manager_service.py deleted file mode 100644 index 17e1033c11c7248be37f7743646f5cb36a7c6bb8..0000000000000000000000000000000000000000 --- a/scripts/SANS/sans/gui_logic/presenter/property_manager_service.py +++ /dev/null @@ -1,95 +0,0 @@ -# Mantid Repository : https://github.com/mantidproject/mantid -# -# Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI, -# NScD Oak Ridge National Laboratory, European Spallation Source -# & Institut Laue - Langevin -# SPDX - License - Identifier: GPL - 3.0 + -""" The property manager service. - -The property manager service serializes a SANS state object into a PropertyManager object and places it on the - PropertyManagerDataService. It is also used to retrieve the state from this service. -""" - -from __future__ import (absolute_import, division, print_function) - -from mantid.kernel import (PropertyManagerDataService) - -from sans.state.state_base import create_deserialized_sans_state_from_property_manager - - -class PropertyManagerService(object): - sans_property_manager_prefix = "SANS_PROPERTY_MANAGER_THIS_NEEDS_TO_BE_UNIQUE_" - - def add_states_to_pmds(self, states): - # 1. Remove all property managers which belong to the sans property manager type - self.remove_sans_property_managers() - - # 2. Add all property managers - self._add_property_managers_to_pmds(states) - - def get_single_state_from_pmds(self, index_to_retrieve): - # 1. Find all sans state names - sans_property_managers = {} - for name in PropertyManagerDataService.getObjectNames(): - if name.startswith(self.sans_property_manager_prefix): - property_manager = PropertyManagerDataService.retrieve(name) - index = self._get_index_from_name(name) - sans_property_managers.update({index: property_manager}) - - # 2. Convert property managers to states - if index_to_retrieve not in list(sans_property_managers.keys()): - return [] - sans_property_manager = sans_property_managers[index_to_retrieve] - states_map = self._convert_property_manager_to_state({index_to_retrieve: sans_property_manager}) - - # 3. Create a sequence container - return self._get_states_list(states_map) - - def get_states_from_pmds(self): - # 1. Find all sans state names - sans_property_managers = {} - for name in PropertyManagerDataService.getObjectNames(): - if name.startswith(self.sans_property_manager_prefix): - property_manager = PropertyManagerDataService.retrieve(name) - index = self._get_index_from_name(name) - sans_property_managers.update({index: property_manager}) - - # 2. Convert property managers to states - states_map = self._convert_property_manager_to_state(sans_property_managers) - - # 3. Create a sequence container - return self._get_states_list(states_map) - - def remove_sans_property_managers(self): - property_manager_names_to_delete = [] - for name in PropertyManagerDataService.getObjectNames(): - if name.startswith(self.sans_property_manager_prefix): - property_manager_names_to_delete.append(name) - - for element in property_manager_names_to_delete: - PropertyManagerDataService.remove(element) - - def _add_property_managers_to_pmds(self, states): - for index, state in states.items(): - name = self.sans_property_manager_prefix + str(index) - PropertyManagerDataService.addOrReplace(name, state.property_manager) - - def _get_index_from_name(self, name): - return int(name.replace(self.sans_property_manager_prefix, "")) - - @staticmethod - def _convert_property_manager_to_state(property_managers): - states = {} - for key, property_manager in property_managers.items(): - state = create_deserialized_sans_state_from_property_manager(property_manager) - states.update({key: state}) - return states - - @staticmethod - def _get_states_list(states_map): - states = [] - indices = list(states_map.keys()) - indices.sort() - for index in indices: - states.append(states_map[index]) - return states diff --git a/scripts/SANS/sans/gui_logic/presenter/settings_diagnostic_presenter.py b/scripts/SANS/sans/gui_logic/presenter/settings_diagnostic_presenter.py index c9e108245152aba09f16ca25b6e6f401a553d65e..f1b55bf2bdacdaafe32af1680b89f3de6a660605 100644 --- a/scripts/SANS/sans/gui_logic/presenter/settings_diagnostic_presenter.py +++ b/scripts/SANS/sans/gui_logic/presenter/settings_diagnostic_presenter.py @@ -12,6 +12,7 @@ import os import json from mantid.kernel import Logger +from sans.state.Serializer import Serializer from ui.sans_isis.settings_diagnostic_tab import SettingsDiagnosticTab from sans.gui_logic.gui_common import JSON_SUFFIX @@ -101,9 +102,13 @@ class SettingsDiagnosticPresenter(object): def display_state_diagnostic_tree(self, state): # Convert to dict before passing the state to the view - if state is not None: - state = state.property_manager - self._view.set_tree(state) + dict_vals = None + + if state: + state = Serializer.to_json(state) + dict_vals = json.loads(state) # We intentionally do not use serializer to get a dict type back + + self._view.set_tree(dict_vals) def on_save_state(self): # Get the save location @@ -120,9 +125,7 @@ class SettingsDiagnosticPresenter(object): row_index = self._view.get_current_row() state = self.get_state(row_index) - serialized_state = state.property_manager - with open(full_file_path, 'w') as f: - json.dump(serialized_state, f, sort_keys=True, indent=4) + Serializer.save_file(state, full_file_path) self.gui_logger.information("The state for row {} has been saved to: {} ".format(row_index, full_file_path)) # Update the file name in the UI diff --git a/scripts/SANS/sans/gui_logic/sans_data_processor_gui_algorithm.py b/scripts/SANS/sans/gui_logic/sans_data_processor_gui_algorithm.py deleted file mode 100644 index ca2bd2ccfb63aa2e9456c7b3713d59e54774b260..0000000000000000000000000000000000000000 --- a/scripts/SANS/sans/gui_logic/sans_data_processor_gui_algorithm.py +++ /dev/null @@ -1,391 +0,0 @@ -# Mantid Repository : https://github.com/mantidproject/mantid -# -# Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI, -# NScD Oak Ridge National Laboratory, European Spallation Source -# & Institut Laue - Langevin -# SPDX - License - Identifier: GPL - 3.0 + -from mantid.kernel import (Direction, Property) -from mantid.api import (DataProcessorAlgorithm, AlgorithmFactory, MatrixWorkspaceProperty, PropertyMode) -from sans.common.enums import (SANSFacility, OutputMode) -from collections import namedtuple -from sans.gui_logic.presenter.property_manager_service import PropertyManagerService -from sans.sans_batch import SANSBatchReduction - -# ---------------------------------------------------------------------------------------------------------------------- -# Globals -# ---------------------------------------------------------------------------------------------------------------------- -SANS_DUMMY_INPUT_ALGORITHM_PROPERTY_NAME = '__sans_dummy_gui_workspace' -SANS_DUMMY_OUTPUT_ALGORITHM_PROPERTY_NAME = '__sans_dummy_gui_workspace' - - -# ---------------------------------------------------------------------------------------------------------------------- -# Set up the white list and black list properties of the data algorithm -# ---------------------------------------------------------------------------------------------------------------------- -algorithm_list_entry = namedtuple('algorithm_list_entry', 'column_name, algorithm_property, description, ' - 'show_value, default, prefix, property_type') - - -def create_option_column_properties(): - """ - Adds a new property which is meant for the Options column. - - This column should correspond to features in our settings section. We need to parse the entries before the - runs are processed in order to account for the settings in the Options column in the state creation. - - Important note: If you add it here then you have to add it to the parsing logic, else nothing will happen with it. - The important bit to edit is in gui_state_director. There the set properties are parsed. - """ - props = [algorithm_list_entry(column_name="", - algorithm_property="WavelengthMin", - description='The min value of the wavelength when converting from TOF.', - show_value=True, - default='', - prefix='', - property_type=float), - algorithm_list_entry(column_name="", - algorithm_property="WavelengthMax", - description='The max value of the wavelength when converting from TOF.', - show_value=True, - default='', - prefix='', - property_type=float), - algorithm_list_entry(column_name="", - algorithm_property="EventSlices", - description='The event slices to reduce. The format is the same as for the event slices' - ' box in settings, however if a comma separated list is given ' - 'it must be enclosed in quotes', - show_value=True, - default='', - prefix='', - property_type=str) - ] - return props - - -def create_properties(show_periods=True): - if show_periods: - properties = [algorithm_list_entry(column_name="SampleScatter", - algorithm_property="SampleScatter", - description='The run number of the scatter sample', - show_value=False, - default='', - prefix='', - property_type=str), - algorithm_list_entry(column_name="ssp", - algorithm_property="SampleScatterPeriod", - description='The sample scatter period', - show_value=False, - default='', - prefix='', - property_type=str), - algorithm_list_entry(column_name="SampleTrans", - algorithm_property="SampleTransmission", - description='The run number of the transmission sample', - show_value=False, - default='', - prefix='', - property_type=str), - algorithm_list_entry(column_name="stp", - algorithm_property="SampleTransmissionPeriod", - description='The sample transmission period', - show_value=False, - default='', - prefix='', - property_type=str), - algorithm_list_entry(column_name="SampleDirect", - algorithm_property="SampleDirect", - description='The run number of the direct sample', - show_value=False, - default='', - prefix='', - property_type=str), - algorithm_list_entry(column_name="sdp", - algorithm_property="SampleDirectPeriod", - description='The sample direct period', - show_value=False, - default='', - prefix='', - property_type=str), - algorithm_list_entry(column_name="CanScatter", - algorithm_property="CanScatter", - description='The run number of the scatter can', - show_value=False, - default='', - prefix='', - property_type=str), - algorithm_list_entry(column_name="csp", - algorithm_property="CanScatterPeriod", - description='The can scatter period', - show_value=False, - default='', - prefix='', - property_type=str), - algorithm_list_entry(column_name="CanTrans", - algorithm_property="CanTransmission", - description='The run number of the transmission can', - show_value=False, - default='', - prefix='', - property_type=str), - algorithm_list_entry(column_name="ctp", - algorithm_property="CanTransmissionPeriod", - description='The can transmission period', - show_value=False, - default='', - prefix='', - property_type=str), - algorithm_list_entry(column_name="CanDirect", - algorithm_property="CanDirect", - description='The run number of the direct can', - show_value=False, - default='', - prefix='', - property_type=str), - algorithm_list_entry(column_name="cdp", - algorithm_property="CanDirectPeriod", - description='The can direct period', - show_value=False, - default='', - prefix='', - property_type=str), - algorithm_list_entry(column_name="", - algorithm_property="UseOptimizations", - description='If optimizations should be used.', - show_value=False, - default=False, - prefix='', - property_type=bool), - algorithm_list_entry(column_name="", - algorithm_property="PlotResults", - description='If results should be plotted.', - show_value=False, - default=False, - prefix='', - property_type=bool), - algorithm_list_entry(column_name="OutputName", - algorithm_property="OutputName", - description='An optional custom output workspace name.', - show_value=False, - default='', - prefix='', - property_type=str), - algorithm_list_entry(column_name="User File", - algorithm_property="UserFile", - description=('The user file to use, this will override GUI changes for this row.' - ' If left unspecified default will be used'), - show_value=False, - default="", - prefix='', - property_type=str), - algorithm_list_entry(column_name="Sample Thickness", - algorithm_property="SampleThickness", - description=('The sample thickness from the user file'), - show_value=False, - default='', - prefix='', - property_type=str), - algorithm_list_entry(column_name="", - algorithm_property="RowIndex", - description='The row index (which is automatically populated by the GUI)', - show_value=False, - default=Property.EMPTY_INT, - prefix='', - property_type=int), - algorithm_list_entry(column_name="", - algorithm_property="OutputMode", - description='The output mode.', - show_value=False, - default=OutputMode.PUBLISH_TO_ADS.value, - prefix='', - property_type=bool), - algorithm_list_entry(column_name="", - algorithm_property="OutputGraph", - description='The name of the graph to output to.', - show_value=False, - default='', - prefix='', - property_type=str) - ] - else: - properties = [algorithm_list_entry(column_name="SampleScatter", - algorithm_property="SampleScatter", - description='The run number of the scatter sample', - show_value=False, - default='', - prefix='', - property_type=str), - algorithm_list_entry(column_name="SampleTrans", - algorithm_property="SampleTransmission", - description='The run number of the transmission sample', - show_value=False, - default='', - prefix='', - property_type=str), - algorithm_list_entry(column_name="SampleDirect", - algorithm_property="SampleDirect", - description='The run number of the direct sample', - show_value=False, - default='', - prefix='', - property_type=str), - algorithm_list_entry(column_name="CanScatter", - algorithm_property="CanScatter", - description='The run number of the scatter can', - show_value=False, - default='', - prefix='', - property_type=str), - algorithm_list_entry(column_name="CanTrans", - algorithm_property="CanTransmission", - description='The run number of the transmission can', - show_value=False, - default='', - prefix='', - property_type=str), - algorithm_list_entry(column_name="CanDirect", - algorithm_property="CanDirect", - description='The run number of the direct can', - show_value=False, - default='', - prefix='', - property_type=str), - algorithm_list_entry(column_name="", - algorithm_property="UseOptimizations", - description='If optimizations should be used.', - show_value=False, - default=False, - prefix='', - property_type=bool), - algorithm_list_entry(column_name="", - algorithm_property="PlotResults", - description='If results should be plotted.', - show_value=False, - default=False, - prefix='', - property_type=bool), - algorithm_list_entry(column_name="OutputName", - algorithm_property="OutputName", - description='An optional custom output workspace name.', - show_value=False, - default='', - prefix='', - property_type=str), - algorithm_list_entry(column_name="User File", - algorithm_property="UserFile", - description=('The user file to use, this will override GUI changes for this row.' - ' If left unspecified default will be used'), - show_value=False, - default="", - prefix='', - property_type=str), - algorithm_list_entry(column_name="Sample Thickness", - algorithm_property="SampleThickness", - description=('The sample thickness from the user file'), - show_value=False, - default='', - prefix='', - property_type=str), - algorithm_list_entry(column_name="", - algorithm_property="RowIndex", - description='The row index (which is automatically populated by the GUI)', - show_value=False, - default=Property.EMPTY_INT, - prefix='', - property_type=int), - algorithm_list_entry(column_name="", - algorithm_property="OutputMode", - description='The output mode.', - show_value=False, - default=OutputMode.PUBLISH_TO_ADS.value, - prefix='', - property_type=bool), - algorithm_list_entry(column_name="", - algorithm_property="OutputGraph", - description='The name of the graph to output to.', - show_value=False, - default='', - prefix='', - property_type=str) - ] - return properties - - -def get_white_list(show_periods=True): - return create_properties(show_periods=show_periods) - - -def get_black_list(show_periods=True): - black_list = "InputWorkspace,OutputWorkspace," - properties = create_properties(show_periods=show_periods) - for prop in properties: - if not prop.show_value: - black_list += prop.algorithm_property - black_list += "," - return black_list - - -def get_gui_algorithm_name(facility): - if facility is SANSFacility.ISIS: - algorithm_name = "SANSGuiDataProcessorAlgorithm" - AlgorithmFactory.subscribe(SANSGuiDataProcessorAlgorithm) - else: - raise RuntimeError("The facility is currently not supported") - return algorithm_name - - -class SANSGuiDataProcessorAlgorithm(DataProcessorAlgorithm): - def category(self): - return 'SANS\\Gui' - - def summary(self): - return 'Dynamic SANS Gui algorithm.' - - def PyInit(self): - # ------------------------------------------------------------ - # Dummy workspace properties. - # ------------------------------------------------------------ - self.declareProperty(MatrixWorkspaceProperty("InputWorkspace", SANS_DUMMY_INPUT_ALGORITHM_PROPERTY_NAME, - optional=PropertyMode.Optional, direction=Direction.Input), - doc='The input workspace (which is not used)') - - self.declareProperty(MatrixWorkspaceProperty("OutputWorkspace", SANS_DUMMY_OUTPUT_ALGORITHM_PROPERTY_NAME, - optional=PropertyMode.Optional, direction=Direction.Output), - doc='The output workspace (which is not used)') - - # ------------------------------------------------------------ - # Create the properties - # ------------------------------------------------------------ - properties = create_properties() - for prop in properties: - self.declareProperty(prop.algorithm_property, defaultValue=prop.default, - direction=Direction.Input, doc=prop.description) - - # ------------------------------------------------------------ - # Add properties which will show up in the options column - # ------------------------------------------------------------ - properties = create_option_column_properties() - for prop in properties: - self.declareProperty(prop.algorithm_property, defaultValue=prop.default, - direction=Direction.Input, doc=prop.description) - - def PyExec(self): - # 1. Get the index of the batch reduction - index = self.getProperty("RowIndex").value - - if index == Property.EMPTY_INT: - return - - # 2. Get the state for the index from the PropertyManagerDataService - property_manager_service = PropertyManagerService() - state = property_manager_service.get_single_state_from_pmds(index_to_retrieve=index) - # 3. Get some global settings - use_optimizations = self.getProperty("UseOptimizations").value - output_mode_as_string = self.getProperty("OutputMode").value - output_mode = OutputMode(output_mode_as_string) - plot_results = self.getProperty('PlotResults').value - output_graph = self.getProperty('OutputGraph').value - - # 3. Run the sans_batch script - sans_batch = SANSBatchReduction() - sans_batch(states=state, use_optimizations=use_optimizations, output_mode=output_mode, plot_results=plot_results - , output_graph=output_graph) diff --git a/scripts/SANS/sans/state/JsonSerializable.py b/scripts/SANS/sans/state/JsonSerializable.py new file mode 100644 index 0000000000000000000000000000000000000000..453ae9c2dddee6e0f8b1e50c34204bf4712b9466 --- /dev/null +++ b/scripts/SANS/sans/state/JsonSerializable.py @@ -0,0 +1,75 @@ +# Mantid Repository : https://github.com/mantidproject/mantid +# +# Copyright © 2020 ISIS Rutherford Appleton Laboratory UKRI, +# NScD Oak Ridge National Laboratory, European Spallation Source +# & Institut Laue - Langevin +# SPDX - License - Identifier: GPL - 3.0 + +from __future__ import (absolute_import, division, print_function) + +from enum import Enum + + +def json_serializable(cls): # Decorator for enums + assert issubclass(cls, Enum) + JsonSerializable._register_enum_type(cls) + return cls + + +class JsonSerializable(type): + """ The fundamental base of the SANS State""" + _derived_types = {} + + __ENUM_TAG = "E#" + __TYPE_TAG = "T#" + + def __init__(cls, name, bases, dct): + cls._derived_types[cls._tag_type(cls)] = cls + super(JsonSerializable, cls).__init__(name, bases, dct) + + @staticmethod + def tag_type(incoming_type): + def check_in_dict(tag): + if tag not in JsonSerializable._derived_types: + raise RuntimeError("Trying to serialize enum {0} which is not registered with JsonSerializer" + "\nUse the add_json_support decorator on the enum".format(tag)) + return tag + + metaclass = type(incoming_type) + if issubclass(incoming_type, Enum): + return check_in_dict(JsonSerializable._tag_enum(incoming_type)) + + if issubclass(metaclass, JsonSerializable): + return check_in_dict(JsonSerializable._tag_type(incoming_type)) + + raise RuntimeError("Unknown type {0} passed to tag_type".format(incoming_type)) + + @staticmethod + def class_type_from_tag(tag): + return JsonSerializable._find_type(tag, JsonSerializable.__TYPE_TAG) + + @staticmethod + def _enum_type_from_tag(tag): + return JsonSerializable._find_type(tag, JsonSerializable.__ENUM_TAG) + + @staticmethod + def _find_type(tag, type_to_search_for): + if not tag.startswith(type_to_search_for): + return + + try: + return JsonSerializable._derived_types[tag] + except KeyError: + if tag.startswith(type_to_search_for): + raise RuntimeError("Trying to deserialize {0} which is not registered with JsonSerializer".format(tag)) + + @staticmethod + def _register_enum_type(e_type): + JsonSerializable._derived_types[JsonSerializable._tag_enum(e_type)] = e_type + + @staticmethod + def _tag_enum(t): + return JsonSerializable.__ENUM_TAG + t.__name__ + + @staticmethod + def _tag_type(t): + return JsonSerializable.__TYPE_TAG + t.__name__ diff --git a/scripts/SANS/sans/state/Serializer.py b/scripts/SANS/sans/state/Serializer.py new file mode 100644 index 0000000000000000000000000000000000000000..fa45bafa9d100a82a525849a9fcfedeb16f2bb1c --- /dev/null +++ b/scripts/SANS/sans/state/Serializer.py @@ -0,0 +1,78 @@ +# Mantid Repository : https://github.com/mantidproject/mantid +# +# Copyright © 2020 ISIS Rutherford Appleton Laboratory UKRI, +# NScD Oak Ridge National Laboratory, European Spallation Source +# & Institut Laue - Langevin +# SPDX - License - Identifier: GPL - 3.0 + +import json +from enum import Enum + +import six + +from sans.state.JsonSerializable import JsonSerializable + + +class Serializer(object): + @staticmethod + def to_json(obj): + return json.dumps(obj, cls=SerializerImpl) + + @staticmethod + def from_json(json_str): + assert isinstance(json_str, str) + return json.loads(json_str, object_hook=SerializerImpl.obj_hook) + + @staticmethod + def load_file(file_path): + with open(file_path, 'r') as f: + return json.load(f, object_hook=SerializerImpl.obj_hook) + + @staticmethod + def save_file(obj, file_path): + with open(file_path, 'w') as f: + json.dump(obj, f, cls=SerializerImpl, sort_keys=True, indent=4) + + +class SerializerImpl(json.JSONEncoder): + def default(self, o): + metaclass = type(type(o)) # Get class of o, then get metaclass of the class type + if issubclass(metaclass, JsonSerializable): + tag = JsonSerializable.tag_type(type(o)) + return {tag: o.__dict__} + + if isinstance(o, Enum): + tag = JsonSerializable.tag_type(type(o)) + return {tag: o.value} + + if isinstance(o, tuple) and hasattr(o, "_fields"): + raise ValueError("A NamedTuple was passed to the JSON encoder, this is not supported as" + " it will be deserialized to a list") + + return json.JSONEncoder.default(self, o) + + @staticmethod + def obj_hook(o): + for type_tag, internal_dict in o.items(): + assert isinstance(type_tag, six.string_types) + + cls_type = JsonSerializable.class_type_from_tag(type_tag) + if cls_type: + return SerializerImpl._reconstruct_class(cls_type, internal_dict) + + enum_type = JsonSerializable._enum_type_from_tag(type_tag) + if enum_type: + return SerializerImpl._reconstruct_enum(enum_type, internal_dict) + + return o + + @staticmethod + def _reconstruct_class(found_type, ordered_dict): + assert isinstance(found_type, type) + assert isinstance(ordered_dict, dict) + obj = found_type() + obj.__dict__ = ordered_dict + return obj + + @staticmethod + def _reconstruct_enum(found_type, val): + return found_type(val) diff --git a/scripts/SANS/sans/state/adjustment.py b/scripts/SANS/sans/state/adjustment.py index 6cece2c654a3f649696e4c09db26464d1ea9ad65..1d02c7a351909e82090e875b1bedbd901b07029c 100644 --- a/scripts/SANS/sans/state/adjustment.py +++ b/scripts/SANS/sans/state/adjustment.py @@ -9,30 +9,25 @@ """State describing the adjustment workspace creation of the SANS reduction.""" from __future__ import (absolute_import, division, print_function) -import json + import copy -from sans.state.state_base import (StateBase, TypedParameter, rename_descriptor_names, BoolParameter, - validator_sub_state) -from sans.state.calculate_transmission import StateCalculateTransmission -from sans.state.normalize_to_monitor import StateNormalizeToMonitor -from sans.state.wavelength_and_pixel_adjustment import StateWavelengthAndPixelAdjustment -from sans.state.automatic_setters import (automatic_setters) +import json + +from six import with_metaclass + from sans.common.enums import SANSFacility +from sans.state.JsonSerializable import JsonSerializable +from sans.state.automatic_setters import automatic_setters -# ---------------------------------------------------------------------------------------------------------------------- -# State -# ---------------------------------------------------------------------------------------------------------------------- -@rename_descriptor_names -class StateAdjustment(StateBase): - calculate_transmission = TypedParameter(StateCalculateTransmission, validator_sub_state) - normalize_to_monitor = TypedParameter(StateNormalizeToMonitor, validator_sub_state) - wavelength_and_pixel_adjustment = TypedParameter(StateWavelengthAndPixelAdjustment, validator_sub_state) - wide_angle_correction = BoolParameter() +class StateAdjustment(with_metaclass(JsonSerializable)): def __init__(self): super(StateAdjustment, self).__init__() - self.wide_angle_correction = False + self.calculate_transmission = None # : StateCalculateTransmission + self.normalize_to_monitor = None # : StateNormalizeToMonitor + self.wavelength_and_pixel_adjustment = None # : StateWavelengthAndPixelAdjustment + self.wide_angle_correction = False # : Bool def validate(self): is_invalid = {} diff --git a/scripts/SANS/sans/state/automatic_setters.py b/scripts/SANS/sans/state/automatic_setters.py index 78e6dccdbf1018e24f0d650636b3e3abf848b2bd..9f6557ca7eb43b3ceca854567c58882d099506c7 100644 --- a/scripts/SANS/sans/state/automatic_setters.py +++ b/scripts/SANS/sans/state/automatic_setters.py @@ -8,7 +8,6 @@ from __future__ import (absolute_import, division, print_function) from functools import (partial, wraps) import inspect -from sans.state.state_base import (TypedParameter, DictParameter) # ------------------------------------------------------------------------------------------------------------- # Automatic Setter functionality # This creates setters on a builder/director instance for parameters of a state object. @@ -43,7 +42,11 @@ def forwarding_setter(value, builder_instance, attribute_name_list): def update_the_method(builder_instance, new_methods, setter_name, attribute_name, attribute_name_list): setter_name_copy = list(setter_name) setter_name_copy.append(attribute_name) - method_name = "_".join(setter_name_copy) + try: + method_name = "_".join(setter_name_copy) + except TypeError as e: + # An enum is being used for a key - the dev needs to switch to a value rather than the enum type + raise TypeError("You are likely trying to use an enum as a dict key which is not supported.\n {0}".format(e)) attribute_name_list_copy = list(attribute_name_list) attribute_name_list_copy.append(attribute_name) @@ -55,8 +58,8 @@ def update_the_method(builder_instance, new_methods, setter_name, attribute_nam def get_all_typed_parameter_descriptors(instance): descriptor_types = {} - for descriptor_name, descriptor_object in inspect.getmembers(type(instance)): - if inspect.isdatadescriptor(descriptor_object) and isinstance(descriptor_object, TypedParameter): + for descriptor_name, descriptor_object in inspect.getmembers(instance): + if not descriptor_name.startswith('__'): descriptor_types.update({descriptor_name: descriptor_object}) return descriptor_types @@ -76,7 +79,7 @@ def create_automatic_setters_for_state(attribute_value, builder_instance, attrib # 1. A dictionary which is empty or None-> install a setter # 2. A dictionary containing elements -> for each element apply a recursion # 3. A regular attribute -> install the setter - if isinstance(value, DictParameter): + if isinstance(value, dict): dict_parameter_value = getattr(attribute_value, name) if dict_parameter_value is None or len(dict_parameter_value) == 0: update_the_method(builder_instance, new_methods, setter_name, name, attribute_name_list) diff --git a/scripts/SANS/sans/state/calculate_transmission.py b/scripts/SANS/sans/state/calculate_transmission.py index eb7911f07aeca46263d93ee05317085ea45ce87e..a3723ef43141a6575ce0966b01e6e29e583f1259 100644 --- a/scripts/SANS/sans/state/calculate_transmission.py +++ b/scripts/SANS/sans/state/calculate_transmission.py @@ -12,33 +12,25 @@ from __future__ import (absolute_import, division, print_function) import json import copy import abc -import six +from six import with_metaclass, itervalues, add_metaclass -from sans.state.state_base import (StateBase, rename_descriptor_names, PositiveIntegerParameter, BoolParameter, - PositiveFloatParameter, FloatParameter, DictParameter, - StringListParameter, PositiveFloatWithNoneParameter, PositiveFloatListParameter) +from sans.state.JsonSerializable import JsonSerializable from sans.common.enums import (RebinType, RangeStepType, FitType, DataType, SANSInstrument) from sans.common.configurations import Configurations +from sans.state.automatic_setters import automatic_setters from sans.state.state_functions import (is_pure_none_or_not_none, validation_message, is_not_none_and_first_larger_than_second, one_is_none) -from sans.state.automatic_setters import (automatic_setters) from sans.common.xml_parsing import get_named_elements_from_ipf_file -# ---------------------------------------------------------------------------------------------------------------------- -# State -# ---------------------------------------------------------------------------------------------------------------------- -@rename_descriptor_names -class StateTransmissionFit(StateBase): - fit_type = FitType.LOGARITHMIC - polynomial_order = PositiveIntegerParameter() - wavelength_low = PositiveFloatWithNoneParameter() - wavelength_high = PositiveFloatWithNoneParameter() +class StateTransmissionFit(with_metaclass(JsonSerializable)): def __init__(self): super(StateTransmissionFit, self).__init__() self.fit_type = FitType.LOGARITHMIC - self.polynomial_order = 0 + self.polynomial_order = 0 # : Int (Positive) + self.wavelength_low = None # : Float (Optional) + self.wavelength_high = None # : Float (Optional) def validate(self): is_invalid = {} @@ -67,65 +59,54 @@ class StateTransmissionFit(StateBase): "Please see: {0}".format(json.dumps(is_invalid))) -@rename_descriptor_names -class StateCalculateTransmission(StateBase): - # ----------------------- - # Transmission - # ----------------------- - transmission_radius_on_detector = PositiveFloatParameter() - transmission_roi_files = StringListParameter() - transmission_mask_files = StringListParameter() - - default_transmission_monitor = PositiveIntegerParameter() - transmission_monitor = PositiveIntegerParameter() - - default_incident_monitor = PositiveIntegerParameter() - incident_monitor = PositiveIntegerParameter() - - # ---------------------- - # Prompt peak correction - # ---------------------- - prompt_peak_correction_min = PositiveFloatParameter() - prompt_peak_correction_max = PositiveFloatParameter() - prompt_peak_correction_enabled = BoolParameter() - - # ---------------- - # Wavelength rebin - # ---------------- - wavelength_low = PositiveFloatListParameter() - wavelength_high = PositiveFloatListParameter() - wavelength_step = PositiveFloatParameter() - rebin_type = RebinType.REBIN - wavelength_step_type = RangeStepType.NOT_SET - - use_full_wavelength_range = BoolParameter() - wavelength_full_range_low = PositiveFloatParameter() - wavelength_full_range_high = PositiveFloatParameter() - - # ----------------------- - # Background correction - # ---------------------- - background_TOF_general_start = FloatParameter() - background_TOF_general_stop = FloatParameter() - background_TOF_monitor_start = DictParameter() - background_TOF_monitor_stop = DictParameter() - background_TOF_roi_start = FloatParameter() - background_TOF_roi_stop = FloatParameter() - - fit = {DataType.CAN : StateTransmissionFit(), - DataType.SAMPLE : StateTransmissionFit()} - +class StateCalculateTransmission(with_metaclass(JsonSerializable)): def __init__(self): super(StateCalculateTransmission, self).__init__() - # The keys of this dictionaries are the spectrum number of the monitors (as a string) - self.background_TOF_monitor_start = {} - self.background_TOF_monitor_stop = {} - self.use_full_wavelength_range = False + # ----------------------- + # Transmission + # ----------------------- + self.transmission_radius_on_detector = None # : Float (Positive) + self.transmission_roi_files = None # : List[Str] + self.transmission_mask_files = None # : List[Str] + + self.default_transmission_monitor = None # : Int (Positive) + self.transmission_monitor = None # : Int (Positive) - # Default rebin type is a standard Rebin + self.default_incident_monitor = None # : Int (Positive) + self.incident_monitor = None # : Int (Positive) + + # ---------------------- + # Prompt peak correction + # ---------------------- + self.prompt_peak_correction_min = None # : Float (Positive) + self.prompt_peak_correction_max = None # : Float (Positive) + self.prompt_peak_correction_enabled = False # : Bool + + # ---------------- + # Wavelength rebin + # ---------------- + self.wavelength_low = None # : List[Float] (Positive) + self.wavelength_high = None # : List[Float] (Positive) + self.wavelength_step = None # : Float (Positive) self.rebin_type = RebinType.REBIN + self.wavelength_step_type = RangeStepType.NOT_SET + + self.use_full_wavelength_range = False # : Bool + self.wavelength_full_range_low = None # : Float (Positive) + self.wavelength_full_range_high = None # : Float (Positive) + + # ----------------------- + # Background correction + # ---------------------- + self.background_TOF_general_start = None # : Float + self.background_TOF_general_stop = None # : Float + self.background_TOF_monitor_start = {} # : Dict + self.background_TOF_monitor_stop = {} # : Dict + self.background_TOF_roi_start = None # : Float + self.background_TOF_roi_stop = None # : Float - self.prompt_peak_correction_enabled = False + self.fit = {DataType.CAN.value: StateTransmissionFit(), + DataType.SAMPLE.value: StateTransmissionFit()} def validate(self): # noqa is_invalid = {} @@ -142,9 +123,9 @@ class StateCalculateTransmission(StateBase): # -------------- # Transmission, either we need some ROI (ie radius, roi files /mask files) or a transmission monitor # -------------- - has_no_transmission_monitor_setting = self.transmission_monitor is None and\ + has_no_transmission_monitor_setting = self.transmission_monitor is None and \ self.default_transmission_monitor is None # noqa - has_no_transmission_roi_setting = self.transmission_radius_on_detector is None and\ + has_no_transmission_roi_setting = self.transmission_radius_on_detector is None and \ self.transmission_roi_files is None # noqa if has_no_transmission_monitor_setting and has_no_transmission_roi_setting: entry = validation_message("No transmission settings were specified.", @@ -188,7 +169,7 @@ class StateCalculateTransmission(StateBase): if self.wavelength_step_type is RangeStepType.NOT_SET: entry = validation_message("A wavelength entry has not been set.", "Make sure that all entries are set.", - {"wavelength_step_type" : self.wavelength_step_type}) + {"wavelength_step_type": self.wavelength_step_type}) is_invalid.update(entry) if is_not_none_and_first_larger_than_second([self.wavelength_low, self.wavelength_high]): @@ -276,7 +257,7 @@ class StateCalculateTransmission(StateBase): "background_TOF_monitor_stop": self.background_TOF_monitor_stop}) is_invalid.update(entry) - for fit_type in six.itervalues(self.fit): + for fit_type in itervalues(self.fit): fit_type.validate() if is_invalid: @@ -359,7 +340,7 @@ def set_default_monitors(calculate_transmission_info, data_info): # --------------------------------------- # State builders # --------------------------------------- -@six.add_metaclass(abc.ABCMeta) +@add_metaclass(abc.ABCMeta) class StateCalculateTransmissionBuilderCommon(object): def __init__(self, state): self.state = state @@ -371,28 +352,28 @@ class StateCalculateTransmissionBuilderCommon(object): self.state.rebin_type = val def set_can_fit_type(self, val): - self.state.fit[DataType.CAN].fit_type = val + self.state.fit[DataType.CAN.value].fit_type = val def set_can_polynomial_order(self, val): - self.state.fit[DataType.CAN].polynomial_order = val + self.state.fit[DataType.CAN.value].polynomial_order = val def set_can_wavelength_low(self, val): - self.state.fit[DataType.CAN].wavelength_low = val + self.state.fit[DataType.CAN.value].wavelength_low = val def set_can_wavelength_high(self, val): - self.state.fit[DataType.CAN].wavelength_high = val + self.state.fit[DataType.CAN.value].wavelength_high = val def set_sample_fit_type(self, val): - self.state.fit[DataType.SAMPLE].fit_type = val + self.state.fit[DataType.SAMPLE.value].fit_type = val def set_sample_polynomial_order(self, val): - self.state.fit[DataType.SAMPLE].polynomial_order = val + self.state.fit[DataType.SAMPLE.value].polynomial_order = val def set_sample_wavelength_low(self, val): - self.state.fit[DataType.SAMPLE].wavelength_low = val + self.state.fit[DataType.SAMPLE.value].wavelength_low = val def set_sample_wavelength_high(self, val): - self.state.fit[DataType.SAMPLE].wavelength_high = val + self.state.fit[DataType.SAMPLE.value].wavelength_high = val class StateCalculateTransmissionBuilderLOQ(StateCalculateTransmissionBuilderCommon): diff --git a/scripts/SANS/sans/state/compatibility.py b/scripts/SANS/sans/state/compatibility.py index 6316b81bb76c7bda2d45248c7bd41c085c1b1f7c..846279dd5d3530f20b0975a8bce1c95ea8e92b42 100644 --- a/scripts/SANS/sans/state/compatibility.py +++ b/scripts/SANS/sans/state/compatibility.py @@ -14,25 +14,26 @@ from __future__ import (absolute_import, division, print_function) import copy -from sans.state.state_base import (StateBase, rename_descriptor_names, BoolParameter, StringParameter) -from sans.state.automatic_setters import (automatic_setters) + +from six import with_metaclass + +from sans.state.JsonSerializable import JsonSerializable + from sans.common.enums import SANSFacility # ---------------------------------------------------------------------------------------------------------------------- # State # ---------------------------------------------------------------------------------------------------------------------- -@rename_descriptor_names -class StateCompatibility(StateBase): - use_compatibility_mode = BoolParameter() - time_rebin_string = StringParameter() - use_event_slice_optimisation = BoolParameter() +from sans.state.automatic_setters import automatic_setters + +class StateCompatibility(with_metaclass(JsonSerializable)): def __init__(self): super(StateCompatibility, self).__init__() - self.use_compatibility_mode = False - self.use_event_slice_optimisation = False - self.time_rebin_string = "" + self.use_compatibility_mode = False # : Bool + self.use_event_slice_optimisation = False # : Bool + self.time_rebin_string = "" # Str def validate(self): pass diff --git a/scripts/SANS/sans/state/convert_to_q.py b/scripts/SANS/sans/state/convert_to_q.py index 75cdf108f85db6074cd62882a1836c55837d2726..8b61b484188efc3252a9fc29feae6d4e5f3d8ad6 100644 --- a/scripts/SANS/sans/state/convert_to_q.py +++ b/scripts/SANS/sans/state/convert_to_q.py @@ -11,61 +11,57 @@ from __future__ import (absolute_import, division, print_function) import json import copy -from sans.state.state_base import (StateBase, rename_descriptor_names, BoolParameter, PositiveFloatParameter, - StringParameter) + +from six import with_metaclass + +from sans.state.JsonSerializable import JsonSerializable from sans.common.enums import (ReductionDimensionality, RangeStepType, SANSFacility) +from sans.state.automatic_setters import automatic_setters from sans.state.state_functions import (is_pure_none_or_not_none, is_not_none_and_first_larger_than_second, validation_message) -from sans.state.automatic_setters import (automatic_setters) # ---------------------------------------------------------------------------------------------------------------------- # State # ---------------------------------------------------------------------------------------------------------------------- -@rename_descriptor_names -class StateConvertToQ(StateBase): - reduction_dimensionality = ReductionDimensionality.ONE_DIM - use_gravity = BoolParameter() - gravity_extra_length = PositiveFloatParameter() - radius_cutoff = PositiveFloatParameter() - wavelength_cutoff = PositiveFloatParameter() - - # 1D settings - q_min = PositiveFloatParameter() - q_max = PositiveFloatParameter() - q_1d_rebin_string = StringParameter() - - # 2D settings - q_xy_max = PositiveFloatParameter() - q_xy_step = PositiveFloatParameter() - q_xy_step_type = RangeStepType.LIN - - # ----------------------- - # Q Resolution specific - # --------------------- - use_q_resolution = BoolParameter() - q_resolution_collimation_length = PositiveFloatParameter() - q_resolution_delta_r = PositiveFloatParameter() - moderator_file = StringParameter() - - # Circular aperture settings - q_resolution_a1 = PositiveFloatParameter() - q_resolution_a2 = PositiveFloatParameter() - - # Rectangular aperture settings - q_resolution_h1 = PositiveFloatParameter() - q_resolution_h2 = PositiveFloatParameter() - q_resolution_w1 = PositiveFloatParameter() - q_resolution_w2 = PositiveFloatParameter() + +class StateConvertToQ(with_metaclass(JsonSerializable)): def __init__(self): super(StateConvertToQ, self).__init__() self.reduction_dimensionality = ReductionDimensionality.ONE_DIM - self.use_gravity = False - self.gravity_extra_length = 0.0 - self.use_q_resolution = False - self.radius_cutoff = 0.0 - self.wavelength_cutoff = 0.0 + self.use_gravity = False # : Bool + self.gravity_extra_length = 0.0 # : Float (Positive) + self.radius_cutoff = 0.0 # : Float (Positive) + self.wavelength_cutoff = 0.0 # : Float (Positive) + + # 1D settings + self.q_min = None # : Float (Positive) + self.q_max = None # : Float (Positive) + self.q_1d_rebin_string = None # : Str() + + # 2D settings + self.q_xy_max = None # : Float (Positive) + self.q_xy_step = None # : Float (Positive) + self.q_xy_step_type = RangeStepType.LIN + + # ----------------------- + # Q Resolution specific + # --------------------- + self.use_q_resolution = False # : Bool + self.q_resolution_collimation_length = None # : Float (Positive) + self.q_resolution_delta_r = None # : Float (Positive) + self.moderator_file = None # : Str() + + # Circular aperture settings + self.q_resolution_a1 = None # : Float (Positive) + self.q_resolution_a2 = None # : Float (Positive) + + # Rectangular aperture settings + self.q_resolution_h1 = None # : Float (Positive) + self.q_resolution_h2 = None # : Float (Positive) + self.q_resolution_w1 = None # : Float (Positive) + self.q_resolution_w2 = None # : Float (Positive) def validate(self): is_invalid = {} diff --git a/scripts/SANS/sans/state/data.py b/scripts/SANS/sans/state/data.py index 4a62bab729d14a6a77e95c1f2c54c148aebab029..03204871fd8ebdeb3a9c84eec3fbf6100e6f3138 100644 --- a/scripts/SANS/sans/state/data.py +++ b/scripts/SANS/sans/state/data.py @@ -11,62 +11,51 @@ from __future__ import (absolute_import, division, print_function) import json import copy -from sans.state.state_base import (StateBase, StringParameter, PositiveIntegerParameter, BoolParameter, - rename_descriptor_names) +from six import with_metaclass + +from sans.state.JsonSerializable import JsonSerializable from sans.common.enums import SANSFacility, SANSInstrument import sans.common.constants -from sans.state.state_functions import (is_pure_none_or_not_none, validation_message) from sans.state.automatic_setters import automatic_setters +from sans.state.state_functions import (is_pure_none_or_not_none, validation_message) # ---------------------------------------------------------------------------------------------------------------------- # State # ---------------------------------------------------------------------------------------------------------------------- -@rename_descriptor_names -class StateData(StateBase): + +class StateData(with_metaclass(JsonSerializable)): ALL_PERIODS = sans.common.constants.ALL_PERIODS - sample_scatter = StringParameter() - sample_scatter_period = PositiveIntegerParameter() - sample_transmission = StringParameter() - sample_transmission_period = PositiveIntegerParameter() - sample_direct = StringParameter() - sample_direct_period = PositiveIntegerParameter() - - can_scatter = StringParameter() - can_scatter_period = PositiveIntegerParameter() - can_transmission = StringParameter() - can_transmission_period = PositiveIntegerParameter() - can_direct = StringParameter() - can_direct_period = PositiveIntegerParameter() - - calibration = StringParameter() - - sample_scatter_run_number = PositiveIntegerParameter() - sample_scatter_is_multi_period = BoolParameter() - idf_file_path = StringParameter() - ipf_file_path = StringParameter() - user_file = StringParameter() - - instrument = SANSInstrument.NO_INSTRUMENT - facility = SANSFacility.NO_FACILITY def __init__(self): super(StateData, self).__init__() - # Setup default values for periods - self.sample_scatter_period = StateData.ALL_PERIODS - self.sample_transmission_period = StateData.ALL_PERIODS - self.sample_direct_period = StateData.ALL_PERIODS + self.sample_scatter = None # : Str() + self.sample_scatter_period = StateData.ALL_PERIODS # : Int (Positive) + self.sample_transmission = None # : Str() + self.sample_transmission_period = StateData.ALL_PERIODS # : Int (Positive) + self.sample_direct = None # : Str() + self.sample_direct_period = StateData.ALL_PERIODS # : Int (Positive) + + self.can_scatter = None # : Str() + self.can_scatter_period = StateData.ALL_PERIODS # : Int (Positive) + self.can_transmission = None # : Str() + self.can_transmission_period = StateData.ALL_PERIODS # : Int (Positive) + self.can_direct = None # : Str() + self.can_direct_period = StateData.ALL_PERIODS # : Int (Positive) + + self.calibration = None # : Str() - self.can_scatter_period = StateData.ALL_PERIODS - self.can_transmission_period = StateData.ALL_PERIODS - self.can_direct_period = StateData.ALL_PERIODS + self.sample_scatter_run_number = None # : Int (Positive) + self.sample_scatter_is_multi_period = None # : Bool + self.idf_file_path = None # : Str() + self.ipf_file_path = None # : Str() + self.user_file = "" # : Str() # This should be reset by the builder. Setting this to NoInstrument ensure that we will trip early on, # in case this is not set, for example by not using the builders. self.instrument = SANSInstrument.NO_INSTRUMENT self.facility = SANSFacility.NO_FACILITY - self.user_file = "" def validate(self): is_invalid = dict() diff --git a/scripts/SANS/sans/state/mask.py b/scripts/SANS/sans/state/mask.py index 3711f52926f11f5ae43e84ed4ecb1e91f4d94c63..c1bfc8453f87e067f88803dcf7a26b5791465175 100644 --- a/scripts/SANS/sans/state/mask.py +++ b/scripts/SANS/sans/state/mask.py @@ -11,11 +11,13 @@ from __future__ import (absolute_import, division, print_function) import json import copy -from sans.state.state_base import (StateBase, BoolParameter, StringListParameter, StringParameter, - PositiveFloatParameter, FloatParameter, FloatListParameter, FloatWithNoneParameter, - DictParameter, PositiveIntegerListParameter, rename_descriptor_names) + +from six import with_metaclass + +from sans.state.JsonSerializable import JsonSerializable +from sans.state.automatic_setters import automatic_setters from sans.state.state_functions import (is_pure_none_or_not_none, validation_message, set_detector_names) -from sans.state.automatic_setters import (automatic_setters) + from sans.common.file_information import find_full_file_path from sans.common.enums import (DetectorType, SANSInstrument) from sans.common.general_functions import get_bank_for_spectrum_number @@ -80,45 +82,44 @@ def is_spectrum_range_all_on_one_detector(start, stop, invalid_dict, start_name, # ------------------------------------------------ # StateData # ------------------------------------------------ -@rename_descriptor_names -class StateMaskDetector(StateBase): - # Vertical strip masks - single_vertical_strip_mask = PositiveIntegerListParameter() - range_vertical_strip_start = PositiveIntegerListParameter() - range_vertical_strip_stop = PositiveIntegerListParameter() - - # Horizontal strip masks - single_horizontal_strip_mask = PositiveIntegerListParameter() - range_horizontal_strip_start = PositiveIntegerListParameter() - range_horizontal_strip_stop = PositiveIntegerListParameter() - - # Spectrum Block - block_horizontal_start = PositiveIntegerListParameter() - block_horizontal_stop = PositiveIntegerListParameter() - block_vertical_start = PositiveIntegerListParameter() - block_vertical_stop = PositiveIntegerListParameter() - - # Spectrum block cross - block_cross_horizontal = PositiveIntegerListParameter() - block_cross_vertical = PositiveIntegerListParameter() - - # Time/Bin mask - bin_mask_start = FloatListParameter() - bin_mask_stop = FloatListParameter() - - # Name of the detector - detector_name = StringParameter() - detector_name_short = StringParameter() - - # Single Spectra - single_spectra = PositiveIntegerListParameter() - - # Spectrum Range - spectrum_range_start = PositiveIntegerListParameter() - spectrum_range_stop = PositiveIntegerListParameter() +class StateMaskDetector(with_metaclass(JsonSerializable)): def __init__(self): super(StateMaskDetector, self).__init__() + # Vertical strip masks + self.single_vertical_strip_mask = None # : List[Int] (Positive) + self.range_vertical_strip_start = None # : List[Int] (Positive) + self.range_vertical_strip_stop = None # : List[Int] (Positive) + + # Horizontal strip masks + self.single_horizontal_strip_mask = None # : List[Int] (Positive) + self.range_horizontal_strip_start = None # : List[Int] (Positive) + self.range_horizontal_strip_stop = None # : List[Int] (Positive) + + # Spectrum Block + self.block_horizontal_start = None # : List[Int] (Positive) + self.block_horizontal_stop = None # : List[Int] (Positive) + self.block_vertical_start = None # : List[Int] (Positive) + self.block_vertical_stop = None # : List[Int] (Positive) + + # Spectrum block cross + self.block_cross_horizontal = None # : List[Int] (Positive) + self.block_cross_vertical = None # : List[Int] (Positive) + + # Time/Bin mask + self.bin_mask_start = None # : List[Float] + self.bin_mask_stop = None # : List[Float] + + # Name of the detector + self.detector_name = None # : Str() + self.detector_name_short = None # : Str() + + # Single Spectra + self.single_spectra = None # : List[Int] (Positive) + + # Spectrum Range + self.spectrum_range_start = None # : List[Int] (Positive) + self.spectrum_range_stop = None # : List[Int] (Positive) def validate(self): is_invalid = {} @@ -170,47 +171,40 @@ class StateMaskDetector(StateBase): "Please see: {0}".format(json.dumps(is_invalid))) -@rename_descriptor_names -class StateMask(StateBase): - # Radius Mask - radius_min = FloatParameter() - radius_max = FloatParameter() - - # Bin mask - bin_mask_general_start = FloatListParameter() - bin_mask_general_stop = FloatListParameter() +class StateMask(with_metaclass(JsonSerializable)): + def __init__(self): + super(StateMask, self).__init__() + # Radius Mask + self.radius_min = None # : Float + self.radius_max = None # : Float - # Mask files - mask_files = StringListParameter() + # Bin mask + self.bin_mask_general_start = None # : List[Float] + self.bin_mask_general_stop = None # : List[Float] - # Angle masking - phi_min = FloatWithNoneParameter() - phi_max = FloatWithNoneParameter() - use_mask_phi_mirror = BoolParameter() + # Mask files + self.mask_files = None # : List[Str] - # Beam stop - beam_stop_arm_width = PositiveFloatParameter() - beam_stop_arm_angle = FloatParameter() - beam_stop_arm_pos1 = FloatParameter() - beam_stop_arm_pos2 = FloatParameter() + # Angle masking + self.phi_min = -90.0 + self.phi_max = 90.0 + self.use_mask_phi_mirror = True - # Clear commands - clear = BoolParameter() - clear_time = BoolParameter() + # Beam stop + self.beam_stop_arm_width = None # : Float (Positive) + self.beam_stop_arm_angle = None # : Float + self.beam_stop_arm_pos1 = None # : Float + self.beam_stop_arm_pos2 = None # : Float - # The detector dependent masks - detectors = DictParameter() + # Clear commands + self.clear = None # : Bool + self.clear_time = None # : Bool - # The idf path of the instrument - idf_path = StringParameter() + # The detector dependent masks + self.detectors = None # : Dict - def __init__(self): - super(StateMask, self).__init__() - # IDF Path + # The idf path of the instrument self.idf_path = "" - self.phi_min = -90.0 - self.phi_max = 90.0 - self.use_mask_phi_mirror = True def validate(self): is_invalid = dict() @@ -257,7 +251,6 @@ class StateMask(StateBase): "Please see: {0}".format(json.dumps(is_invalid))) -@rename_descriptor_names class StateMaskSANS2D(StateMask): def __init__(self): super(StateMaskSANS2D, self).__init__() @@ -269,7 +262,6 @@ class StateMaskSANS2D(StateMask): super(StateMaskSANS2D, self).validate() -@rename_descriptor_names class StateMaskLOQ(StateMask): def __init__(self): super(StateMaskLOQ, self).__init__() @@ -281,7 +273,6 @@ class StateMaskLOQ(StateMask): super(StateMaskLOQ, self).validate() -@rename_descriptor_names class StateMaskLARMOR(StateMask): def __init__(self): super(StateMaskLARMOR, self).__init__() @@ -292,7 +283,6 @@ class StateMaskLARMOR(StateMask): super(StateMaskLARMOR, self).validate() -@rename_descriptor_names class StateMaskZOOM(StateMask): def __init__(self): super(StateMaskZOOM, self).__init__() diff --git a/scripts/SANS/sans/state/move.py b/scripts/SANS/sans/state/move.py index 322517940278aca711bfc65a142e42c00bf7fe5d..0836f5ed6c598af830a599500b1b54f927b02754 100644 --- a/scripts/SANS/sans/state/move.py +++ b/scripts/SANS/sans/state/move.py @@ -13,55 +13,41 @@ from __future__ import (absolute_import, division, print_function) import copy import json +from six import with_metaclass + from sans.common.enums import (CanonicalCoordinates, SANSInstrument, DetectorType) +from sans.state.JsonSerializable import JsonSerializable from sans.state.automatic_setters import automatic_setters -from sans.state.state_base import (StateBase, FloatParameter, DictParameter, - StringWithNoneParameter, rename_descriptor_names) from sans.state.state_functions import (validation_message, set_detector_names, set_monitor_names) # ---------------------------------------------------------------------------------------------------------------------- # State # ---------------------------------------------------------------------------------------------------------------------- -@rename_descriptor_names -class StateMoveDetector(StateBase): - x_translation_correction = FloatParameter() - y_translation_correction = FloatParameter() - z_translation_correction = FloatParameter() - - rotation_correction = FloatParameter() - side_correction = FloatParameter() - radius_correction = FloatParameter() - - x_tilt_correction = FloatParameter() - y_tilt_correction = FloatParameter() - z_tilt_correction = FloatParameter() - - sample_centre_pos1 = FloatParameter() - sample_centre_pos2 = FloatParameter() - - # Name of the detector - detector_name = StringWithNoneParameter() - detector_name_short = StringWithNoneParameter() +class StateMoveDetector(with_metaclass(JsonSerializable)): def __init__(self): super(StateMoveDetector, self).__init__() # Translation correction - self.x_translation_correction = 0.0 - self.y_translation_correction = 0.0 - self.z_translation_correction = 0.0 + self.x_translation_correction = 0.0 # : Float + self.y_translation_correction = 0.0 # : Float + self.z_translation_correction = 0.0 # : Float - self.rotation_correction = 0.0 - self.side_correction = 0.0 - self.radius_correction = 0.0 + self.rotation_correction = 0.0 # : Float + self.side_correction = 0.0 # : Float + self.radius_correction = 0.0 # : Float - self.x_tilt_correction = 0.0 - self.y_tilt_correction = 0.0 - self.z_tilt_correction = 0.0 + self.x_tilt_correction = 0.0 # : Float + self.y_tilt_correction = 0.0 # : Float + self.z_tilt_correction = 0.0 # : Float # Sample centre Pos 1 + Pos 2 - self.sample_centre_pos1 = 0.0 - self.sample_centre_pos2 = 0.0 + self.sample_centre_pos1 = 0.0 # : Float + self.sample_centre_pos2 = 0.0 # : Float + + # Name of the detector + self.detector_name = None # : Str + self.detector_name_short = None # : Str def validate(self): is_invalid = {} @@ -80,19 +66,13 @@ class StateMoveDetector(StateBase): "Please see: {0}".format(json.dumps(is_invalid))) -@rename_descriptor_names -class StateMove(StateBase): - sample_offset = FloatParameter() - detectors = DictParameter() - monitor_names = DictParameter() - - sample_offset_direction = CanonicalCoordinates.Z - +class StateMove(with_metaclass(JsonSerializable)): def __init__(self): super(StateMove, self).__init__() - # Setup the sample offset - self.sample_offset = 0.0 + self.sample_offset = 0.0 # : Float + self.detectors = None # : Dict + self.monitor_names = None # : Dict # The sample offset direction is Z for the ISIS instruments self.sample_offset_direction = CanonicalCoordinates.Z @@ -107,14 +87,11 @@ class StateMove(StateBase): self.detectors[key].validate() -@rename_descriptor_names class StateMoveLOQ(StateMove): - center_position = FloatParameter() - def __init__(self): super(StateMoveLOQ, self).__init__() # Set the center_position in meter - self.center_position = 317.5 / 1000. + self.center_position = 317.5 / 1000. # : Float # Set the monitor names self.monitor_names = {} @@ -128,42 +105,25 @@ class StateMoveLOQ(StateMove): super(StateMoveLOQ, self).validate() -@rename_descriptor_names class StateMoveSANS2D(StateMove): - hab_detector_radius = FloatParameter() - hab_detector_default_sd_m = FloatParameter() - hab_detector_default_x_m = FloatParameter() - - lab_detector_default_sd_m = FloatParameter() - - hab_detector_x = FloatParameter() - hab_detector_z = FloatParameter() - - hab_detector_rotation = FloatParameter() - - lab_detector_x = FloatParameter() - lab_detector_z = FloatParameter() - - monitor_4_offset = FloatParameter() - def __init__(self): super(StateMoveSANS2D, self).__init__() # Set the descriptors which corresponds to information which we gain through the IPF - self.hab_detector_radius = 306.0 / 1000. - self.hab_detector_default_sd_m = 4.0 - self.hab_detector_default_x_m = 1.1 - self.lab_detector_default_sd_m = 4.0 + self.hab_detector_radius = 306.0 / 1000. # : Float + self.hab_detector_default_sd_m = 4.0 # : Float + self.hab_detector_default_x_m = 1.1 # : Float + self.lab_detector_default_sd_m = 4.0 # : Float # The actual values are found on the workspace and should be used from there. This is only a fall back. - self.hab_detector_x = 0.0 - self.hab_detector_z = 0.0 - self.hab_detector_rotation = 0.0 - self.lab_detector_x = 0.0 - self.lab_detector_z = 0.0 + self.hab_detector_x = 0.0 # : Float + self.hab_detector_z = 0.0 # : Float + self.hab_detector_rotation = 0.0 # : Float + self.lab_detector_x = 0.0 # : Float + self.lab_detector_z = 0.0 # : Float # Set the monitor names self.monitor_names = {} - self.monitor_4_offset = 0.0 + self.monitor_4_offset = 0.0 # : Float # Setup the detectors self.detectors = {DetectorType.LAB.value: StateMoveDetector(), @@ -173,15 +133,12 @@ class StateMoveSANS2D(StateMove): super(StateMoveSANS2D, self).validate() -@rename_descriptor_names class StateMoveLARMOR(StateMove): - bench_rotation = FloatParameter() - def __init__(self): super(StateMoveLARMOR, self).__init__() # Set a default for the bench rotation - self.bench_rotation = 0.0 + self.bench_rotation = 0.0 # : Float # Set the monitor names self.monitor_names = {} @@ -193,22 +150,16 @@ class StateMoveLARMOR(StateMove): super(StateMoveLARMOR, self).validate() -@rename_descriptor_names class StateMoveZOOM(StateMove): - - lab_detector_default_sd_m = FloatParameter() - monitor_4_offset = FloatParameter() - monitor_5_offset = FloatParameter() - def __init__(self): super(StateMoveZOOM, self).__init__() - self.lab_detector_default_sd_m = 0.0 + self.lab_detector_default_sd_m = 0.0 # : Float # Set the monitor names self.monitor_names = {} - self.monitor_4_offset = 0.0 - self.monitor_5_offset = 0.0 + self.monitor_4_offset = 0.0 # : Float + self.monitor_5_offset = 0.0 # : Float # Setup the detectors self.detectors = {DetectorType.LAB.value: StateMoveDetector()} diff --git a/scripts/SANS/sans/state/normalize_to_monitor.py b/scripts/SANS/sans/state/normalize_to_monitor.py index 9e0a68c1dac5b026b166654a6b07dab778b351fd..69f19f39bea4cfe33b4e0dbdf16df8077f72df8b 100644 --- a/scripts/SANS/sans/state/normalize_to_monitor.py +++ b/scripts/SANS/sans/state/normalize_to_monitor.py @@ -11,46 +11,36 @@ from __future__ import (absolute_import, division, print_function) import json import copy -from sans.state.state_base import (StateBase, rename_descriptor_names, PositiveIntegerParameter, - PositiveFloatParameter, FloatParameter, DictParameter, - PositiveFloatWithNoneParameter, BoolParameter, PositiveFloatListParameter) -from sans.state.automatic_setters import (automatic_setters) + +from six import with_metaclass + +from sans.state.JsonSerializable import JsonSerializable from sans.common.enums import (RebinType, RangeStepType, SANSInstrument) +from sans.state.automatic_setters import automatic_setters from sans.state.state_functions import (is_pure_none_or_not_none, is_not_none_and_first_larger_than_second, one_is_none, validation_message) from sans.common.xml_parsing import get_named_elements_from_ipf_file -# ---------------------------------------------------------------------------------------------------------------------- -# State -# ---------------------------------------------------------------------------------------------------------------------- -@rename_descriptor_names -class StateNormalizeToMonitor(StateBase): - prompt_peak_correction_min = PositiveFloatWithNoneParameter() - prompt_peak_correction_max = PositiveFloatWithNoneParameter() - prompt_peak_correction_enabled = BoolParameter() - - rebin_type = RebinType.REBIN - wavelength_low = PositiveFloatListParameter() - wavelength_high = PositiveFloatListParameter() - wavelength_step = PositiveFloatParameter() - wavelength_step_type = RangeStepType.NOT_SET - - background_TOF_general_start = FloatParameter() - background_TOF_general_stop = FloatParameter() - background_TOF_monitor_start = DictParameter() - background_TOF_monitor_stop = DictParameter() - - incident_monitor = PositiveIntegerParameter() - +class StateNormalizeToMonitor(with_metaclass(JsonSerializable)): def __init__(self): super(StateNormalizeToMonitor, self).__init__() - self.background_TOF_monitor_start = {} - self.background_TOF_monitor_stop = {} - self.prompt_peak_correction_enabled = False + self.prompt_peak_correction_min = None # : Float (Optional) + self.prompt_peak_correction_max = None # : Float (Optional) + self.prompt_peak_correction_enabled = False # : Bool - # Default rebin type is a standard Rebin self.rebin_type = RebinType.REBIN + self.wavelength_low = None # : List[Float] (Positive) + self.wavelength_high = None # : List[Float] (Positive) + self.wavelength_step = None # : Float (Positive) + self.wavelength_step_type = RangeStepType.NOT_SET + + self.background_TOF_general_start = None # : Float + self.background_TOF_general_stop = None # : Float + self.background_TOF_monitor_start = {} # : Dict + self.background_TOF_monitor_stop = {} # : Dict + + self.incident_monitor = None # : Int (Positive) def validate(self): is_invalid = {} @@ -150,7 +140,6 @@ class StateNormalizeToMonitor(StateBase): "Please see: {0}".format(json.dumps(is_invalid))) -@rename_descriptor_names class StateNormalizeToMonitorLOQ(StateNormalizeToMonitor): def __init__(self): super(StateNormalizeToMonitorLOQ, self).__init__() diff --git a/scripts/SANS/sans/state/reduction_mode.py b/scripts/SANS/sans/state/reduction_mode.py index 6a98e407841cde44f688a95e7cbcb81ff4c9c2c2..eff1637ae0c3e1f8b9d573a7f6cecf0d20f4d1dc 100644 --- a/scripts/SANS/sans/state/reduction_mode.py +++ b/scripts/SANS/sans/state/reduction_mode.py @@ -11,68 +11,31 @@ from __future__ import (absolute_import, division, print_function) import copy import json -from abc import (ABCMeta, abstractmethod) from six import (with_metaclass) from sans.common.enums import (ReductionMode, ReductionDimensionality, FitModeForMerge, SANSFacility, DetectorType) from sans.common.xml_parsing import get_named_elements_from_ipf_file -from sans.state.automatic_setters import (automatic_setters) -from sans.state.state_base import (StateBase, FloatParameter, DictParameter, - FloatWithNoneParameter, rename_descriptor_names, BoolParameter) +from sans.state.JsonSerializable import JsonSerializable +from sans.state.automatic_setters import automatic_setters -# ---------------------------------------------------------------------------------------------------------------------- -# State -# ---------------------------------------------------------------------------------------------------------------------- -class StateReductionBase(with_metaclass(ABCMeta, object)): - @abstractmethod - def get_merge_strategy(self): - pass - - @abstractmethod - def get_detector_name_for_reduction_mode(self, reduction_mode): - pass - - @abstractmethod - def get_all_reduction_modes(self): - pass - - -@rename_descriptor_names -class StateReductionMode(StateReductionBase, StateBase): - reduction_mode = ReductionMode.NOT_SET - - reduction_dimensionality = ReductionDimensionality.ONE_DIM - merge_max = FloatWithNoneParameter() - merge_min = FloatWithNoneParameter() - merge_mask = BoolParameter() - - # Fitting - merge_fit_mode = FitModeForMerge.NO_FIT - merge_shift = FloatParameter() - merge_scale = FloatParameter() - merge_range_min = FloatWithNoneParameter() - merge_range_max = FloatWithNoneParameter() - - # Map from detector type to detector name - detector_names = DictParameter() - +class StateReductionMode(with_metaclass(JsonSerializable)): def __init__(self): super(StateReductionMode, self).__init__() self.reduction_mode = ReductionMode.LAB self.reduction_dimensionality = ReductionDimensionality.ONE_DIM # Set the shifts to defaults which essentially don't do anything. - self.merge_shift = 0.0 - self.merge_scale = 1.0 + self.merge_shift = 0.0 # : Float + self.merge_scale = 1.0 # : Float self.merge_fit_mode = FitModeForMerge.NO_FIT - self.merge_range_min = None - self.merge_range_max = None - self.merge_max = None - self.merge_min = None - self.merge_mask = False + self.merge_range_min = None # : Float + self.merge_range_max = None # : Float + self.merge_max = None # : Float + self.merge_min = None # : Float + self.merge_mask = False # : Bool # Set the detector names to empty strings self.detector_names = {DetectorType.LAB.value: "", diff --git a/scripts/SANS/sans/state/save.py b/scripts/SANS/sans/state/save.py index c97334e799a9c892b446817571fdc1ba1a9764ef..12e880fde19def88fa62250a4cf43ed633b27cec 100644 --- a/scripts/SANS/sans/state/save.py +++ b/scripts/SANS/sans/state/save.py @@ -10,28 +10,24 @@ from __future__ import (absolute_import, division, print_function) import copy -from sans.state.state_base import (StateBase, BoolParameter, StringParameter, StringWithNoneParameter, - rename_descriptor_names) -from sans.common.enums import (SaveType, SANSFacility) -from sans.state.automatic_setters import (automatic_setters) +from six import with_metaclass -# ---------------------------------------------------------------------------------------------------------------------- -# State -# ---------------------------------------------------------------------------------------------------------------------- -@rename_descriptor_names -class StateSave(StateBase): - zero_free_correction = BoolParameter() - file_format = SaveType.NO_TYPE +from sans.state.JsonSerializable import JsonSerializable +from sans.common.enums import (SaveType, SANSFacility) +from sans.state.automatic_setters import automatic_setters - # Settings for the output name - user_specified_output_name = StringWithNoneParameter() - user_specified_output_name_suffix = StringParameter() - use_reduction_mode_as_suffix = BoolParameter() +class StateSave(with_metaclass(JsonSerializable)): def __init__(self): super(StateSave, self).__init__() - self.zero_free_correction = True + self.zero_free_correction = True # : Bool + self.file_format = SaveType.NO_TYPE + + # Settings for the output name + self.user_specified_output_name = None # : Str + self.user_specified_output_name_suffix = None # : Str() + self.use_reduction_mode_as_suffix = None # : Bool def validate(self): pass diff --git a/scripts/SANS/sans/state/scale.py b/scripts/SANS/sans/state/scale.py index fb191a4449da2eb7f29814e9826ec9330e0635d8..c0bf8ad877683ca7ab5d82a132fcbb03416c2537 100644 --- a/scripts/SANS/sans/state/scale.py +++ b/scripts/SANS/sans/state/scale.py @@ -8,35 +8,37 @@ from __future__ import (absolute_import, division, print_function) import copy -from sans.state.state_base import (StateBase, rename_descriptor_names, PositiveFloatParameter) -from sans.common.enums import (SampleShape, SANSFacility) -from sans.state.automatic_setters import (automatic_setters) + +from six import with_metaclass + +from sans.state.JsonSerializable import JsonSerializable +from sans.common.enums import SampleShape, SANSFacility # ---------------------------------------------------------------------------------------------------------------------- # State # ---------------------------------------------------------------------------------------------------------------------- -@rename_descriptor_names -class StateScale(StateBase): - shape = None +from sans.state.automatic_setters import automatic_setters - thickness = PositiveFloatParameter() - width = PositiveFloatParameter() - height = PositiveFloatParameter() - scale = PositiveFloatParameter() - # Geometry from the file - shape_from_file = SampleShape.DISC - thickness_from_file = PositiveFloatParameter() - width_from_file = PositiveFloatParameter() - height_from_file = PositiveFloatParameter() +class StateScale(with_metaclass(JsonSerializable)): def __init__(self): super(StateScale, self).__init__() + self.shape = None + + self.thickness = None # : Float (Positive) + self.width = None # : Float (Positive) + self.height = None # : Float (Positive) + self.scale = None # : Float (Positive) + + # Geometry from the file + self.shape_from_file = SampleShape.DISC + # The default values are 1mm - self.thickness_from_file = 1. - self.width_from_file = 1. - self.height_from_file = 1. + self.thickness_from_file = 1. # : Float (Positive) + self.width_from_file = 1. # : Float (Positive) + self.height_from_file = 1. # : Float (Positive) def validate(self): pass @@ -54,7 +56,7 @@ def set_geometry_from_file(state, file_information): class StateScaleBuilder(object): - @automatic_setters(StateScale, exclusions=[]) + @automatic_setters(StateScale) def __init__(self, file_information): super(StateScaleBuilder, self).__init__() self.state = StateScale() diff --git a/scripts/SANS/sans/state/slice_event.py b/scripts/SANS/sans/state/slice_event.py index 91f3c059f515c5bbc67c734533a206e6b8d62ba2..b740b8b66d7d01e51480052ed2944a16d7e153ef 100644 --- a/scripts/SANS/sans/state/slice_event.py +++ b/scripts/SANS/sans/state/slice_event.py @@ -9,23 +9,26 @@ from __future__ import (absolute_import, division, print_function) import json import copy -from sans.state.state_base import (StateBase, rename_descriptor_names, FloatListParameter) + +from six import with_metaclass + +from sans.state.JsonSerializable import JsonSerializable +from sans.state.automatic_setters import automatic_setters from sans.state.state_functions import (is_pure_none_or_not_none, validation_message) from sans.common.enums import SANSFacility -from sans.state.automatic_setters import (automatic_setters) # ---------------------------------------------------------------------------------------------------------------------- # State # ---------------------------------------------------------------------------------------------------------------------- -@rename_descriptor_names -class StateSliceEvent(StateBase): - start_time = FloatListParameter() - end_time = FloatListParameter() +class StateSliceEvent(with_metaclass(JsonSerializable)): def __init__(self): super(StateSliceEvent, self).__init__() + self.start_time = None # : List[Float] + self.end_time = None # : List[Float] + def validate(self): is_invalid = dict() diff --git a/scripts/SANS/sans/state/state.py b/scripts/SANS/sans/state/state.py index 132f6c8a8ad34c6ed359af51bff7d699541bc030..68448e4b5f6286c83cbdb6186a5c4c4e94381855 100644 --- a/scripts/SANS/sans/state/state.py +++ b/scripts/SANS/sans/state/state.py @@ -5,53 +5,41 @@ # & Institut Laue - Langevin # SPDX - License - Identifier: GPL - 3.0 + """ Defines the main State object.""" - -# pylint: disable=too-few-public-methods - from __future__ import (absolute_import, division, print_function) -import json -import pickle -import inspect + import copy -from sans.common.enums import SANSFacility -from sans.state.state_base import (StateBase, TypedParameter, - rename_descriptor_names, validator_sub_state) -from sans.state.data import StateData -from sans.state.move import StateMove -from sans.state.reduction_mode import StateReductionMode -from sans.state.slice_event import StateSliceEvent -from sans.state.mask import StateMask -from sans.state.wavelength import StateWavelength -from sans.state.save import StateSave -from sans.state.adjustment import StateAdjustment -from sans.state.scale import StateScale -from sans.state.convert_to_q import StateConvertToQ -from sans.state.automatic_setters import (automatic_setters) +import json + +from six import with_metaclass +from sans.common.enums import SANSFacility +from sans.state.JsonSerializable import JsonSerializable # Note that the compatibility state is not part of the new reduction chain, but allows us to accurately compare # results obtained via the old and new reduction chain -from sans.state.compatibility import (StateCompatibility, get_compatibility_builder) +from sans.state.automatic_setters import automatic_setters +from sans.state.compatibility import get_compatibility_builder # ---------------------------------------------------------------------------------------------------------------------- # State # ---------------------------------------------------------------------------------------------------------------------- -@rename_descriptor_names -class State(StateBase): - data = TypedParameter(StateData, validator_sub_state) - move = TypedParameter(StateMove, validator_sub_state) - reduction = TypedParameter(StateReductionMode, validator_sub_state) - slice = TypedParameter(StateSliceEvent, validator_sub_state) - mask = TypedParameter(StateMask, validator_sub_state) - wavelength = TypedParameter(StateWavelength, validator_sub_state) - save = TypedParameter(StateSave, validator_sub_state) - scale = TypedParameter(StateScale, validator_sub_state) - adjustment = TypedParameter(StateAdjustment, validator_sub_state) - convert_to_q = TypedParameter(StateConvertToQ, validator_sub_state) - compatibility = TypedParameter(StateCompatibility, validator_sub_state) + +class State(with_metaclass(JsonSerializable)): def __init__(self): + super(State, self).__init__() + self.data = None # : StateData + self.move = None # : StateMove + self.reduction = None # : StateReductionMode + self.slice = None # : StateSliceEvent + self.mask = None # : StateMask + self.wavelength = None # : StateWavelength + self.save = None # : StateSave + self.scale = None # : StateScale + self.adjustment = None # : StateAdjustment + self.convert_to_q = None # : StateConvertToQ + self.compatibility = None # : StateCompatibility def validate(self): is_invalid = dict() @@ -84,25 +72,9 @@ class State(StateBase): self.compatibility = get_compatibility_builder(self.data).build() if is_invalid: - raise ValueError("State: There is an issue with your in put. See: {0}".format(json.dumps(is_invalid))) - - # Check the attributes themselves - is_invalid = {} - for descriptor_name, descriptor_object in inspect.getmembers(type(self)): - if inspect.isdatadescriptor(descriptor_object) and isinstance(descriptor_object, TypedParameter): - try: - attr = getattr(self, descriptor_name) - attr.validate() - except ValueError as err: - is_invalid.update({descriptor_name: pickle.dumps(str(err))}) + raise ValueError("State: There is an issue with your input. See: {0}".format(json.dumps(is_invalid))) - if is_invalid: - raise ValueError("State: There is an issue with your in put. See: {0}".format(json.dumps(is_invalid))) - -# ---------------------------------------------------------------------------------------------------------------------- -# Builder -# ---------------------------------------------------------------------------------------------------------------------- class StateBuilder(object): @automatic_setters(State) def __init__(self): diff --git a/scripts/SANS/sans/state/state_base.py b/scripts/SANS/sans/state/state_base.py deleted file mode 100644 index d4d01039b740727ac6c3f13e47e5dcd017149ff0..0000000000000000000000000000000000000000 --- a/scripts/SANS/sans/state/state_base.py +++ /dev/null @@ -1,601 +0,0 @@ -# Mantid Repository : https://github.com/mantidproject/mantid -# -# Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI, -# NScD Oak Ridge National Laboratory, European Spallation Source -# & Institut Laue - Langevin -# SPDX - License - Identifier: GPL - 3.0 + -# pylint: disable=too-few-public-methods, invalid-name - -""" Fundamental classes and Descriptors for the State mechanism.""" -from __future__ import (absolute_import, division, print_function) - -import copy -import inspect -from abc import (ABCMeta, abstractmethod) -from functools import (partial) -from importlib import import_module - -from six import string_types, with_metaclass - -from mantid.kernel import (PropertyManager, std_vector_dbl, std_vector_str, std_vector_int, std_vector_long) -from mantid.py3compat import Enum - - -# --------------------------------------------------------------- -# Validator functions -# --------------------------------------------------------------- - - -def is_not_none(value): - return value is not None - - -def is_positive(value): - return value >= 0 - - -def is_positive_or_none(value): - return value is None or value >= 0 - - -def all_list_elements_are_of_specific_type_and_not_empty(value, comparison_type, - additional_comparison=lambda x: True, type_check=isinstance): - """ - Ensures that all elements of a list are of a specific type and that the list is not empty - - :param value: the list to check - :param comparison_type: the expected type of the elements of the list. - :param additional_comparison: additional comparison lambda. - :param type_check: the method which performs type checking. - :return: True if the list is not empty and all types are as expected, else False. - """ - is_of_type = True - for element in value: - # Perform type check - if not type_check(element, comparison_type): - is_of_type = False - # Perform additional check - if not additional_comparison(element): - is_of_type = False - - if not value: - is_of_type = False - return is_of_type - - -def all_list_elements_are_of_instance_type_and_not_empty(value, comparison_type, additional_comparison=lambda x: True): - """ - Ensures that all elements of a list are of a certain INSTANCE type and that the list is not empty. - """ - return all_list_elements_are_of_specific_type_and_not_empty(value=value, comparison_type=comparison_type, - additional_comparison=additional_comparison, - type_check=isinstance) - - -def all_list_elements_are_of_class_type_and_not_empty(value, comparison_type, additional_comparison=lambda x: True): - """ - Ensures that all elements of a list are of a certain INSTANCE type and that the list is not empty. - """ - return all_list_elements_are_of_specific_type_and_not_empty(value=value, comparison_type=comparison_type, - additional_comparison=additional_comparison, - type_check=issubclass) - - -def all_list_elements_are_float_and_not_empty(value): - typed_comparison = partial(all_list_elements_are_of_instance_type_and_not_empty, comparison_type=float) - return typed_comparison(value) - - -def all_list_elements_are_float_and_positive_and_not_empty(value): - typed_comparison = partial(all_list_elements_are_of_instance_type_and_not_empty, comparison_type=float, - additional_comparison=lambda x: x >= 0) - return typed_comparison(value) - - -def all_list_elements_are_string_and_not_empty(value): - typed_comparison = partial(all_list_elements_are_of_instance_type_and_not_empty, comparison_type=str) - return typed_comparison(value) - - -def all_list_elements_are_int_and_not_empty(value): - typed_comparison = partial(all_list_elements_are_of_instance_type_and_not_empty, comparison_type=int) - return typed_comparison(value) - - -def all_list_elements_are_int_and_positive_and_not_empty(value): - typed_comparison = partial(all_list_elements_are_of_instance_type_and_not_empty, comparison_type=int, - additional_comparison=lambda x: x >= 0) - return typed_comparison(value) - - -def validator_sub_state(sub_state): - is_valid = True - try: - sub_state.validate() - except ValueError: - is_valid = False - return is_valid - - -# ------------------------------------------------------- -# Parameters -# ------------------------------------------------------- -class TypedParameter(object): - """ - The TypedParameter descriptor allows the user to store/handle a type-checked value with an additional - validator option, e.g. one can restrict the held parameter to be only a positive value. - """ - __counter = 0 - - def __init__(self, parameter_type, validator=lambda x: True): - cls = self.__class__ - prefix = cls.__name__ - # pylint: disable=protected-access - index = cls.__counter - cls.__counter += 1 - # Name which is used to store value in the instance. This will be unique and not accessible via the standard - # attribute access, since the developer/user cannot apply the hash symbol in their code (it is valid though - # when writing into the __dict__). Note that the name which we generate here will be altered (via a - # class decorator) in the classes which actually use the TypedParameter descriptor, to make it more readable. - self.name = '_{0}#{1}'.format(prefix, index) - self.parameter_type = parameter_type - self.value = None - self.validator = validator - - def __get__(self, instance, owner): - if instance is None: - return self - else: - if hasattr(instance, self.name): - return getattr(instance, self.name) - else: - return None - - def __set__(self, instance, value): - # Perform a type check - self._type_check(value) - if self.validator(value): - # The descriptor should be holding onto its own data and return a deepcopy of the data. - copied_value = copy.deepcopy(value) - setattr(instance, self.name, copied_value) - else: - raise ValueError("Trying to set {0} with an invalid value of {1}".format(self.name, str(value))) - - def __delete__(self): - raise AttributeError("Cannot delete the attribute {0}".format(self.name)) - - def _type_check(self, value): - if not isinstance(value, self.parameter_type): - raise TypeError("Trying to set {0} which expects a value of type {1}." - " Got a value of {2} which is of type: {3}".format(self.name, str(self.parameter_type), - str(value), str(type(value)))) - - -# --------------------------------------------------- -# Various standard cases of the TypedParameter -# --------------------------------------------------- -class StringParameter(TypedParameter): - def __init__(self): - super(StringParameter, self).__init__(str, is_not_none) - - -class BoolParameter(TypedParameter): - def __init__(self): - super(BoolParameter, self).__init__(bool, is_not_none) - - -class FloatParameter(TypedParameter): - def __init__(self): - super(FloatParameter, self).__init__(float, is_not_none) - - -class PositiveFloatParameter(TypedParameter): - def __init__(self): - super(PositiveFloatParameter, self).__init__(float, is_positive) - - -class PositiveIntegerParameter(TypedParameter): - def __init__(self): - super(PositiveIntegerParameter, self).__init__(int, is_positive) - - -class DictParameter(TypedParameter): - def __init__(self): - super(DictParameter, self).__init__(dict, is_not_none) - - -class DictFloatsParameter(TypedParameter): - def __init__(self): - super(DictFloatsParameter, self).__init__(dict, is_not_none) - - def _type_check(self, value): - if not all(isinstance(val, float) for val in value.values()): - raise TypeError("Trying to set {0} which expects a value of type {1}." - " Got a value of {2} which is of type: {3}".format(self.name, self.parameter_type, - value, type(value))) - - -class FloatWithNoneParameter(TypedParameter): - def __init__(self): - super(FloatWithNoneParameter, self).__init__(float) - - def _type_check(self, value): - if not isinstance(value, self.parameter_type) and value is not None: - raise TypeError("Trying to set {0} which expects a value of type {1}." - " Got a value of {2} which is of type: {3}".format(self.name, self.parameter_type, - value, type(value))) - - -class StringWithNoneParameter(TypedParameter): - def __init__(self): - super(StringWithNoneParameter, self).__init__(str) - - def _type_check(self, value): - if not isinstance(value, self.parameter_type) and value is not None: - raise TypeError("Trying to set {0} which expects a value of type {1}." - " Got a value of {2} which is of type: {3}".format(self.name, self.parameter_type, - value, type(value))) - - -class PositiveFloatWithNoneParameter(TypedParameter): - def __init__(self): - super(PositiveFloatWithNoneParameter, self).__init__(float, is_positive_or_none) - - def _type_check(self, value): - if not isinstance(value, self.parameter_type) and value is not None: - raise TypeError("Trying to set {0} which expects a value of type {1}." - " Got a value of {2} which is of type: {3}".format(self.name, self.parameter_type, - value, type(value))) - - -class FloatListParameter(TypedParameter): - def __init__(self): - super(FloatListParameter, self).__init__(list) - - def _type_check(self, value): - if not isinstance(value, self.parameter_type) or not all_list_elements_are_float_and_not_empty(value): - raise TypeError("Trying to set {0} which expects a value of type {1}." - " Got a value of {2} which is of type: {3}".format(self.name, self.parameter_type, - value, type(value))) - - -class PositiveFloatListParameter(TypedParameter): - def __init__(self): - super(PositiveFloatListParameter, self).__init__(list, all_list_elements_are_float_and_positive_and_not_empty) - - def _type_check(self, value): - if not isinstance(value, self.parameter_type) or not all_list_elements_are_float_and_not_empty(value): - raise TypeError("Trying to set {0} which expects a value of type {1}." - " Got a value of {2} which is of type: {3}".format(self.name, self.parameter_type, - value, type(value))) - - -class StringListParameter(TypedParameter): - def __init__(self): - super(StringListParameter, self).__init__(list, all_list_elements_are_string_and_not_empty) - - def _type_check(self, value): - if not isinstance(value, self.parameter_type) or not all_list_elements_are_string_and_not_empty(value): - raise TypeError("Trying to set {0} which expects a value of type {1}." - " Got a value of {2} which is of type: {3}".format(self.name, self.parameter_type, - value, type(value))) - - -class PositiveIntegerListParameter(TypedParameter): - def __init__(self): - super(PositiveIntegerListParameter, self).__init__(list, - all_list_elements_are_int_and_positive_and_not_empty) - - def _type_check(self, value): - if not isinstance(value, self.parameter_type) or not all_list_elements_are_int_and_not_empty(value): - raise TypeError("Trying to set {0} which expects a value of type {1}." - " Got a value of {2} which is of type: {3}".format(self.name, self.parameter_type, - value, type(value))) - - -# ------------------------------------------------ -# StateBase -# ------------------------------------------------ -class StateBase(with_metaclass(ABCMeta, object)): - """ The fundamental base of the SANS State""" - - @property - def property_manager(self): - return convert_state_to_dict(self) - - @property_manager.setter - def property_manager(self, value): - set_state_from_property_manager(self, value) - - @abstractmethod - def validate(self): - pass - - def convert_to_dict(self): - return convert_state_to_dict(self) - - -def rename_descriptor_names(cls): - """ - Class decorator which changes the names of TypedParameters in a class instance in order to make it more readable. - - This is especially helpful for debugging. And also in order to find attributes in the dictionaries. - :param cls: The class with the TypedParameters - :return: The class with the TypedParameters - """ - for attribute_name, attribute_value in list(cls.__dict__.items()): - if isinstance(attribute_value, TypedParameter): - attribute_value.name = '_{0}#{1}'.format(type(attribute_value).__name__, attribute_name) - return cls - - -# ------------------------------------------------ -# Serialization of the State -# ------------------------------------------------ -# Serialization of the state object is currently done via generating a dict object. Reversely, we can generate a -# State object from a property manager object, not a dict object. This quirk results from the way Mantid -# treats property manager inputs and outputs (it reads in dicts and converts them to property manager objects). -# We might have to live with that for now. -# -# During serialization we place identifier tags into the serialized object, e.g. we add a specifier if the item -# is a State type at all and if so which state it is. -ENUM_TYPE_TAG = "EnumTag#" - -INT_TAG = "int" - -STATE_NAME = "state_name" -STATE_MODULE = "state_module" -SEPARATOR_SERIAL = "#" -MODULE = "__module__" - - -def is_state(property_manager): - return property_manager.existsProperty(STATE_NAME) and property_manager.existsProperty(STATE_MODULE) - - -def is_float_vector(value): - return isinstance(value, std_vector_dbl) - - -def is_string_vector(value): - return isinstance(value, std_vector_str) - - -def is_int_vector(value): - return isinstance(value, std_vector_int) or isinstance(value, std_vector_long) - - -def get_module_and_class_name(instance): - if inspect.isclass(instance): - module_name, class_name = str(instance.__dict__[MODULE]), str(instance.__name__) - else: - module_name, class_name = str(type(instance).__dict__[MODULE]), str(type(instance).__name__) - return module_name, class_name - - -def provide_class_from_module_and_class_name(module_name, class_name): - module = import_module(module_name) - return getattr(module, class_name) - - -def provide_class(instance): - module_name = instance.getProperty(STATE_MODULE).value - class_name = instance.getProperty(STATE_NAME).value - return provide_class_from_module_and_class_name(module_name, class_name) - - -def is_enum_type_parameter(value): - return isinstance(value, string_types) and ENUM_TYPE_TAG in value - - -def is_enum_list_parameter(value): - if isinstance(value, string_types): - return False - - try: - return all(ENUM_TYPE_TAG in s for s in value) - except TypeError: - return False - - -def get_module_and_class_name_from_encoded_string(encoder, value): - without_encoder = value.replace(encoder, "") - return without_encoder.split(SEPARATOR_SERIAL) - - -def create_sub_state(value): - # We are dealing with a sub state. We first have to create it and then populate it - sub_state_class = provide_class(value) - # Create the sub state, populate it and set it on the super state - sub_state = sub_state_class() - sub_state.property_manager = value - return sub_state - - -def get_descriptor_values(instance): - # Get all user defined attributes - member_variables = inspect.getmembers(type(instance), lambda x: not(inspect.isroutine(x))) - # Remove anything starting with a '_' or '__' - i.e. private attributes such as weak ref - member_variables = [item for item in member_variables if not item[0].startswith('_')] - - # Property manager is a fake property that wraps the serializing method (i.e. this) - # so trying to pack it causes inf recursion - member_variables = [item for item in member_variables if "property_manager" not in item[0]] - - # We only need names - member_variables = [item[0] for item in member_variables] - - # Get the descriptor values from the instance - descriptor_values = {} - for key in member_variables: - if hasattr(instance, key): - value = getattr(instance, key) - if value is not None: - descriptor_values.update({key: value}) - - return descriptor_values - - -def get_class_descriptor_types(instance): - # Get all descriptor names which are TypedParameter of instance's type - descriptors = {} - for descriptor_name, descriptor_object in inspect.getmembers(type(instance)): - if inspect.isdatadescriptor(descriptor_object) and isinstance(descriptor_object, TypedParameter): - descriptors.update({descriptor_name: type(descriptor_object)}) - return descriptors - - -def convert_state_to_dict(instance): - """ - Converts the state object to a dictionary. - - :param instance: the instance which is to be converted - :return: a serialized state object in the form of a dict - """ - attribute = get_descriptor_values(instance) - # Add the descriptors to a dict - state_dict = dict() - - # Don't do anything if primitive type that Mantid can serialize - primative_types = (int, str, bool, float, TypedParameter) - - for attr_name, attr_val in attribute.items(): - if isinstance(attr_val, StateBase): - # If the value is a SANSBaseState then create a dict from it - attr_val = attr_val.property_manager - elif isinstance(attr_val, dict): - attr_val = serialize_dict(attr_val) - elif isinstance(attr_val, Enum) or isinstance(attr_val, list) and all(isinstance(x, Enum) for x in attr_val): - attr_val = serialize_enum(attr_val) - elif isinstance(attr_val, primative_types) \ - or isinstance(attr_val, list) and all(isinstance(x, primative_types) for x in attr_val): - pass # A primative type or list of primitives don't need anything special - else: - raise ValueError("Cannot serialize {0}".format(attr_val)) - - state_dict.update({attr_name: attr_val}) - # Add information about the current state object, such as in which module it lives and what its name is - module_name, class_name = get_module_and_class_name(instance) - state_dict.update({STATE_MODULE: module_name}) - state_dict.update({STATE_NAME: class_name}) - return state_dict - - -def serialize_dict(value): - # If we have a dict, then we need to watch out since a value in the dict might be a State - sub_dictionary = {} - for key_sub, val_sub in list(value.items()): - # We have to handle the key being an enum too - if isinstance(key_sub, Enum): - key_sub = serialize_enum(key_sub) - - if isinstance(val_sub, StateBase): - val_sub = val_sub.property_manager - elif isinstance(val_sub, Enum): - val_sub = serialize_enum(val_sub) - - sub_dictionary.update({key_sub: val_sub}) - - return sub_dictionary - - -def set_state_from_property_manager(instance, property_manager): - """ - Set the State object from the information stored on a property manager object. This is the deserialization step. - - :param instance: the instance which is to be set with a values of the property manager - :param property_manager: the property manager with the stored setting - """ - - def _set_element(inst, k_element, v_element): - if k_element != STATE_NAME and k_element != STATE_MODULE: - setattr(inst, k_element, v_element) - - keys = list(property_manager.keys()) - for key in keys: - value = property_manager.getProperty(key).value - # There are some special scenarios that need to be considered - # 1. ParameterManager 1: This indicates (most often) that we are dealing with a new state -> create it and - # apply recursion - # 2. ParameterManager 2: In some cases the ParameterManager object is actually a map rather than a state -> - # populate the state - - if type(value) is PropertyManager and is_state(value): - sub_state = create_sub_state(value) - setattr(instance, key, sub_state) - elif type(value) is PropertyManager: - deserialize_dict(instance, key, value) - elif is_enum_type_parameter(value) or is_enum_list_parameter(value): - enum_type_parameter = deserialize_enum(value) - _set_element(instance, key, enum_type_parameter) - elif is_float_vector(value): - float_list_value = list(value) - _set_element(instance, key, float_list_value) - elif is_string_vector(value): - string_list_value = list(value) - _set_element(instance, key, string_list_value) - elif is_int_vector(value): - int_list_value = list(value) - _set_element(instance, key, int_list_value) - else: - _set_element(instance, key, value) - - -def deserialize_dict(instance, key, value): - # We must be dealing with an actual dict descriptor - sub_dict_keys = list(value.keys()) - dict_element = {} - # We need to watch out if a value of the dictionary is a sub state - for sub_dict_key in sub_dict_keys: - sub_dict_value = value.getProperty(sub_dict_key).value - if type(sub_dict_value) == PropertyManager and is_state(sub_dict_value): - sub_state = create_sub_state(sub_dict_value) - sub_dict_value_to_insert = sub_state - elif is_enum_type_parameter(sub_dict_value): - sub_dict_value_to_insert = deserialize_enum(sub_dict_value) - else: - sub_dict_value_to_insert = sub_dict_value - - if is_enum_type_parameter(sub_dict_key): - sub_dict_key = deserialize_enum(sub_dict_key) - - dict_element.update({sub_dict_key: sub_dict_value_to_insert}) - setattr(instance, key, dict_element) - - -def serialize_enum(value): - to_parse = value if isinstance(value, list) else [value] - serialized = [] - for val in to_parse: - assert (isinstance(val, Enum)) - module_name, class_name = get_module_and_class_name(val) - selected_val = val.value - - # Some devs use int for enums too so handle that - if isinstance(selected_val, int): - selected_val = INT_TAG + str(selected_val) - - serialized.append(ENUM_TYPE_TAG + module_name + SEPARATOR_SERIAL + class_name + SEPARATOR_SERIAL + selected_val) - - return serialized[0] if len(serialized) == 1 else serialized - - -def deserialize_enum(value): - # Mantid returns a std::vec type which needs to decay to a list - to_parse = [value] if isinstance(value, string_types) else [i for i in value] - parsed = [] - - for serialized_str in to_parse: - module_name, class_name, selection = get_module_and_class_name_from_encoded_string(ENUM_TYPE_TAG, - serialized_str) - enum_class = provide_class_from_module_and_class_name(module_name, class_name) - - selection = int(selection.replace(INT_TAG, '')) if INT_TAG in selection else selection - parsed_val = enum_class(selection) - parsed.append(parsed_val) - - return parsed[0] if len(parsed) == 1 else parsed - - -def create_deserialized_sans_state_from_property_manager(property_manager): - return create_sub_state(property_manager) diff --git a/scripts/SANS/sans/state/wavelength.py b/scripts/SANS/sans/state/wavelength.py index 1cbda44331e73b7c0cf599960f164ff552d1e94d..f10df0f0592197bdb6fe6b91797491772ccc74b8 100644 --- a/scripts/SANS/sans/state/wavelength.py +++ b/scripts/SANS/sans/state/wavelength.py @@ -10,27 +10,22 @@ from __future__ import (absolute_import, division, print_function) import json import copy -from sans.state.state_base import (StateBase, PositiveFloatParameter, rename_descriptor_names, - PositiveFloatListParameter) +from six import with_metaclass + +from sans.state.JsonSerializable import JsonSerializable from sans.common.enums import (RebinType, RangeStepType, SANSFacility) +from sans.state.automatic_setters import automatic_setters from sans.state.state_functions import (is_not_none_and_first_larger_than_second, one_is_none, validation_message) -from sans.state.automatic_setters import (automatic_setters) - -# ---------------------------------------------------------------------------------------------------------------------- -# State -# ---------------------------------------------------------------------------------------------------------------------- -@rename_descriptor_names -class StateWavelength(StateBase): - rebin_type = RebinType.REBIN - wavelength_low = PositiveFloatListParameter() - wavelength_high = PositiveFloatListParameter() - wavelength_step = PositiveFloatParameter() - wavelength_step_type = RangeStepType.NOT_SET +class StateWavelength(with_metaclass(JsonSerializable)): def __init__(self): super(StateWavelength, self).__init__() self.rebin_type = RebinType.REBIN + self.wavelength_low = None # : List[Float] (Positive) + self.wavelength_high = None # : List[Float] (Positive) + self.wavelength_step = None # : Float (Positive) + self.wavelength_step_type = RangeStepType.NOT_SET def validate(self): is_invalid = dict() diff --git a/scripts/SANS/sans/state/wavelength_and_pixel_adjustment.py b/scripts/SANS/sans/state/wavelength_and_pixel_adjustment.py index 75eb298ce4d473c91994b68e0c5b7f2eb6774805..375e228660b1a5cc9db4547a93708a633f0630b0 100644 --- a/scripts/SANS/sans/state/wavelength_and_pixel_adjustment.py +++ b/scripts/SANS/sans/state/wavelength_and_pixel_adjustment.py @@ -11,23 +11,20 @@ from __future__ import (absolute_import, division, print_function) import json import copy -from sans.state.state_base import (StateBase, rename_descriptor_names, StringParameter, - PositiveFloatParameter, DictParameter, PositiveFloatListParameter) + +from six import with_metaclass + +from sans.state.JsonSerializable import JsonSerializable +from sans.state.automatic_setters import automatic_setters from sans.state.state_functions import (is_not_none_and_first_larger_than_second, one_is_none, validation_message) from sans.common.enums import (RangeStepType, DetectorType, SANSFacility) -from sans.state.automatic_setters import (automatic_setters) - -# ---------------------------------------------------------------------------------------------------------------------- -# State -# ---------------------------------------------------------------------------------------------------------------------- -@rename_descriptor_names -class StateAdjustmentFiles(StateBase): - pixel_adjustment_file = StringParameter() - wavelength_adjustment_file = StringParameter() +class StateAdjustmentFiles(with_metaclass(JsonSerializable)): def __init__(self): super(StateAdjustmentFiles, self).__init__() + self.pixel_adjustment_file = None # : Str() + self.wavelength_adjustment_file = None # : Str() def validate(self): is_invalid = {} @@ -39,19 +36,16 @@ class StateAdjustmentFiles(StateBase): "Please see: {0}".format(json.dumps(is_invalid))) -@rename_descriptor_names -class StateWavelengthAndPixelAdjustment(StateBase): - wavelength_low = PositiveFloatListParameter() - wavelength_high = PositiveFloatListParameter() - wavelength_step = PositiveFloatParameter() - wavelength_step_type = RangeStepType.NOT_SET - - adjustment_files = DictParameter() - - idf_path = StringParameter() - +class StateWavelengthAndPixelAdjustment(with_metaclass(JsonSerializable)): def __init__(self): super(StateWavelengthAndPixelAdjustment, self).__init__() + self.wavelength_low = None # : List[Float] (Positive) + self.wavelength_high = None # : List[Float] (Positive) + self.wavelength_step = None # : Float (Positive) + self.wavelength_step_type = RangeStepType.NOT_SET + + self.idf_path = None # : Str() + self.adjustment_files = {DetectorType.LAB.value: StateAdjustmentFiles(), DetectorType.HAB.value: StateAdjustmentFiles()} diff --git a/scripts/SANS/sans/test_helper/mock_objects.py b/scripts/SANS/sans/test_helper/mock_objects.py index f23babbb31d406e2107854a2e065cfd7bfefa6c5..c5871b261f806c017f38a7fe4856e66fc8b81e4e 100644 --- a/scripts/SANS/sans/test_helper/mock_objects.py +++ b/scripts/SANS/sans/test_helper/mock_objects.py @@ -8,9 +8,12 @@ from __future__ import (absolute_import) from functools import (partial) +from six import with_metaclass + from mantid.py3compat import mock from sans.gui_logic.presenter.run_tab_presenter import RunTabPresenter from sans.common.enums import (RangeStepType, OutputMode, SANSFacility, SANSInstrument) +from sans.state.JsonSerializable import JsonSerializable from sans.test_helper.test_director import TestDirector from ui.sans_isis.sans_data_processor_gui import SANSDataProcessorGui from ui.sans_isis.settings_diagnostic_tab import SettingsDiagnosticTab @@ -237,11 +240,10 @@ def create_mock_view2(user_file_path, batch_file_path=None): return view -class FakeState(object): - dummy_state = "dummy_state" - +class FakeState(with_metaclass(JsonSerializable)): def __init__(self): super(FakeState, self).__init__() + self.dummy_state = "dummy_state" @property def property_manager(self): diff --git a/scripts/SANS/sans/test_helper/test_director.py b/scripts/SANS/sans/test_helper/test_director.py index 44266e0661f34bb9e83af83dc70999859161f31f..9f58258b073219b857009d183f3c8898986c6beb 100644 --- a/scripts/SANS/sans/test_helper/test_director.py +++ b/scripts/SANS/sans/test_helper/test_director.py @@ -6,12 +6,12 @@ # SPDX - License - Identifier: GPL - 3.0 + """ A Test director """ from __future__ import (absolute_import, division, print_function) -from sans.state.state import get_state_builder from sans.state.data import get_data_builder from sans.state.move import get_move_builder from sans.state.reduction_mode import get_reduction_mode_builder from sans.state.slice_event import get_slice_event_builder from sans.state.mask import get_mask_builder +from sans.state.state import get_state_builder from sans.state.wavelength import get_wavelength_builder from sans.state.save import get_save_builder from sans.state.normalize_to_monitor import get_normalize_to_monitor_builder @@ -22,7 +22,8 @@ from sans.state.adjustment import get_adjustment_builder from sans.state.convert_to_q import get_convert_to_q_builder from sans.common.enums import (SANSFacility, ReductionMode, ReductionDimensionality, - FitModeForMerge, RebinType, RangeStepType, SaveType, FitType, SampleShape, SANSInstrument) + FitModeForMerge, RebinType, RangeStepType, SaveType, FitType, SampleShape, + SANSInstrument) from sans.test_helper.file_information_mock import SANSFileInformationMock diff --git a/scripts/SANS/sans/user_file/state_director.py b/scripts/SANS/sans/user_file/state_director.py index 635189b63010618f620dfa859fec02d8b53eb573..caafa10403d49ec62b786be42a234273c00db590 100644 --- a/scripts/SANS/sans/user_file/state_director.py +++ b/scripts/SANS/sans/user_file/state_director.py @@ -11,11 +11,12 @@ from sans.common.enums import (DetectorType, FitModeForMerge, RebinType, DataTyp from sans.common.file_information import find_full_file_path from sans.common.general_functions import (get_ranges_for_rebin_setting, get_ranges_for_rebin_array, get_ranges_from_event_slice_setting) +from sans.state.automatic_setters import set_up_setter_forwarding_from_director_to_builder from sans.user_file.user_file_reader import UserFileReader from sans.user_file.settings_tags import (DetectorId, BackId, LimitsId, simple_range, complex_range, MaskId, rebin_string_values, SampleId, SetId, TransId, TubeCalibrationFileId, QResolutionId, FitId, MonId, GravityId, OtherId) -from sans.state.automatic_setters import set_up_setter_forwarding_from_director_to_builder + from sans.state.state import get_state_builder from sans.state.mask import get_mask_builder from sans.state.move import get_move_builder diff --git a/scripts/test/SANS/algorithm_detail/centre_finder_new_test.py b/scripts/test/SANS/algorithm_detail/centre_finder_new_test.py index 796a9db5d1c48b6e2d996c0905a4c5f76c7b2a62..2de8f332037506a9aa0465c79b2d278cc7aa16f2 100644 --- a/scripts/test/SANS/algorithm_detail/centre_finder_new_test.py +++ b/scripts/test/SANS/algorithm_detail/centre_finder_new_test.py @@ -11,11 +11,13 @@ import unittest from mantid.py3compat import mock from sans.algorithm_detail.centre_finder_new import centre_finder_new, centre_finder_mass from sans.common.enums import (SANSDataType, FindDirectionEnum, DetectorType) +from sans.test_helper.test_director import TestDirector class CentreFinderNewTest(unittest.TestCase): def setUp(self): - self.state = mock.MagicMock() + state_builder = TestDirector() + self.state = state_builder.construct() @mock.patch('sans.algorithm_detail.centre_finder_new.provide_loaded_data') @mock.patch('sans.algorithm_detail.centre_finder_new.create_managed_non_child_algorithm') diff --git a/scripts/test/SANS/algorithm_detail/mask_sans_workspace_test.py b/scripts/test/SANS/algorithm_detail/mask_sans_workspace_test.py index bf926e44a8d5851a461364ea1ee4d3d8d0521550..bf4bda666cb635f095236a2efb8c3239d4f841d3 100644 --- a/scripts/test/SANS/algorithm_detail/mask_sans_workspace_test.py +++ b/scripts/test/SANS/algorithm_detail/mask_sans_workspace_test.py @@ -13,6 +13,7 @@ from mantid.simpleapi import CloneWorkspace, DeleteWorkspace, Load, LoadEmptyIns from sans.algorithm_detail.mask_sans_workspace import mask_workspace from sans.common.enums import SANSFacility from sans.common.file_information import SANSFileInformationFactory +from sans.state.Serializer import Serializer from sans.state.data import get_data_builder from sans.state.mask import get_mask_builder from sans.state.move import get_move_builder @@ -294,9 +295,6 @@ class MaskSansWorkspaceTest(unittest.TestCase): bin_mask_general_start = [30000., 67000.] bin_mask_general_stop = [35000., 75000.] - # bin_mask_start = [14000] - # bin_mask_stop = FloatListParameter() - mask_builder.set_bin_mask_general_start(bin_mask_general_start) mask_builder.set_bin_mask_general_stop(bin_mask_general_stop) @@ -409,8 +407,9 @@ class MaskSansWorkspaceTest(unittest.TestCase): test_director.set_states(data_state=data_info, mask_state=mask_info) state = test_director.construct() - returned_data = SANSLoad(SANSState=state.property_manager, SampleScatterWorkspace="mask_sans_ws", + returned_data = SANSLoad(SANSState=Serializer.to_json(state), SampleScatterWorkspace="mask_sans_ws", SampleScatterMonitorWorkspace="dummy") + workspace = returned_data[0] DeleteWorkspace(returned_data[1]) diff --git a/scripts/test/SANS/command_interface/command_interface_state_director_test.py b/scripts/test/SANS/command_interface/command_interface_state_director_test.py index 7704e3b2419d501c0d4e8cdc50a5041129e8a3f3..00ef1c5ec64086d2afafadd2a7fb783a8b088187 100644 --- a/scripts/test/SANS/command_interface/command_interface_state_director_test.py +++ b/scripts/test/SANS/command_interface/command_interface_state_director_test.py @@ -141,14 +141,14 @@ class CommandInterfaceStateDirectorTest(unittest.TestCase): self.assertEqual(state.move.detectors[DetectorType.HAB.value].sample_centre_pos1, 12.4/1000.) self.assertTrue(state.move.detectors[DetectorType.HAB.value].sample_centre_pos2 == 23.54/1000.) - self.assertTrue(state.adjustment.calculate_transmission.fit[DataType.CAN].fit_type + self.assertTrue(state.adjustment.calculate_transmission.fit[DataType.CAN.value].fit_type is FitType.LOGARITHMIC) - self.assertTrue(state.adjustment.calculate_transmission.fit[DataType.CAN].polynomial_order + self.assertTrue(state.adjustment.calculate_transmission.fit[DataType.CAN.value].polynomial_order == 0) - self.assertTrue(state.adjustment.calculate_transmission.fit[DataType.CAN].wavelength_low + self.assertTrue(state.adjustment.calculate_transmission.fit[DataType.CAN.value].wavelength_low == 10.4) - self.assertTrue(state.adjustment.calculate_transmission.fit[DataType.CAN].wavelength_high + self.assertTrue(state.adjustment.calculate_transmission.fit[DataType.CAN.value].wavelength_high == 12.54) self.assertEqual(state.reduction.merge_scale, 1.2) diff --git a/scripts/test/SANS/gui_logic/CMakeLists.txt b/scripts/test/SANS/gui_logic/CMakeLists.txt index 62b534b2ccaaaa7e8850222e705ac7c6f7e16296..9262d9d9590c5c0ee9a74cd2a6af2c7420845a43 100644 --- a/scripts/test/SANS/gui_logic/CMakeLists.txt +++ b/scripts/test/SANS/gui_logic/CMakeLists.txt @@ -7,9 +7,7 @@ set(TEST_PY_FILES gui_state_director_test.py gui_common_test.py masking_table_presenter_test.py - property_manager_service_test.py run_tab_presenter_test.py - sans_data_processor_gui_algorithm_test.py state_gui_model_test.py settings_diagnostic_presenter_test.py table_model_test.py diff --git a/scripts/test/SANS/gui_logic/property_manager_service_test.py b/scripts/test/SANS/gui_logic/property_manager_service_test.py deleted file mode 100644 index e1e0e612b0bd1e0541556a00abd7f4a92c839723..0000000000000000000000000000000000000000 --- a/scripts/test/SANS/gui_logic/property_manager_service_test.py +++ /dev/null @@ -1,105 +0,0 @@ -# Mantid Repository : https://github.com/mantidproject/mantid -# -# Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI, -# NScD Oak Ridge National Laboratory, European Spallation Source -# & Institut Laue - Langevin -# SPDX - License - Identifier: GPL - 3.0 + -from __future__ import (absolute_import, division, print_function) - -import unittest - -from mantid.api import Algorithm -from mantid.kernel import PropertyManagerDataService, PropertyManagerProperty -from sans.common.enums import SANSFacility, SANSInstrument -from sans.gui_logic.presenter.property_manager_service import PropertyManagerService -from sans.state.data import get_data_builder -from sans.test_helper.file_information_mock import SANSFileInformationMock -from sans.test_helper.test_director import TestDirector - - -class FakeAlgorithm(Algorithm): - def PyInit(self): - self.declareProperty(PropertyManagerProperty("Args")) - - def PyExec(self): - pass - - -def get_example_state(): - ws_name_sample = "SANS2D00022024" - file_information = SANSFileInformationMock(instrument=SANSInstrument.SANS2D, run_number=22024) - data_builder = get_data_builder(SANSFacility.ISIS, file_information) - data_builder.set_sample_scatter(ws_name_sample) - data = data_builder.build() - - # Get the sample state - test_director = TestDirector() - test_director.set_states(data_state=data) - return test_director.construct() - - -class PropertyManagerServiceTest(unittest.TestCase): - def test_that_add_states_to_pmds(self): - self.assertEqual(len(PropertyManagerDataService.getObjectNames()), 0) - states = {0: get_example_state(), 1: get_example_state()} - pms = PropertyManagerService() - pms.add_states_to_pmds(states) - self.assertEqual(len(PropertyManagerDataService.getObjectNames()), 2) - self._remove_all_property_managers() - - def test_that_removes_sans_property_managers_from_pmds(self): - self.assertEqual(len(PropertyManagerDataService.getObjectNames()), 0) - states = {0: get_example_state(), 1: get_example_state()} - pms = PropertyManagerService() - pms.add_states_to_pmds(states) - pms.remove_sans_property_managers() - self.assertEqual(len(PropertyManagerDataService.getObjectNames()), 0) - self._remove_all_property_managers() - - def test_that_can_retrieve_states_from_pmds(self): - self.assertEqual(len(PropertyManagerDataService.getObjectNames()), 0) - states = {0: get_example_state(), 1: get_example_state()} - pms = PropertyManagerService() - pms.add_states_to_pmds(states) - retrieved_states = pms.get_states_from_pmds() - self.assertEqual(len(retrieved_states), 2) - self.assertTrue(isinstance(retrieved_states[0], type(states[0]))) - self.assertEqual(len(PropertyManagerDataService.getObjectNames()), 2) - self._remove_all_property_managers() - - def test_that_does_not_delete_pms_which_are_not_sans(self): - property_manager = self._get_property_manager() - PropertyManagerDataService.addOrReplace("test", property_manager) - pms = PropertyManagerService() - pms.remove_sans_property_managers() - self.assertEqual(len(PropertyManagerDataService.getObjectNames()), 1) - self._remove_all_property_managers() - - def test_that_it_adds_nothing_when_empty_list_is_passed_in(self): - pms = PropertyManagerService() - number_of_elements_on_pmds_after = len(PropertyManagerDataService.getObjectNames()) - pms.add_states_to_pmds({}) - self.assertEqual(number_of_elements_on_pmds_after, 0) - self._remove_all_property_managers() - - def test_that_it_returns_empty_list_if_no_states_have_been_added(self): - pms = PropertyManagerService() - states = pms.get_states_from_pmds() - self.assertEqual(states, []) - self._remove_all_property_managers() - - @staticmethod - def _remove_all_property_managers(): - for element in PropertyManagerDataService.getObjectNames(): - PropertyManagerDataService.remove(element) - - @staticmethod - def _get_property_manager(): - alg = FakeAlgorithm() - alg.initialize() - alg.setProperty("Args", {"test": 1}) - return alg.getProperty("Args").value - - -if __name__ == '__main__': - unittest.main() diff --git a/scripts/test/SANS/gui_logic/sans_data_processor_gui_algorithm_test.py b/scripts/test/SANS/gui_logic/sans_data_processor_gui_algorithm_test.py deleted file mode 100644 index b035399cdd6c8adf4eb40fcbcdc3da1d5ac9be32..0000000000000000000000000000000000000000 --- a/scripts/test/SANS/gui_logic/sans_data_processor_gui_algorithm_test.py +++ /dev/null @@ -1,95 +0,0 @@ -# Mantid Repository : https://github.com/mantidproject/mantid -# -# Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI, -# NScD Oak Ridge National Laboratory, European Spallation Source -# & Institut Laue - Langevin -# SPDX - License - Identifier: GPL - 3.0 + -from __future__ import (absolute_import, division, print_function) - -import unittest - -from sans.common.enums import (SANSFacility) -from sans.gui_logic.sans_data_processor_gui_algorithm import (create_properties, create_option_column_properties, - get_gui_algorithm_name, get_white_list, get_black_list) - - -class SANSGuiDataProcessorAlgorithmTest(unittest.TestCase): - def test_that_all_option_columns_are_there(self): - options = create_option_column_properties() - self.assertEqual(len(options), 3) - self.assertEqual(options[0].algorithm_property, "WavelengthMin") - self.assertEqual(options[1].algorithm_property, "WavelengthMax") - self.assertEqual(options[2].algorithm_property, "EventSlices") - - def test_that_the_properties_with_periods_can_be_provided(self): - props = create_properties() - self.assertEqual(len(props), 20) - - expected = [{"algorithm_property": "SampleScatter", "column_name": "SampleScatter"}, - {"algorithm_property": "SampleScatterPeriod", "column_name": "ssp"}, - {"algorithm_property": "SampleTransmission", "column_name": "SampleTrans"}, - {"algorithm_property": "SampleTransmissionPeriod", "column_name": "stp"}, - {"algorithm_property": "SampleDirect", "column_name": "SampleDirect"}, - {"algorithm_property": "SampleDirectPeriod", "column_name": "sdp"}, - {"algorithm_property": "CanScatter", "column_name": "CanScatter"}, - {"algorithm_property": "CanScatterPeriod", "column_name": "csp"}, - {"algorithm_property": "CanTransmission", "column_name": "CanTrans"}, - {"algorithm_property": "CanTransmissionPeriod", "column_name": "ctp"}, - {"algorithm_property": "CanDirect", "column_name": "CanDirect"}, - {"algorithm_property": "CanDirectPeriod", "column_name": "cdp"}, - {"algorithm_property": "UseOptimizations", "column_name": ""}, - {"algorithm_property": "PlotResults", "column_name": ""}, - {"algorithm_property": "OutputName", "column_name": "OutputName"}, - {"algorithm_property": "UserFile", "column_name": "User File"}, - {"algorithm_property": "SampleThickness", "column_name": "Sample Thickness"}, - {"algorithm_property": "RowIndex", "column_name": ""}, - {"algorithm_property": "OutputMode", "column_name": ""}, - {"algorithm_property": "OutputGraph", "column_name": ""}] - - for index, element in enumerate(props): - self.assertEqual(element.algorithm_property, expected[index]["algorithm_property"]) - self.assertEqual(element.column_name, expected[index]["column_name"]) - - def test_that_the_properties_without_periods_can_be_provided(self): - props = create_properties(show_periods=False) - self.assertEqual(len(props), 14) - - expected = [{"algorithm_property": "SampleScatter", "column_name": "SampleScatter"}, - {"algorithm_property": "SampleTransmission", "column_name": "SampleTrans"}, - {"algorithm_property": "SampleDirect", "column_name": "SampleDirect"}, - {"algorithm_property": "CanScatter", "column_name": "CanScatter"}, - {"algorithm_property": "CanTransmission", "column_name": "CanTrans"}, - {"algorithm_property": "CanDirect", "column_name": "CanDirect"}, - {"algorithm_property": "UseOptimizations", "column_name": ""}, - {"algorithm_property": "PlotResults", "column_name": ""}, - {"algorithm_property": "OutputName", "column_name": "OutputName"}, - {"algorithm_property": "UserFile", "column_name": "User File"}, - {"algorithm_property": "SampleThickness", "column_name": "Sample Thickness"}, - {"algorithm_property": "RowIndex", "column_name": ""}, - {"algorithm_property": "OutputMode", "column_name": ""}, - {"algorithm_property": "OutputGraph", "column_name": ""}] - - for index, element in enumerate(props): - self.assertEqual(element.algorithm_property, expected[index]["algorithm_property"]) - self.assertEqual(element.column_name, expected[index]["column_name"]) - - def test_that_gets_gui_algorithm_name(self): - alg_name = get_gui_algorithm_name(SANSFacility.ISIS) - self.assertEqual(alg_name, "SANSGuiDataProcessorAlgorithm") - - def test_that_getting_algorithm_name_raises_when_facility_is_not_known(self): - args = ["teskdfsd"] - self.assertRaises(RuntimeError, get_gui_algorithm_name, *args) - - def test_that_white_list_contains_all_properties(self): - white_list = get_white_list() - self.assertEqual(len(white_list), 20) - - def test_that_black_list_contains_input_and_output_ws(self): - black_list = get_black_list() - self.assertTrue(black_list) - self.assertTrue(black_list.startswith("InputWorkspace,OutputWorkspace")) - - -if __name__ == '__main__': - unittest.main() diff --git a/scripts/test/SANS/gui_logic/settings_diagnostic_presenter_test.py b/scripts/test/SANS/gui_logic/settings_diagnostic_presenter_test.py index 46a6854740190e325b68fc5c7d3a22b48a62773a..6ecf7140242976740722a939ef73e61a613d5f13 100644 --- a/scripts/test/SANS/gui_logic/settings_diagnostic_presenter_test.py +++ b/scripts/test/SANS/gui_logic/settings_diagnostic_presenter_test.py @@ -13,6 +13,7 @@ import unittest from mantid.py3compat import mock from sans.gui_logic.presenter.settings_diagnostic_presenter import SettingsDiagnosticPresenter +from sans.state.Serializer import Serializer from sans.test_helper.mock_objects import (create_run_tab_presenter_mock, FakeState, create_mock_settings_diagnostic_tab) @@ -58,9 +59,8 @@ class SettingsDiagnosticPresenterTest(unittest.TestCase): # Assert self.assertTrue(os.path.exists(dummy_file_path)) - with open(dummy_file_path) as f: - data = json.load(f) - self.assertEqual(data, "dummy_state") + obj = Serializer.load_file(dummy_file_path) + self.assertEqual("dummy_state", obj.dummy_state) if os.path.exists(dummy_file_path): os.remove(dummy_file_path) diff --git a/scripts/test/SANS/state/CMakeLists.txt b/scripts/test/SANS/state/CMakeLists.txt index 4dc96390f8d42338ca4b6874e63799959b5c339e..37989207431f5a9ca7a84871831a156c422625ab 100644 --- a/scripts/test/SANS/state/CMakeLists.txt +++ b/scripts/test/SANS/state/CMakeLists.txt @@ -2,7 +2,7 @@ set(TEST_PY_FILES adjustment_test.py - state_base_test.py + JsonSerializerTest.py calculate_transmission_test.py convert_to_q_test.py data_test.py diff --git a/scripts/test/SANS/state/JsonSerializerTest.py b/scripts/test/SANS/state/JsonSerializerTest.py new file mode 100644 index 0000000000000000000000000000000000000000..d77d4cbfb5fc7310a5407d23db61751a112ee65a --- /dev/null +++ b/scripts/test/SANS/state/JsonSerializerTest.py @@ -0,0 +1,176 @@ +# Mantid Repository : https://github.com/mantidproject/mantid +# +# Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI, +# NScD Oak Ridge National Laboratory, European Spallation Source +# & Institut Laue - Langevin +# SPDX - License - Identifier: GPL - 3.0 + +from __future__ import (absolute_import, division, print_function) + +import unittest + +from six import with_metaclass + +from mantid.api import Algorithm +from mantid.py3compat import Enum + + +# ---------------------------------------------------------------------------------------------------------------------- +# ---------------------------------------------------------------------------------------------------------------------- +# Test the typed parameters +# ---------------------------------------------------------------------------------------------------------------------- +# ---------------------------------------------------------------------------------------------------------------------- +from sans.state.JsonSerializable import JsonSerializable, json_serializable +from sans.state.Serializer import Serializer + + +class TestClass(with_metaclass(JsonSerializable)): + string_parameter = None # : Str() + bool_parameter = None # : Bool + float_parameter = None # : Float + positive_float_parameter = None # : Float (Positive) + positive_integer_parameter = None # : Int (Positive) + dict_parameter = None # : Dict + float_with_none_parameter = None # : Float + positive_float_with_none_parameter = None # : Float (Optional) + float_list_parameter = None # : List[Float] + string_list_parameter = None # : List[Str] + positive_integer_list_parameter = None # : List[Int] (Positive) + + def __init__(self): + super(TestClass, self).__init__() + + def validate(self): + pass + + +@json_serializable +class FakeEnumClass(Enum): + FOO = 1 + BAR = "2" + + +class ExampleWrapper(with_metaclass(JsonSerializable)): + # This has to be at the top module level, else the module name finding will fail + def __init__(self): + self._foo = FakeEnumClass.FOO + self.bar = FakeEnumClass.BAR + + def validate(self): + return True + + +class VerySimpleState(with_metaclass(JsonSerializable)): + string_parameter = None # : Str() + + def __init__(self): + super(VerySimpleState, self).__init__() + self.string_parameter = "test_in_very_simple" + + def validate(self): + pass + + +class SimpleState(with_metaclass(JsonSerializable)): + def __init__(self): + super(SimpleState, self).__init__() + self.string_parameter = "String_in_SimpleState" + self.bool_parameter = False + # We explicitly leave out the float_parameter + self.positive_float_parameter = 1. + self.positive_integer_parameter = 6 + self.dict_parameter = {"1": 123, "2": "test"} + self.float_with_none_parameter = 325. + # We expliclty leave out the positive_float_with_none_parameter + self.float_list_parameter = [123., 234.] + self.string_list_parameter = ["test1", "test2"] + self.positive_integer_list_parameter = [1, 2, 3] + self.sub_state_very_simple = VerySimpleState() + + def validate(self): + pass + + +class ComplexState(with_metaclass(JsonSerializable)): + def __init__(self): + super(ComplexState, self).__init__() + self.float_parameter = 23. + self.positive_float_with_none_parameter = 234. + self.sub_state_1 = SimpleState() + self.dict_parameter = {"A": SimpleState(), "B": SimpleState()} + + def validate(self): + pass + + +class JsonSerializerTest(unittest.TestCase): + class FakeAlgorithm(Algorithm): + def PyInit(self): + self.declareProperty("Args", '') + + def PyExec(self): + pass + + def test_that_enum_can_be_serialized(self): + original_obj = ExampleWrapper() + + # Serializing test + serialized = Serializer.to_json(original_obj) + self.assertTrue("bar" in serialized) + self.assertTrue("_foo" in serialized) + self.assertTrue(isinstance(serialized, str), "The type was not converted to a string") + + # Deserializing Test + fake = JsonSerializerTest.FakeAlgorithm() + fake.initialize() + fake.setProperty("Args", serialized) + property_manager = fake.getProperty("Args").value + + new_obj = Serializer.from_json(property_manager) + self.assertEqual(FakeEnumClass.BAR, new_obj.bar) + self.assertEqual(FakeEnumClass.FOO, new_obj._foo) + + def test_that_enum_list_can_be_serialized(self): + original_obj = ExampleWrapper() + original_obj.bar = [FakeEnumClass.BAR, FakeEnumClass.BAR] + + # Serializing test + serialized = Serializer.to_json(original_obj) + self.assertTrue("bar" in serialized) + self.assertTrue("_foo" in serialized) + self.assertTrue(isinstance(serialized, str)) + + # Deserializing Test + fake = JsonSerializerTest.FakeAlgorithm() + fake.initialize() + fake.setProperty("Args", serialized) + property_manager = fake.getProperty("Args").value + + new_obj = Serializer.from_json(property_manager) + self.assertEqual(original_obj.bar, new_obj.bar) + self.assertEqual(original_obj._foo, new_obj._foo) + + def test_that_sans_state_can_be_serialized_and_deserialized_when_going_through_an_algorithm(self): + # Arrange + state = ComplexState() + + # Act + serialized = Serializer.to_json(state) + fake = JsonSerializerTest.FakeAlgorithm() + fake.initialize() + fake.setProperty("Args", serialized) + property_manager = fake.getProperty("Args").value + + # Assert + self.assertEqual(type(serialized), str) + state_2 = Serializer.from_json(property_manager) + + # The direct sub state + self.assertEqual(state.sub_state_1.float_list_parameter, state_2.sub_state_1.float_list_parameter) + + # The regular parameters + self.assertEqual(state_2.float_parameter, 23.) + self.assertEqual(state_2.positive_float_with_none_parameter, 234.) + + +if __name__ == '__main__': + unittest.main() diff --git a/scripts/test/SANS/state/calculate_transmission_test.py b/scripts/test/SANS/state/calculate_transmission_test.py index e3a26dade8cd3b604bbc84b8cdd144b71bfe82ef..cf75cbe5ab0ca22e10baa5b749fd14d559a5501c 100644 --- a/scripts/test/SANS/state/calculate_transmission_test.py +++ b/scripts/test/SANS/state/calculate_transmission_test.py @@ -26,7 +26,7 @@ class StateCalculateTransmissionTest(unittest.TestCase): value = custom_settings[key] if value is not None: # If the value is None, then don't set it - setattr(state.fit[fit_key], key, value) + setattr(state.fit[fit_key.value], key, value) @staticmethod def _get_calculate_transmission_state(trans_entries, fit_entries): @@ -274,15 +274,15 @@ class StateCalculateTransmissionBuilderTest(unittest.TestCase): self.assertEqual(state.background_TOF_roi_start, 1.4) self.assertEqual(state.background_TOF_roi_stop, 34.4) - self.assertEqual(state.fit[DataType.SAMPLE].fit_type, FitType.LINEAR) - self.assertEqual(state.fit[DataType.SAMPLE].polynomial_order, 0) - self.assertEqual(state.fit[DataType.SAMPLE].wavelength_low, 10.) - self.assertEqual(state.fit[DataType.SAMPLE].wavelength_high, 20.) + self.assertEqual(state.fit[DataType.SAMPLE.value].fit_type, FitType.LINEAR) + self.assertEqual(state.fit[DataType.SAMPLE.value].polynomial_order, 0) + self.assertEqual(state.fit[DataType.SAMPLE.value].wavelength_low, 10.) + self.assertEqual(state.fit[DataType.SAMPLE.value].wavelength_high, 20.) - self.assertEqual(state.fit[DataType.CAN].fit_type, FitType.POLYNOMIAL) - self.assertEqual(state.fit[DataType.CAN].polynomial_order, 3) - self.assertEqual(state.fit[DataType.CAN].wavelength_low, 10.) - self.assertEqual(state.fit[DataType.CAN].wavelength_high, 20.) + self.assertEqual(state.fit[DataType.CAN.value].fit_type, FitType.POLYNOMIAL) + self.assertEqual(state.fit[DataType.CAN.value].polynomial_order, 3) + self.assertEqual(state.fit[DataType.CAN.value].wavelength_low, 10.) + self.assertEqual(state.fit[DataType.CAN.value].wavelength_high, 20.) if __name__ == '__main__': diff --git a/scripts/test/SANS/state/state_base_test.py b/scripts/test/SANS/state/state_base_test.py deleted file mode 100644 index bddcbcff2fcc1fd9201c22210cdf5f6b4d1f8bc3..0000000000000000000000000000000000000000 --- a/scripts/test/SANS/state/state_base_test.py +++ /dev/null @@ -1,326 +0,0 @@ -# Mantid Repository : https://github.com/mantidproject/mantid -# -# Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI, -# NScD Oak Ridge National Laboratory, European Spallation Source -# & Institut Laue - Langevin -# SPDX - License - Identifier: GPL - 3.0 + -from __future__ import (absolute_import, division, print_function) - -import unittest - -from mantid.api import Algorithm -from mantid.kernel import (PropertyManagerProperty, PropertyManager) -from mantid.py3compat import Enum -from sans.state.state_base import (StringParameter, BoolParameter, FloatParameter, PositiveFloatParameter, - PositiveIntegerParameter, DictParameter, FloatWithNoneParameter, - PositiveFloatWithNoneParameter, FloatListParameter, - StringListParameter, PositiveIntegerListParameter, StateBase, - rename_descriptor_names, TypedParameter, validator_sub_state, - create_deserialized_sans_state_from_property_manager) - - -# ---------------------------------------------------------------------------------------------------------------------- -# ---------------------------------------------------------------------------------------------------------------------- -# Test the typed parameters -# ---------------------------------------------------------------------------------------------------------------------- -# ---------------------------------------------------------------------------------------------------------------------- -@rename_descriptor_names -class StateBaseTestClass(StateBase): - string_parameter = StringParameter() - bool_parameter = BoolParameter() - float_parameter = FloatParameter() - positive_float_parameter = PositiveFloatParameter() - positive_integer_parameter = PositiveIntegerParameter() - dict_parameter = DictParameter() - float_with_none_parameter = FloatWithNoneParameter() - positive_float_with_none_parameter = PositiveFloatWithNoneParameter() - float_list_parameter = FloatListParameter() - string_list_parameter = StringListParameter() - positive_integer_list_parameter = PositiveIntegerListParameter() - - def __init__(self): - super(StateBaseTestClass, self).__init__() - - def validate(self): - pass - - -class FakeEnumClass(Enum): - FOO = 1 - BAR = "2" - - -class ExampleWrapper(StateBase): - # This has to be at the top module level, else the module name finding will fail - _foo = FakeEnumClass.FOO - bar = FakeEnumClass.BAR - - def validate(self): - return True - - -class TypedParameterTest(unittest.TestCase): - def _check_that_raises(self, error_type, obj, descriptor_name, value): - try: - setattr(obj, descriptor_name, value) - self.fail() - except error_type: - pass - except: # noqa - self.fail() - - def test_that_can_set_to_valid_value_of_correct_type(self): - test_class = StateBaseTestClass() - try: - test_class.string_parameter = "Test" - test_class.bool_parameter = True - test_class.float_parameter = -23.5768 - test_class.positive_float_parameter = 234.5 - test_class.positive_integer_parameter = 12 - test_class.dict_parameter = {} - test_class.dict_parameter = {"test": 12, "test2": 13} - test_class.float_with_none_parameter = None - test_class.float_with_none_parameter = -123.67 - test_class.positive_float_with_none_parameter = None - test_class.positive_float_with_none_parameter = 123.67 - test_class.float_list_parameter = [12., -123., 2355.] - test_class.string_list_parameter = ["test", "test"] - test_class.positive_integer_list_parameter = [1, 2, 4] - - except ValueError: - self.fail() - - def test_that_will_raise_type_error_if_set_with_wrong_type(self): - test_class = StateBaseTestClass() - self._check_that_raises(TypeError, test_class, "string_parameter", 1.) - self._check_that_raises(TypeError, test_class, "bool_parameter", 1.) - self._check_that_raises(TypeError, test_class, "float_parameter", "test") - self._check_that_raises(TypeError, test_class, "positive_float_parameter", "test") - self._check_that_raises(TypeError, test_class, "positive_integer_parameter", "test") - self._check_that_raises(TypeError, test_class, "dict_parameter", "test") - self._check_that_raises(TypeError, test_class, "float_with_none_parameter", "test") - self._check_that_raises(TypeError, test_class, "positive_float_with_none_parameter", "test") - self._check_that_raises(TypeError, test_class, "float_list_parameter", [1.23, "test"]) - self._check_that_raises(TypeError, test_class, "string_list_parameter", ["test", "test", 123.]) - self._check_that_raises(TypeError, test_class, "positive_integer_list_parameter", [1, "test"]) - - def test_that_will_raise_if_set_with_wrong_value(self): - # Note that this check does not apply to all parameter, it checks the validator - test_class = StateBaseTestClass() - self._check_that_raises(ValueError, test_class, "positive_float_parameter", -1.2) - self._check_that_raises(ValueError, test_class, "positive_integer_parameter", -1) - self._check_that_raises(ValueError, test_class, "positive_float_with_none_parameter", -234.) - self._check_that_raises(ValueError, test_class, "positive_integer_list_parameter", [1, -2, 4]) - - -# ---------------------------------------------------------------------------------------------------------------------- -# ---------------------------------------------------------------------------------------------------------------------- -# Test the sans_parameters decorator -# ---------------------------------------------------------------------------------------------------------------------- -# ---------------------------------------------------------------------------------------------------------------------- - -class SANSParameterTest(unittest.TestCase): - @rename_descriptor_names - class SANSParameterTestClass(object): - my_string_parameter = StringParameter() - my_bool_parameter = BoolParameter() - - class SANSParameterTestClass2(object): - my_string_parameter = StringParameter() - my_bool_parameter = BoolParameter() - - def test_that_name_is_in_readable_format_in_instance_dictionary(self): - test_class = SANSParameterTest.SANSParameterTestClass() - test_class.my_string_parameter = "test" - test_class.my_bool_parameter = True - keys = list(test_class.__dict__.keys()) - # We don't have a sensible name in the instance dictionary - self.assertTrue("_BoolParameter#my_bool_parameter" in keys) - self.assertTrue("_StringParameter#my_string_parameter" in keys) - - def test_that_name_cannot_be_found_in_instance_dictionary_when_sans_parameters_decorator_is_not_applied(self): - test_class = SANSParameterTest.SANSParameterTestClass2() - test_class.my_string_parameter = "test" - test_class.my_bool_parameter = True - keys = list(test_class.__dict__.keys()) - # We don't have a sensible name in the instance dictionary. - # It will be rather stored as something like: _BoolParameter#2 etc. - self.assertTrue("_BoolParameter#my_bool_parameter" not in keys) - self.assertTrue("_StringParameter#my_string_parameter" not in keys) - - -# ---------------------------------------------------------------------------------------------------------------------- -# ---------------------------------------------------------------------------------------------------------------------- -# StateBase -# This will mainly test serialization -# ---------------------------------------------------------------------------------------------------------------------- -# ---------------------------------------------------------------------------------------------------------------------- - -@rename_descriptor_names -class VerySimpleState(StateBase): - string_parameter = StringParameter() - - def __init__(self): - super(VerySimpleState, self).__init__() - self.string_parameter = "test_in_very_simple" - - def validate(self): - pass - - -@rename_descriptor_names -class SimpleState(StateBase): - string_parameter = StringParameter() - bool_parameter = BoolParameter() - float_parameter = FloatParameter() - positive_float_parameter = PositiveFloatParameter() - positive_integer_parameter = PositiveIntegerParameter() - dict_parameter = DictParameter() - float_with_none_parameter = FloatWithNoneParameter() - positive_float_with_none_parameter = PositiveFloatWithNoneParameter() - float_list_parameter = FloatListParameter() - string_list_parameter = StringListParameter() - positive_integer_list_parameter = PositiveIntegerListParameter() - sub_state_very_simple = TypedParameter(VerySimpleState, validator_sub_state) - - def __init__(self): - super(SimpleState, self).__init__() - self.string_parameter = "String_in_SimpleState" - self.bool_parameter = False - # We explicitly leave out the float_parameter - self.positive_float_parameter = 1. - self.positive_integer_parameter = 6 - self.dict_parameter = {"1": 123, "2": "test"} - self.float_with_none_parameter = 325. - # We expliclty leave out the positive_float_with_none_parameter - self.float_list_parameter = [123., 234.] - self.string_list_parameter = ["test1", "test2"] - self.positive_integer_list_parameter = [1, 2, 3] - self.sub_state_very_simple = VerySimpleState() - - def validate(self): - pass - - -@rename_descriptor_names -class ComplexState(StateBase): - float_parameter = FloatParameter() - positive_float_with_none_parameter = PositiveFloatWithNoneParameter() - sub_state_1 = TypedParameter(SimpleState, validator_sub_state) - dict_parameter = DictParameter() - - def __init__(self): - super(ComplexState, self).__init__() - self.float_parameter = 23. - self.positive_float_with_none_parameter = 234. - self.sub_state_1 = SimpleState() - self.dict_parameter = {"A": SimpleState(), "B": SimpleState()} - - def validate(self): - pass - - -class TestStateBase(unittest.TestCase): - class FakeAlgorithm(Algorithm): - def PyInit(self): - self.declareProperty(PropertyManagerProperty("Args")) - - def PyExec(self): - pass - - def _assert_simple_state(self, state): - self.assertEqual(state.string_parameter, "String_in_SimpleState") - self.assertFalse(state.bool_parameter) - self.assertEqual(state.float_parameter, None) # We did not set it on the instance - self.assertEqual(state.positive_float_parameter, 1.) - self.assertEqual(state.positive_integer_parameter, 6) - self.assertEqual(state.dict_parameter["1"], 123) - self.assertEqual(state.dict_parameter["2"], "test") - self.assertEqual(state.float_with_none_parameter, 325.) - self.assertEqual(state.positive_float_with_none_parameter, None) - - self.assertEqual(len(state.float_list_parameter), 2) - self.assertEqual(state.float_list_parameter[0], 123.) - self.assertEqual(state.float_list_parameter[1], 234.) - - self.assertEqual(len(state.string_list_parameter), 2) - self.assertEqual(state.string_list_parameter[0], "test1") - self.assertEqual(state.string_list_parameter[1], "test2") - - self.assertEqual(len(state.positive_integer_list_parameter), 3) - self.assertEqual(state.positive_integer_list_parameter[0], 1) - self.assertEqual(state.positive_integer_list_parameter[1], 2) - self.assertEqual(state.positive_integer_list_parameter[2], 3) - - self.assertEqual(state.sub_state_very_simple.string_parameter, "test_in_very_simple") - - def test_that_enum_can_be_serialized(self): - original_obj = ExampleWrapper() - - # Serializing test - serialized = original_obj.property_manager - self.assertTrue("bar" in serialized) - self.assertFalse("_foo" in serialized) - self.assertTrue(isinstance(serialized["bar"], str), "The type was not converted to a string") - - # Deserializing Test - fake = TestStateBase.FakeAlgorithm() - fake.initialize() - fake.setProperty("Args", serialized) - property_manager = fake.getProperty("Args").value - - new_obj = create_deserialized_sans_state_from_property_manager(property_manager) - self.assertEqual(FakeEnumClass.BAR, new_obj.bar) - self.assertEqual(FakeEnumClass.FOO, new_obj._foo) - - def test_that_enum_list_can_be_serialized(self): - original_obj = ExampleWrapper() - original_obj.bar = [FakeEnumClass.BAR, FakeEnumClass.BAR] - - # Serializing test - serialized = original_obj.property_manager - self.assertTrue("bar" in serialized) - self.assertFalse("_foo" in serialized) - self.assertTrue(isinstance(serialized["bar"], list), "The type was not converted to a list of strings") - - # Deserializing Test - fake = TestStateBase.FakeAlgorithm() - fake.initialize() - fake.setProperty("Args", serialized) - property_manager = fake.getProperty("Args").value - - new_obj = create_deserialized_sans_state_from_property_manager(property_manager) - self.assertEqual(original_obj.bar, new_obj.bar) - self.assertEqual(original_obj._foo, new_obj._foo) - - def test_that_sans_state_can_be_serialized_and_deserialized_when_going_through_an_algorithm(self): - # Arrange - state = ComplexState() - - # Act - serialized = state.property_manager - fake = TestStateBase.FakeAlgorithm() - fake.initialize() - fake.setProperty("Args", serialized) - property_manager = fake.getProperty("Args").value - - # Assert - self.assertEqual(type(serialized), dict) - self.assertEqual(type(property_manager), PropertyManager) - state_2 = create_deserialized_sans_state_from_property_manager(property_manager) - state_2.property_manager = property_manager - - # The direct sub state - self._assert_simple_state(state_2.sub_state_1) - - # The two states in the dictionary - self._assert_simple_state(state_2.dict_parameter["A"]) - self._assert_simple_state(state_2.dict_parameter["B"]) - - # The regular parameters - self.assertEqual(state_2.float_parameter, 23.) - self.assertEqual(state_2.positive_float_with_none_parameter, 234.) - - -if __name__ == '__main__': - unittest.main() diff --git a/scripts/test/SANS/user_file/state_director_test.py b/scripts/test/SANS/user_file/state_director_test.py index a512b3c5a882fb7da74e385d76bef9211ce5443a..127ef6bfcb477151b0b114ac9c95869f5a4f6b8a 100644 --- a/scripts/test/SANS/user_file/state_director_test.py +++ b/scripts/test/SANS/user_file/state_director_test.py @@ -147,14 +147,14 @@ class UserFileStateDirectorISISTest(unittest.TestCase): self.assertEqual(calculate_transmission.background_TOF_monitor_stop["2"], 98000) self.assertEqual(calculate_transmission.background_TOF_roi_start, 123) self.assertEqual(calculate_transmission.background_TOF_roi_stop, 466) - self.assertEqual(calculate_transmission.fit[DataType.SAMPLE].fit_type, FitType.LOGARITHMIC) - self.assertEqual(calculate_transmission.fit[DataType.SAMPLE].wavelength_low, 1.5) - self.assertEqual(calculate_transmission.fit[DataType.SAMPLE].wavelength_high, 12.5) - self.assertEqual(calculate_transmission.fit[DataType.SAMPLE].polynomial_order, 0) - self.assertEqual(calculate_transmission.fit[DataType.CAN].fit_type, FitType.LOGARITHMIC) - self.assertEqual(calculate_transmission.fit[DataType.CAN].wavelength_low, 1.5) - self.assertEqual(calculate_transmission.fit[DataType.CAN].wavelength_high, 12.5) - self.assertEqual(calculate_transmission.fit[DataType.CAN].polynomial_order, 0) + self.assertEqual(calculate_transmission.fit[DataType.SAMPLE.value].fit_type, FitType.LOGARITHMIC) + self.assertEqual(calculate_transmission.fit[DataType.SAMPLE.value].wavelength_low, 1.5) + self.assertEqual(calculate_transmission.fit[DataType.SAMPLE.value].wavelength_high, 12.5) + self.assertEqual(calculate_transmission.fit[DataType.SAMPLE.value].polynomial_order, 0) + self.assertEqual(calculate_transmission.fit[DataType.CAN.value].fit_type, FitType.LOGARITHMIC) + self.assertEqual(calculate_transmission.fit[DataType.CAN.value].wavelength_low, 1.5) + self.assertEqual(calculate_transmission.fit[DataType.CAN.value].wavelength_high, 12.5) + self.assertEqual(calculate_transmission.fit[DataType.CAN.value].polynomial_order, 0) # Wavelength and Pixel Adjustment wavelength_and_pixel_adjustment = adjustment.wavelength_and_pixel_adjustment @@ -163,11 +163,11 @@ class UserFileStateDirectorISISTest(unittest.TestCase): self.assertEqual(wavelength_and_pixel_adjustment.wavelength_step, 0.125) self.assertEqual(wavelength_and_pixel_adjustment.wavelength_step_type, RangeStepType.LIN) self.assertTrue(wavelength_and_pixel_adjustment.adjustment_files[ - DetectorType.LAB.value].wavelength_adjustment_file == - "DIRECTM1_15785_12m_31Oct12_v12.dat") + DetectorType.LAB.value].wavelength_adjustment_file + == "DIRECTM1_15785_12m_31Oct12_v12.dat") self.assertTrue(wavelength_and_pixel_adjustment.adjustment_files[ - DetectorType.HAB.value].wavelength_adjustment_file == - "DIRECTM1_15785_12m_31Oct12_v12.dat") + DetectorType.HAB.value].wavelength_adjustment_file + == "DIRECTM1_15785_12m_31Oct12_v12.dat") # Assert wide angle correction self.assertTrue(state.adjustment.wide_angle_correction)