- set_sample_measured(..., account=...) stores the e-account of the status change in <slot>:MEAS_ACCOUNT; OWNER is no longer filled with the account (owner is free text now). Callers in omny.py/flomni.py pass bec.active_account. The account moves with the sample (transfer_sample) and is reset on load/clear. - OMNY: status 'screened' when a sample leaves the sample stage (stage -> gripper) with status 'none', i.e. no tomoscan was started on it (Mirko 2026-10-27). - widget: shows 'screened'; the P-touch label prints MEAS_ACCOUNT (owner as fallback for older records); CLI views show the account. - IOCs: OMNY ioc_omny.py / flOMNI ioc_flomni.py got the MEAS_ACCOUNT field (and 'screened' appended to the OMNY status enum), deployed on mpc3217 / mpc2844. - tests/e2e harness updated (owner_if_empty replaced by account). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M8zoDvfTY6VYunNtWse9VZ
109 lines
4.6 KiB
Python
109 lines
4.6 KiB
Python
"""OMNYSampleStorage on the OMNY soft IOC records (OMNY-SAMPLE:<slot>:USED/NAME/OWNER/STATUS/MEAS_DATE/MEAS_ACCOUNT)."""
|
|
|
|
import pytest
|
|
|
|
from csaxs_bec.devices.omny.omny_sample_storage import (
|
|
SAMPLE_SLOTS,
|
|
OMNYSampleStorage,
|
|
OMNYSampleStorageError,
|
|
slot_name,
|
|
)
|
|
from csaxs_bec.devices.sim.sim_omny import SimOMNYSampleStorage
|
|
|
|
|
|
@pytest.fixture
|
|
def storage():
|
|
dev = SimOMNYSampleStorage(name="omny_samples", sim_samples={})
|
|
try:
|
|
yield dev
|
|
finally:
|
|
dev.destroy()
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"container, slot_nr, expected",
|
|
[("A", 1, "A1"), ("C", 6, "C6"), ("O", 10, "P10"), ("O", 101, "P101"), ("STAGE", 0, "STAGE"),
|
|
("gripper", 0, "GRIPPER"), ("B4", 0, "B4"), ("P32", 0, "P32")],
|
|
)
|
|
def test_slot_name(container, slot_nr, expected):
|
|
assert slot_name(container, slot_nr) == expected
|
|
|
|
|
|
@pytest.mark.parametrize("container, slot_nr", [("A", 0), ("A", 7), ("O", 15), ("O", 1), ("D", 1)])
|
|
def test_slot_name_rejects_unknown_slots(container, slot_nr):
|
|
with pytest.raises(OMNYSampleStorageError):
|
|
slot_name(container, slot_nr)
|
|
|
|
|
|
def test_pv_names_match_the_ioc():
|
|
assert OMNYSampleStorage.sample_used.defn["A1"][1] == "OMNY-SAMPLE:A1:USED"
|
|
assert OMNYSampleStorage.sample_names.defn["P100"][1] == "OMNY-SAMPLE:P100:NAME"
|
|
assert OMNYSampleStorage.sample_status.defn["STAGE"][1] == "OMNY-SAMPLE:STAGE:STATUS"
|
|
assert OMNYSampleStorage.sample_meas_date.defn["GRIPPER"][1] == "OMNY-SAMPLE:GRIPPER:MEAS_DATE"
|
|
assert OMNYSampleStorage.sample_meas_account.defn["A3"][1] == "OMNY-SAMPLE:A3:MEAS_ACCOUNT"
|
|
assert OMNYSampleStorage.parking_names.defn["parking3"][1] == "OMNY-SAMPLE:PARK3:NAME"
|
|
assert len(SAMPLE_SLOTS) == 18 + 10 + 2
|
|
|
|
|
|
def test_set_and_unset_slot(storage):
|
|
storage.set_sample_slot("O", 32, "pin1", "e12345")
|
|
assert storage.get_sample_record("O", 32) == {
|
|
"used": True, "name": "pin1", "owner": "e12345", "status": "none", "meas_date": "",
|
|
"meas_account": ""}
|
|
storage.unset_sample_slot("O", 32)
|
|
rec = storage.get_sample_record("O", 32)
|
|
assert rec["used"] is False and rec["name"] == "-" and rec["owner"] == ""
|
|
|
|
|
|
def test_status_travels_with_the_sample(storage):
|
|
storage.set_sample_slot("B", 2, "pin2", "e11111")
|
|
storage.set_sample_measured("B", 2, "completed", "2026-10", account="e20001")
|
|
storage.transfer_sample(("B", 2), "GRIPPER")
|
|
storage.transfer_sample("GRIPPER", "STAGE")
|
|
assert not storage.is_sample_slot_used("B", 2)
|
|
assert not storage.is_sample_in_gripper()
|
|
assert storage.get_sample_record("STAGE") == {
|
|
"used": True, "name": "pin2", "owner": "e11111", "status": "completed", "meas_date": "2026-10",
|
|
"meas_account": "e20001"}
|
|
|
|
|
|
def test_set_sample_measured_stores_the_account_not_the_owner(storage):
|
|
# owner (free text) and measurement account are separate fields (2026-10-27)
|
|
storage.set_sample_in_samplestage("pin3", "Group XY")
|
|
storage.set_sample_measured("STAGE", 0, "started", "2026-10", account="e22222")
|
|
assert storage.get_sample_owner_in_samplestage() == "Group XY"
|
|
assert storage.get_sample_status_in_samplestage() == "started"
|
|
assert storage.get_sample_meas_account("STAGE") == "e22222"
|
|
# the account of the latest status change is stored
|
|
storage.set_sample_measured("STAGE", 0, "completed", "2026-10", account="e33333")
|
|
assert storage.get_sample_meas_account("STAGE") == "e33333"
|
|
# no account given: the stored one is kept
|
|
storage.set_sample_measured("STAGE", 0, "completed", "2026-11")
|
|
assert storage.get_sample_meas_account("STAGE") == "e33333"
|
|
# loading a new sample resets status, date and account
|
|
storage.set_sample_in_samplestage("pin3b", "Group XY")
|
|
assert storage.get_sample_record("STAGE")["meas_account"] == ""
|
|
|
|
|
|
def test_screened_status(storage):
|
|
storage.set_sample_slot("C", 4, "pin5", "someone")
|
|
storage.set_sample_measured("C", 4, "screened", "2026-10", account="e44444")
|
|
assert storage.get_sample_status("C", 4) == "screened"
|
|
assert storage.get_sample_meas_account("C", 4) == "e44444"
|
|
|
|
|
|
def test_invalid_status_raises(storage):
|
|
storage.set_sample_slot("A", 1, "pin4")
|
|
with pytest.raises(OMNYSampleStorageError):
|
|
storage.set_sample_measured("A", 1, "bogus", "")
|
|
|
|
|
|
def test_parking(storage):
|
|
storage.set_shuttle_slot("C", 2)
|
|
assert storage.is_shuttle_slot_used(2) and storage.get_shuttle_name_slot(2) == "C"
|
|
assert storage.search_shuttle_in_slot("C") == 2
|
|
storage.unset_shuttle_slot(2)
|
|
assert not storage.is_shuttle_slot_used(2) and storage.get_shuttle_name_slot(2) == "none"
|
|
with pytest.raises(OMNYSampleStorageError):
|
|
storage.set_shuttle_slot("A", 7)
|