From 9d8ea0a36ed9fca4a352d13585a019ba1732cf63 Mon Sep 17 00:00:00 2001 From: Ollie Copping Date: Fri, 30 Jan 2026 11:55:22 +0000 Subject: [PATCH 1/3] Change devsta to status in Component model --- src/techui_builder/models.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/techui_builder/models.py b/src/techui_builder/models.py index 8a1fc60c..f42fcfdc 100644 --- a/src/techui_builder/models.py +++ b/src/techui_builder/models.py @@ -106,7 +106,7 @@ class Component(BaseModel): extras: list[str] | None = None file: str | None = None macros: dict[str, str | int | float] | None = None - devsta: list[str] | None = None + status: list[str] | None = None model_config = ConfigDict( # Makes sure that 'macros' is only allowed if 'file' is present # (this is required for VSCode checks) @@ -148,9 +148,9 @@ def _check_extras(cls, v: list[str]) -> list[str]: raise ValueError("extras must contain unique items") return v - @field_validator("devsta") + @field_validator("status") @classmethod - def _check_devsta(cls, v: list[str]) -> list[str]: + def _check_status(cls, v: list[str]) -> list[str]: for p in v: # split up prefix and database link flags (if they exist) m = re.match(r"([\w:.-]+)[ ]?([\w ]*)", p) @@ -160,17 +160,17 @@ def _check_devsta(cls, v: list[str]) -> list[str]: if not _DLS_PREFIX_RE.match(prefix): raise ValueError( - f"devsta item '{p}' does not match extended DLS prefix pattern" + f"status PV '{p}' does not match extended DLS prefix pattern" ) if flags is not None: if not _DATABASE_FLAGS_RE.match(flags): raise ValueError( - f"devsta item '{p}' does have valid database link flags" + f"status PV '{p}' does have valid database link flags" ) # ensure unique (schema enforces too) if len(set(v)) != len(v): - raise ValueError("devsta must contain unique items") + raise ValueError("status must contain unique items") return v @computed_field(repr=False, return_type=str | None) From bdda88b2bdd11d98686dff63d56e2e917d49a84a Mon Sep 17 00:00:00 2001 From: Ollie Copping Date: Fri, 30 Jan 2026 13:25:50 +0000 Subject: [PATCH 2/3] Rework logic to convert devsta to status --- src/techui_builder/__main__.py | 2 +- src/techui_builder/autofill.py | 2 +- src/techui_builder/builder.py | 22 +++++++++++----------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/techui_builder/__main__.py b/src/techui_builder/__main__.py index 9ed744b4..db92748a 100644 --- a/src/techui_builder/__main__.py +++ b/src/techui_builder/__main__.py @@ -156,7 +156,7 @@ def main( gui.setup() gui.create_screens() - gui.write_devsta_pvs() + gui.write_status_pvs() logger_.info(f"Screens generated for {gui.conf.beamline.short_dom}.") diff --git a/src/techui_builder/autofill.py b/src/techui_builder/autofill.py index 85213d49..f2b9b9ad 100644 --- a/src/techui_builder/autofill.py +++ b/src/techui_builder/autofill.py @@ -78,7 +78,7 @@ def replace_content( match macro: case "prefix": tag_name = "pv_name" - component_attr = f"{component.P}:DEVSTA" + component_attr = f"{component.P}:STA" case "desc" | "file" | "macros": # Get current component attribute diff --git a/src/techui_builder/builder.py b/src/techui_builder/builder.py index a1247ca3..95811a54 100644 --- a/src/techui_builder/builder.py +++ b/src/techui_builder/builder.py @@ -48,7 +48,7 @@ class Builder: entities: defaultdict[str, list[Entity]] = field( default_factory=lambda: defaultdict(list), init=False ) - devsta_pvs: dict[str, Record] = field(default_factory=dict, init=False) + status_pvs: dict[str, Record] = field(default_factory=dict, init=False) _services_dir: Path = field(init=False, repr=False) _gui_map: dict = field(init=False, repr=False) _write_directory: Path = field(default=Path("opis"), init=False, repr=False) @@ -100,13 +100,13 @@ def clean_files(self): logger_.debug(f"Removing generated file: {file_.name}") os.remove(file_) - def _create_devsta_pv(self, prefix: str, inputs: list[str]): + def _create_status_pv(self, prefix: str, inputs: list[str]): # Extract all input PVs, provided a default "" if not provided values = [(inputs[i] if i < len(inputs) else "") for i in range(12)] inpa, inpb, inpc, inpd, inpe, inpf, inpg, inph, inpi, inpj, inpk, inpl = values - devsta_pv = records.calc( # pyright: ignore[reportAttributeAccessIssue] - f"{prefix}:DEVSTA", + status_pv = records.calc( # pyright: ignore[reportAttributeAccessIssue] + f"{prefix}:STA", CALC="(A|B|C|D|E|F|G|H|I|J|K|L)>0?1:0", SCAN="1 second", ACKT="NO", @@ -124,16 +124,16 @@ def _create_devsta_pv(self, prefix: str, inputs: list[str]): INPL=inpl, ) - self.devsta_pvs[prefix] = devsta_pv + self.status_pvs[prefix] = status_pv - def write_devsta_pvs(self): + def write_status_pvs(self): conf_dir = self._write_directory.joinpath("config") # Create the config/ dir if it doesn't exist if not conf_dir.exists(): os.mkdir(conf_dir) - with open(conf_dir.joinpath("devsta.db"), "w") as f: + with open(conf_dir.joinpath("status.db"), "w") as f: # Add a header explaining the file is autogenerated f.write("#" * 51 + "\n") f.write( @@ -144,8 +144,8 @@ def write_devsta_pvs(self): ) f.write("#" * 51 + "\n") - # Write the devsta PVs - for dpv in self.devsta_pvs.values(): + # Write the status PVs + for dpv in self.status_pvs.values(): dpv.Print(f) def _extract_services(self): @@ -207,8 +207,8 @@ def create_screens(self): for component_name, component in self.conf.components.items(): screen_entities: list[Entity] = [] - if component.devsta is not None: - self._create_devsta_pv(component.prefix, component.devsta) + if component.status is not None: + self._create_status_pv(component.prefix, component.status) # ONLY IF there is a matching component and entity, generate a screen if component.prefix in self.entities.keys(): From 38a85cfd06fae2c5b9a00fdca3f8cd6a8501045f Mon Sep 17 00:00:00 2001 From: Ollie Copping Date: Fri, 30 Jan 2026 13:31:53 +0000 Subject: [PATCH 3/3] Rework tests to look at status instead of devsta --- tests/test_autofiller.py | 2 +- tests/test_builder.py | 42 ++++++++++++++++++++-------------------- tests/test_models.py | 6 +++--- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/tests/test_autofiller.py b/tests/test_autofiller.py index 3afb5bc6..1f702802 100644 --- a/tests/test_autofiller.py +++ b/tests/test_autofiller.py @@ -101,7 +101,7 @@ def test_autofiller_replace_content( fake_component, ) - assert example_related_widget.pv_name == f"{prefix}:DEVSTA" + assert example_related_widget.pv_name == f"{prefix}:STA" assert example_related_widget.actions.action.description.text == expected_desc assert example_related_widget.actions.action.file.text == expected_file if macros is not None: diff --git a/tests/test_builder.py b/tests/test_builder.py index d0cd9730..25d86999 100644 --- a/tests/test_builder.py +++ b/tests/test_builder.py @@ -69,13 +69,13 @@ def test_component_attributes( assert component.extras == extras -def test_builder_create_devsta_pv(builder): +def test_builder_create_status_pv(builder): p = "BL01T-MO-MOTOR-01" inpa = "BL01T-MO-MOTOR-01:MOTOR1.MOVN" - builder._create_devsta_pv(prefix=p, inputs=[inpa]) + builder._create_status_pv(prefix=p, inputs=[inpa]) - devsta_pv = """ -record(calc, "BL01T-MO-MOTOR-01:DEVSTA") + status_pv = """ +record(calc, "BL01T-MO-MOTOR-01:STA") { field(ACKT, "NO") field(CALC, "(A|B|C|D|E|F|G|H|I|J|K|L)>0?1:0") @@ -95,44 +95,44 @@ def test_builder_create_devsta_pv(builder): } """ - assert builder.devsta_pvs != {} + assert builder.status_pvs != {} # Fake file-like object to "print" the record to - auto_devsta_pv = StringIO() + auto_status_pv = StringIO() # Get the string representation of the record - builder.devsta_pvs[p].Print(auto_devsta_pv) + builder.status_pvs[p].Print(auto_status_pv) - assert auto_devsta_pv.getvalue() == devsta_pv + assert auto_status_pv.getvalue() == status_pv # Make sure the record is deleted ClearRecords() -def test_builder_write_devsta_pvs(builder): - # To mock the open() function used in _write_devsta_pvs +def test_builder_write_status_pvs(builder): + # To mock the open() function used in _write_status_pvs m = mock_open() p = "BL01T-MO-MOTOR-01" inpa = "BL01T-MO-MOTOR-01:MOTOR1.MOVN" - devsta_pv = records.calc( # pyright: ignore[reportAttributeAccessIssue] - f"{p}:DEVSTA", + status_pv = records.calc( # pyright: ignore[reportAttributeAccessIssue] + f"{p}:STA", CALC="(A|B|C|D|E|F|G|H|I|J|K|L)>0?1:0", SCAN="1 second", ACKT="NO", INPA=inpa, ) - builder.devsta_pvs[p] = devsta_pv + builder.status_pvs[p] = status_pv # Mock the Print() function so we don't actually write a file with ( patch("builtins.open", m), patch("techui_builder.builder.Record.Print") as mock_print, ): - builder.write_devsta_pvs() + builder.write_status_pvs() # Check open() was called with the correct args m.assert_called_once_with( - Path(builder._write_directory.joinpath("config/devsta.db")), + Path(builder._write_directory.joinpath("config/status.db")), "w", ) mock_print.assert_called_once() @@ -208,8 +208,8 @@ def test_builder_validate_screen(builder_with_setup): def test_create_screens(builder_with_setup): - # We don't want to make a devsta PV in this test - builder_with_setup._create_devsta_pv = Mock() + # We don't want to make a status PV in this test + builder_with_setup._create_status_pv = Mock() # We don't want to access Generator in this test builder_with_setup._generate_screen = Mock() builder_with_setup._validate_screen = Mock() @@ -221,8 +221,8 @@ def test_create_screens(builder_with_setup): def test_create_screens_no_entities(builder, caplog): - # We don't want to make a devsta PV in this test - builder._create_devsta_pv = Mock() + # We don't want to make a status PV in this test + builder._create_status_pv = Mock() builder.entities = [] @@ -236,8 +236,8 @@ def test_create_screens_no_entities(builder, caplog): def test_create_screens_extra_p_does_not_exist(builder_with_setup, caplog): - # We don't want to make a devsta PV in this test - builder_with_setup._create_devsta_pv = Mock() + # We don't want to make a status PV in this test + builder_with_setup._create_status_pv = Mock() # We don't want to actually generate a screen builder_with_setup._generate_screen = Mock(side_effect=None) diff --git a/tests/test_models.py b/tests/test_models.py index 692d4149..3a626338 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -21,7 +21,7 @@ def beamline() -> Beamline: @pytest.fixture def component() -> Component: return Component( - prefix="BL01T-EA-TEST-02", desc="Test Device", devsta=["BL01T-MO-MOTOR-01:Y"] + prefix="BL01T-EA-TEST-02", desc="Test Device", status=["BL01T-MO-MOTOR-01:Y"] ) @@ -46,14 +46,14 @@ def test_component_object(component: Component): assert component.P == "BL01T-EA-TEST-02" assert component.R is None assert component.attribute is None - assert component.devsta == ["BL01T-MO-MOTOR-01:Y"] + assert component.status == ["BL01T-MO-MOTOR-01:Y"] def test_component_repr(component: Component): assert ( str(component) == "prefix='BL01T-EA-TEST-02' desc='Test Device' extras=None\ - file=None macros=None devsta=['BL01T-MO-MOTOR-01:Y']" + file=None macros=None status=['BL01T-MO-MOTOR-01:Y']" )