fix: read the aareDB total angle from the column that exists
CI / lint (push) Skipped
CI / test (3.12) (push) Skipped
CI / test (3.13) (push) Skipped
CI / test-with-beamline-plugins (pxi_bec) (push) Skipped
CI / test-with-beamline-plugins (pxii_bec) (push) Skipped
CI / test-with-beamline-plugins (pxiii_bec) (push) Skipped

The column is totalangle, not totalrange, so reading it raised
AttributeError four times a second in the status loop.

Reading a spreadsheet cell now goes through _spreadsheet_float, which
treats a missing column, an empty cell and a cell that does not hold a
number all as "not set" and logs the column once. aareDB owns these
names; the GUI should fall back to its defaults when one moves, not die
on the status loop it is read from.

Note that the same misspelling is still in daq.py (spreadsheet_params
and get_auto_raster_params), where getattr's default hides it: automation
has never picked up the total angle from the spreadsheet either.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JHoUkj66jxByS2ypY5h9Mn
This commit is contained in:
2026-09-07 16:54:16 +02:00
co-authored by Claude Opus 5
parent fd4b9b3122
commit 85e0609aa4
2 changed files with 32 additions and 9 deletions
+23 -8
View File
@@ -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"),
)
@@ -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
# ---------------------------------------------------------------------------