def __init__(self, model, view): self.model = model self.view = view self.worker = None self.calibration_notifier = self.CalibrationNotifier(self) self.current_calibration = CalibrationInfo() self.connect_view_signals() # Main Window State Variables self.instrument = "ENGINX" self.rb_num = None # Cropping Options self.cropping_widget = CroppingPresenter(parent=self.view, view=self.view.get_cropping_widget()) self.show_cropping(False)
def test_calibrate_clicked_new_valid_calibration_with_plotting(self, mock_validate, mock_start_worker): self.view.get_new_checked.return_value = True self.view.get_plot_output.return_value = True mock_validate.return_value = True self.presenter.current_calibration = mock.create_autospec(CalibrationInfo()) self.presenter.on_calibrate_clicked() mock_start_worker.assert_called_once_with(mock_validate())
def test_calibrate_clicked_new_invalid_calibration(self, mock_validate, mock_start_worker): self.view.get_new_checked.return_value = True self.view.get_plot_output.return_value = True self.view.get_load_checked.return_value = False mock_validate.return_value = False self.presenter.current_calibration = mock.create_autospec(CalibrationInfo()) self.presenter.on_calibrate_clicked() mock_start_worker.assert_not_called() # called if created new one self.model.load_existing_calibration_files.assert_not_called() # called if loaded calibration
def test_calibrate_clicked_load_invalid_path(self, mock_valid_path, mock_setting): self.presenter.calibration_notifier = MagicMock() self.view.get_new_checked.return_value = False self.view.get_load_checked.return_value = True mock_valid_path.return_value = False self.presenter.current_calibration = mock.create_autospec(CalibrationInfo()) self.presenter.on_calibrate_clicked() self.presenter.current_calibration.set_calibration_from_prm_fname.assert_not_called() self.presenter.calibration_notifier.notify_subscribers.assert_not_called() mock_setting.assert_not_called()
def test_create_output_files_makes_savdir_and_saves_both_banks( self, mock_write_prm, mock_save_nxs, mock_exists, mock_mkdir, mock_copy): mock_exists.return_value = False # make new directory calibration = CalibrationInfo( ) # easier to work with real calibration info object here prm_name = "ENGINX_193749_all_banks.prm" calibration.set_calibration_from_prm_fname(prm_name) calibration.set_calibration_table("cal_table") save_dir = "savedir" create_output_files(save_dir, calibration, "ws") mock_mkdir.assert_called_once_with(save_dir) self.calibration.save_grouping_workspace.assert_not_called( ) # only called if not bank data prm_fpath = path.join(save_dir, prm_name) write_prm_calls = [ call("ws", prm_fpath), call("ws", prm_fpath.replace("all_banks", "bank_1"), spec_nums=[0]), call("ws", prm_fpath.replace("all_banks", "bank_2"), spec_nums=[1]) ] mock_write_prm.assert_has_calls(write_prm_calls) nxs_fpath = prm_fpath.replace(".prm", ".nxs") mock_save_nxs.assert_called_once_with(InputWorkspace="cal_table", Filename=nxs_fpath) copy_calls = [ call(nxs_fpath, nxs_fpath.replace("all_banks", "bank_1")), call(nxs_fpath, nxs_fpath.replace("all_banks", "bank_2")) ] mock_copy.assert_has_calls(copy_calls)
def test_update_calibration_from_view_no_cropping(self): self.view.get_load_checked.return_value = False self.view.get_crop_checked.return_value = False self.presenter.instrument = "ENGINX" self.view.get_sample_filename.return_value = "193749" self.presenter.current_calibration = mock.create_autospec(CalibrationInfo()) self.presenter.update_calibration_from_view() self.presenter.current_calibration.set_calibration_paths.assert_called_once_with("ENGINX", "193749") self.presenter.current_calibration.set_group.assert_called_once_with(GROUP.BOTH) self.presenter.current_calibration.set_cal_file.assert_not_called() self.presenter.current_calibration.set_spectra_list.assert_not_called()
def __init__(self, vanadium_run: str, focus_runs: Sequence[str], save_dir: str, full_inst_calib_path: str, prm_path: Optional[str] = None, ceria_run: Optional[str] = None, group: Optional[GROUP] = None, calfile_path: Optional[str] = None, spectrum_num: Optional[str] = None) -> None: # init attributes self.calibration = CalibrationInfo() self.van_run = vanadium_run self.focus_runs = focus_runs self.save_dir = save_dir # Load custom full inst calib if supplied (needs to be in ADS) try: self.full_calib_ws = Load(full_inst_calib_path, OutputWorkspace="full_inst_calib") except ValueError as e: logger.error("Unable to load calibration file " + full_inst_calib_path + ". Error: " + str(e)) # setup CalibrationInfo object if prm_path: self.calibration.set_calibration_from_prm_fname( prm_path) # to load existing calibration elif ceria_run and group: # make new calibration self.calibration.set_group(group) self.calibration.set_calibration_paths("ENGINX", ceria_run) if group == GROUP.CUSTOM and calfile_path: self.calibration.set_cal_file(calfile_path) elif group == GROUP.CROPPED and spectrum_num: self.calibration.set_spectra_list(spectrum_num)
def __init__(self, model, view): self.model = model self.view = view self.worker = None self.calibration_observer = CalibrationObserver(self) # Observable Setup self.focus_run_notifier = GenericObservable() # Connect view signals to local methods. self.view.set_on_focus_clicked(self.on_focus_clicked) self.view.set_enable_controls_connection( self.set_focus_controls_enabled) # Variables from other GUI tabs. self.current_calibration = CalibrationInfo() self.instrument = "ENGINX" self.rb_num = None last_van_path = get_setting(output_settings.INTERFACES_SETTINGS_GROUP, output_settings.ENGINEERING_PREFIX, "last_vanadium_run") if last_van_path: self.view.set_van_file_text_with_search(last_van_path)
def test_calibrate_clicked_load_valid_path(self, mock_valid_path, mock_setting): self.presenter.calibration_notifier = MagicMock() self.view.get_new_checked.return_value = False self.view.get_load_checked.return_value = True prm_filepath = "path/to/prm.prm" self.view.get_path_filename.return_value = prm_filepath mock_valid_path.return_value = True self.presenter.current_calibration = mock.create_autospec(CalibrationInfo()) self.presenter.on_calibrate_clicked() self.model.load_existing_calibration_files.assert_called_with(self.presenter.current_calibration) self.presenter.current_calibration.set_calibration_from_prm_fname.assert_called_with(prm_filepath) self.presenter.calibration_notifier.notify_subscribers.assert_called_once() mock_setting.assert_called_once()
def setUp(self): self.calibration = create_autospec(CalibrationInfo()) self.calibration.is_valid.return_value = True self.calibration.get_instrument.return_value = "ENGINX" self.calibration.get_group_suffix.return_value = "all_banks" self.calibration.get_foc_ws_suffix.return_value = "bank"
class CalibrationPresenter(object): def __init__(self, model, view): self.model = model self.view = view self.worker = None self.calibration_notifier = self.CalibrationNotifier(self) self.current_calibration = CalibrationInfo() self.connect_view_signals() # Main Window State Variables self.instrument = "ENGINX" self.rb_num = None # Cropping Options self.cropping_widget = CroppingPresenter(parent=self.view, view=self.view.get_cropping_widget()) self.show_cropping(False) def connect_view_signals(self): self.view.set_on_calibrate_clicked(self.on_calibrate_clicked) self.view.set_enable_controls_connection(self.set_calibrate_controls_enabled) self.view.set_update_field_connection(self.set_field_value) self.view.set_on_radio_new_toggled(self.set_create_new_enabled) self.view.set_on_radio_existing_toggled(self.set_load_existing_enabled) self.view.set_on_check_cropping_state_changed(self.show_cropping) def update_calibration_from_view(self): self.current_calibration.clear() if self.view.get_load_checked(): # loading calibration from path to .prm self.current_calibration.set_calibration_from_prm_fname(self.view.get_path_filename()) else: # make a new calibration sample_file = self.view.get_sample_filename() self.current_calibration.set_calibration_paths(self.instrument, sample_file) # set group and any additional parameters needed if self.view.get_crop_checked(): self.current_calibration.set_group(self.cropping_widget.get_group()) if self.current_calibration.group == GROUP.CUSTOM: self.current_calibration.set_cal_file(self.cropping_widget.get_custom_calfile()) elif self.current_calibration.group == GROUP.CROPPED: self.current_calibration.set_spectra_list(self.cropping_widget.get_custom_spectra()) else: # default if no cropping self.current_calibration.set_group(GROUP.BOTH) def on_calibrate_clicked(self): if self.view.get_new_checked() and self._validate(): self.update_calibration_from_view() self.start_calibration_worker(self.view.get_plot_output()) elif self.view.get_load_checked() and self.validate_path(): self.update_calibration_from_view() self.model.load_existing_calibration_files(self.current_calibration) self._notify_updated_calibration() def start_calibration_worker(self, plot_output): """ Calibrate the data in a separate thread so as to not freeze the GUI. """ self.worker = AsyncTask(self.model.create_new_calibration, (self.current_calibration, self.rb_num, plot_output), error_cb=self._on_error, success_cb=self._on_success) self.set_calibrate_controls_enabled(False) self.worker.start() def _on_error(self, error_info): logger.error(str(error_info)) self.emit_enable_button_signal() def _on_success(self, success_info): self._notify_updated_calibration() self.emit_enable_button_signal() def _notify_updated_calibration(self): self.calibration_notifier.notify_subscribers(self.current_calibration) set_setting(output_settings.INTERFACES_SETTINGS_GROUP, output_settings.ENGINEERING_PREFIX, "last_calibration_path", self.current_calibration.get_prm_filepath()) def set_field_value(self): self.view.set_sample_text(self.current_calibration.get_sample()) def load_last_calibration(self) -> None: """ Loads the most recently created or loaded calibration into the interface instance. To be used on interface startup. """ last_cal_path = get_setting(output_settings.INTERFACES_SETTINGS_GROUP, output_settings.ENGINEERING_PREFIX, "last_calibration_path") if last_cal_path: self.view.set_load_checked(True) self.view.set_file_text_with_search(last_cal_path) def set_instrument_override(self, instrument): instrument = INSTRUMENT_DICT[instrument] self.view.set_instrument_override(instrument) self.instrument = instrument def set_rb_num(self, rb_num): self.rb_num = rb_num def _validate(self): # Do nothing if run numbers are invalid or view is searching. if self.view.is_searching(): create_error_message(self.view, "Mantid is searching for data files. Please wait.") return False if not self.view.get_sample_valid(): create_error_message(self.view, "Check run numbers/path is valid.") return False if self.view.get_crop_checked(): if self.cropping_widget.get_custom_calfile_enabled() and not self.cropping_widget.is_calfile_valid(): create_error_message(self.view, "Check custom calfile path is valid.") return False if self.cropping_widget.get_custom_spectra_enabled() and not self.cropping_widget.is_spectra_valid(): create_error_message(self.view, "Check custom spectra are valid.") return False return True def validate_path(self): return self.view.get_path_valid() def emit_enable_button_signal(self): self.view.sig_enable_controls.emit(True) def set_calibrate_controls_enabled(self, enabled): self.view.set_calibrate_button_enabled(enabled) def set_create_new_enabled(self, enabled): self.view.set_sample_enabled(enabled) if enabled: self.set_calibrate_button_text("Calibrate") self.view.set_check_plot_output_enabled(True) self.view.set_check_cropping_enabled(True) self.find_files() def set_load_existing_enabled(self, enabled): self.view.set_path_enabled(enabled) if enabled: self.set_calibrate_button_text("Load") self.view.set_check_plot_output_enabled(False) self.view.set_check_cropping_enabled(False) self.view.set_check_cropping_checked(False) def set_calibrate_button_text(self, text): self.view.set_calibrate_button_text(text) def find_files(self): self.view.find_sample_files() def show_cropping(self, show): self.view.set_cropping_widget_visibility(show) # ----------------------- # Observers / Observables # ----------------------- class CalibrationNotifier(Observable): def __init__(self, outer): Observable.__init__(self) self.outer = outer def notify_subscribers(self, *args, **kwargs): Observable.notify_subscribers(self, *args)
def setUp(self): self.model = CalibrationModel() self.calibration_info = create_autospec(CalibrationInfo()) mock.NonCallableMock.assert_any_call_partial = assert_any_call_partial
class EnginX: def __init__(self, vanadium_run: str, focus_runs: Sequence[str], save_dir: str, full_inst_calib_path: str, prm_path: Optional[str] = None, ceria_run: Optional[str] = None, group: Optional[GROUP] = None, calfile_path: Optional[str] = None, spectrum_num: Optional[str] = None) -> None: # init attributes self.calibration = CalibrationInfo() self.van_run = vanadium_run self.focus_runs = focus_runs self.save_dir = save_dir # Load custom full inst calib if supplied (needs to be in ADS) try: self.full_calib_ws = Load(full_inst_calib_path, OutputWorkspace="full_inst_calib") except ValueError as e: logger.error("Unable to load calibration file " + full_inst_calib_path + ". Error: " + str(e)) # setup CalibrationInfo object if prm_path: self.calibration.set_calibration_from_prm_fname( prm_path) # to load existing calibration elif ceria_run and group: # make new calibration self.calibration.set_group(group) self.calibration.set_calibration_paths("ENGINX", ceria_run) if group == GROUP.CUSTOM and calfile_path: self.calibration.set_cal_file(calfile_path) elif group == GROUP.CROPPED and spectrum_num: self.calibration.set_spectra_list(spectrum_num) def calibrate(self, plot_output: bool) -> None: if self.calibration.get_prm_filepath(): self.calibration.load_relevant_calibration_files( ) # loading existing calibration files else: create_new_calibration(self.calibration, rb_num=None, plot_output=plot_output, save_dir=self.save_dir, full_calib=self.full_calib_ws) def focus(self, plot_output: bool) -> None: if self.calibration.is_valid() and self.van_run: focus_run(self.focus_runs, self.van_run, plot_output, rb_num=None, calibration=self.calibration, save_dir=self.save_dir, full_calib=self.full_calib_ws) def main(self, plot_cal: bool = False, plot_foc: bool = False): self.calibrate(plot_cal) self.focus(plot_foc)
class FocusPresenter(object): def __init__(self, model, view): self.model = model self.view = view self.worker = None self.calibration_observer = CalibrationObserver(self) # Observable Setup self.focus_run_notifier = GenericObservable() # Connect view signals to local methods. self.view.set_on_focus_clicked(self.on_focus_clicked) self.view.set_enable_controls_connection( self.set_focus_controls_enabled) # Variables from other GUI tabs. self.current_calibration = CalibrationInfo() self.instrument = "ENGINX" self.rb_num = None last_van_path = get_setting(output_settings.INTERFACES_SETTINGS_GROUP, output_settings.ENGINEERING_PREFIX, "last_vanadium_run") if last_van_path: self.view.set_van_file_text_with_search(last_van_path) def add_focus_subscriber(self, obs): self.focus_run_notifier.add_subscriber(obs) def on_focus_clicked(self): if not self._validate(): return focus_paths = self.view.get_focus_filenames() van_path = self.view.get_vanadium_filename() if self._number_of_files_warning(focus_paths): self.start_focus_worker(focus_paths, van_path, self.view.get_plot_output(), self.rb_num, self.current_calibration) van_run = self.view.get_vanadium_run() set_setting(output_settings.INTERFACES_SETTINGS_GROUP, output_settings.ENGINEERING_PREFIX, "last_vanadium_run", van_run) def start_focus_worker(self, focus_paths: list, van_path: str, plot_output: bool, rb_num: str, calibration: CalibrationInfo) -> None: """ Focus data in a separate thread to stop the main GUI from hanging. :param focus_paths: List of paths to the files containing the data to focus. :param plot_output: True if the output should be plotted. :param rb_num: The RB Number from the main window (often an experiment id) :param regions_dict: Dictionary containing the regions to focus over, mapping region_name -> grouping_ws_name """ self.worker = AsyncTask( self.model.focus_run, (focus_paths, van_path, plot_output, rb_num, calibration), error_cb=self._on_worker_error, finished_cb=self._on_worker_success) self.set_focus_controls_enabled(False) self.worker.start() def _on_worker_success(self): self.emit_enable_button_signal() self.focus_run_notifier.notify_subscribers( self.model.get_last_focused_files()) def set_instrument_override(self, instrument): instrument = INSTRUMENT_DICT[instrument] self.view.set_instrument_override(instrument) self.instrument = instrument def set_rb_num(self, rb_num): self.rb_num = rb_num def _validate(self): """ Ensure that the worker is ready to be started. :return: True if the worker can be started safely. """ if self.view.is_searching(): create_error_message( self.view, "Mantid is searching for data files. Please wait.") return False if not self.view.get_focus_valid(): create_error_message(self.view, "Check run numbers/path is valid.") return False if not self.view.get_vanadium_valid(): create_error_message(self.view, "Check vanadium run number/path is valid.") return False if not self.current_calibration.is_valid(): create_error_message( self.view, "Create or Load a calibration via the Calibration tab before focusing." ) return False if self.current_calibration.get_instrument() != self.instrument: create_error_message( self.view, "Please make sure the selected instrument matches instrument for the current calibration.\n" "The instrument for the current calibration is: " + self.current_calibration.get_instrument()) return False return True def _number_of_files_warning(self, paths): if len( paths ) > 10: # Just a guess on the warning for now. May change in future. response = QMessageBox.warning( self.view, 'Engineering Diffraction - Warning', 'You are attempting to focus {} workspaces. This may take some time.\n\n Would you like to continue?' .format(len(paths)), QMessageBox.Ok | QMessageBox.Cancel) return response == QMessageBox.Ok else: return True def _on_worker_error(self, error_info): logger.error(str(error_info)) self.emit_enable_button_signal() def set_focus_controls_enabled(self, enabled): self.view.set_focus_button_enabled(enabled) self.view.set_plot_output_enabled(enabled) def emit_enable_button_signal(self): self.view.sig_enable_controls.emit(True) def update_calibration(self, calibration): """ Update the current calibration following an call from a CalibrationNotifier :param calibration: The new current calibration. """ self.current_calibration = calibration region_text = calibration.get_group_description() self.view.set_region_display_text(region_text)