diff --git a/src/aare/gui/panels/scan_settings_panel.py b/src/aare/gui/panels/scan_settings_panel.py index 04d67301..f6085ade 100644 --- a/src/aare/gui/panels/scan_settings_panel.py +++ b/src/aare/gui/panels/scan_settings_panel.py @@ -48,9 +48,24 @@ from aare.gui.widgets.number_line_edit import NumberLineEdit logger = setup_logger(LOGGER_NAME) -def _as_float(value: Any) -> float | None: - """Spreadsheet values arrive as numbers or as strings; an empty cell is None.""" - return None if value is None else float(value) +# Columns already reported as unusable, so the status loop says it once. +_BAD_COLUMNS: set[str] = set() + + +def _spreadsheet_float(params: Any, column: str) -> float | None: + """One cell of the sample's spreadsheet row, as a float. An empty cell, a + cell that does not hold a number, and a column aareDB does not have all + read as None: aareDB owns these names, and a renamed column must not kill + the status loop. Each bad column is reported once - pydantic's own message + names the column it expected instead.""" + try: + value = getattr(params, column) + return None if value is None else float(value) + except (AttributeError, TypeError, ValueError) as e: + if column not in _BAD_COLUMNS: + _BAD_COLUMNS.add(column) + logger.error(f"Ignoring the aareDB parameter {column!r}: {e}") + return None @dataclass @@ -71,16 +86,16 @@ class SampleParameters: params = None if sample is None else sample.aaredb_params if params is None: return cls() - transmission = _as_float(params.transmission) + transmission = _spreadsheet_float(params, "transmission") if transmission is not None and transmission > 1.0: # aareDB holds transmission either as a fraction or as a percentage transmission = transmission / 100.0 return cls( - resolution_a=_as_float(params.targetresolution), + resolution_a=_spreadsheet_float(params, "targetresolution"), transmission=transmission, - total_angle_deg=_as_float(params.totalrange), - image_angle_deg=_as_float(params.oscillation), - exp_time_s=_as_float(params.exposure), + total_angle_deg=_spreadsheet_float(params, "totalangle"), + image_angle_deg=_spreadsheet_float(params, "oscillation"), + exp_time_s=_spreadsheet_float(params, "exposure"), ) diff --git a/tests/unit/gui/test_data_collection_settings.py b/tests/unit/gui/test_data_collection_settings.py index a1a45d2c..70d296a8 100644 --- a/tests/unit/gui/test_data_collection_settings.py +++ b/tests/unit/gui/test_data_collection_settings.py @@ -46,7 +46,7 @@ def test_sample_parameters_translate_the_spreadsheet_row(): params = types.SimpleNamespace( targetresolution=1.5, transmission=20.0, # percent - totalrange="180", # spreadsheet cells can arrive as text + totalangle="180", # spreadsheet cells can arrive as text oscillation=0.1, exposure=0.02, ) @@ -64,6 +64,14 @@ def test_sample_parameters_are_empty_without_a_sample(): assert SampleParameters.from_sample(None) == SampleParameters() +def test_a_column_aaredb_does_not_have_is_ignored(): + # aareDB owns these names: a renamed column must leave the panel on its + # defaults, not break the status loop it is read from. + params = types.SimpleNamespace(targetresolution=1.5) + sample = cast(Any, types.SimpleNamespace(aaredb_params=params)) + assert SampleParameters.from_sample(sample) == SampleParameters(resolution_a=1.5) + + # --------------------------------------------------------------------------- # Rotation panel # ---------------------------------------------------------------------------