diff --git a/src/chexus/validators.py b/src/chexus/validators.py index 951cb29..9868155 100644 --- a/src/chexus/validators.py +++ b/src/chexus/validators.py @@ -50,7 +50,7 @@ def validate(self, node: Dataset | Group) -> Violation | None: if name not in start.children: return Violation(node.name, f"depends_on target {target} is missing") start = start.children[name] - if not is_transformation(start): + if not is_complete_transformation(start): return Violation( node.name, f"depends_on target {target} is not a transformation" ) @@ -217,6 +217,15 @@ def is_transformation(node: Dataset | Group) -> bool: return "transformation_type" in node.attrs and "vector" in node.attrs +def is_complete_transformation(node: Dataset | Group) -> bool: + return ( + is_transformation(node) + and "depends_on" in node.attrs + # Check that the transformation is type NX_NUMBER + and (isinstance(node, Dataset) or node.attrs.get('NX_class') == 'NXlog') + ) + + class transformation_offset_units_missing(Validator): def __init__(self) -> None: super().__init__( @@ -378,7 +387,7 @@ def validate(self, node: Dataset | Group) -> Violation | None: if not np.isin(event_ids, detector_numbers).all(): diff = np.setdiff1d(event_ids, detector_numbers) return Violation( - node.name, 'event_id:s that are not in detector_number: ' f'{diff}' + node.name, f'event_id:s that are not in detector_number: {diff}' ) @@ -533,6 +542,26 @@ def validate(self, node: Group | Dataset) -> Violation | None: return Violation(node.name, "NXevent_data must have event dataset(s)") +class children_of_nxtransformations_are_nxnumber(Validator): + def __init__(self) -> None: + super().__init__( + "NXtransformations_child_not_NX_NUMBER", + "children of NXtransformations must be NX_NUMBER", + ) + + def applies_to(self, node: Dataset | Group) -> bool: + return ( + node.parent is not None + and node.parent.attrs.get("NX_class") == "NXtransformations" + ) + + def validate(self, node: Dataset | Group) -> Violation | None: + if not (isinstance(node, Dataset) or node.attrs.get("NX_class") == "NXlog"): + return Violation( + node.name, "child of NXtransformation is not NXlog or dataset" + ) + + def base_validators(*, has_scipp=True): validators = [ depends_on_missing(), @@ -554,6 +583,7 @@ def base_validators(*, has_scipp=True): NXdetector_pixel_offsets_are_unambiguous(), event_index_is_eight_bytes(), event_data_group_has_event_datasets(), + children_of_nxtransformations_are_nxnumber(), ] if has_scipp: validators += [ diff --git a/tests/validators_test.py b/tests/validators_test.py index f2f070e..1c45bfe 100644 --- a/tests/validators_test.py +++ b/tests/validators_test.py @@ -57,6 +57,30 @@ def test_depends_on_target_missing(): assert result.name == "x/transform" +def test_depends_on_target_requires_depends_on_attribute(): + group = chexus.Group(name="x", attrs={"NX_class": "NXdetector"}) + depends_on = chexus.Dataset( + name="x/depends_on", value="transform", shape=None, dtype=str, parent=group + ) + transform = chexus.Dataset( + name="x/transform", + value=None, + shape=None, + dtype=float, + parent=group, + attrs={ + "transformation_type": "translation", + "vector": [1.0, 0.0, 0.0], + }, + ) + group.children = {"depends_on": depends_on, "transform": transform} + + result = chexus.validators.depends_on_target_missing().validate(depends_on) + + assert isinstance(result, chexus.Violation) + assert result.name == "x/depends_on" + + def test_float_dataset_units_missing(): good = chexus.Dataset( name="x", @@ -905,3 +929,32 @@ def test_event_data_has_event_datasets_bad_empty() -> None: chexus.validators.event_data_group_has_event_datasets().validate(parent), chexus.Violation, ) + + +def test_children_of_nxtransformations_are_nxnumber() -> None: + parent = chexus.Group( + name="transformations", attrs={"NX_class": "NXtransformations"} + ) + dataset = chexus.Dataset( + name="transformations/dataset", + value=1.0, + shape=None, + dtype=float, + parent=parent, + ) + good = chexus.Group( + name="transformations/good", attrs={"NX_class": "NXlog"}, parent=parent + ) + bad = chexus.Group( + name="transformations/bad", attrs={"NX_class": "NXcollection"}, parent=parent + ) + + validator = chexus.validators.children_of_nxtransformations_are_nxnumber() + + assert not validator.applies_to(parent) + assert validator.applies_to(dataset) + assert validator.validate(dataset) is None + assert validator.applies_to(good) + assert validator.validate(good) is None + assert validator.applies_to(bad) + assert isinstance(validator.validate(bad), chexus.Violation)