diff --git a/src/maus/models/anwendungshandbuch.py b/src/maus/models/anwendungshandbuch.py index f3160f46..23e62b78 100644 --- a/src/maus/models/anwendungshandbuch.py +++ b/src/maus/models/anwendungshandbuch.py @@ -10,10 +10,11 @@ from typing import Callable, List, Optional, Sequence from uuid import UUID +import attr.validators import attrs from marshmallow import Schema, fields, post_load # type:ignore[import] -from maus.models.edifact_components import Segment, SegmentGroup, SegmentGroupSchema +from maus.models.edifact_components import DataElementFreeText, Segment, SegmentGroup, SegmentGroupSchema # pylint:disable=too-many-instance-attributes @@ -250,6 +251,24 @@ def deserialize(self, data, **kwargs) -> FlatAnwendungshandbuch: return FlatAnwendungshandbuch(**data) +@attrs.define(auto_attribs=True, kw_only=True) +class DeepAhbInputReplacement: + """ + A container class that models replacements of inputs in the DeepAnwendungshandbuch + """ + + #: true iff a replacement is applicable + replacement_found: bool = attrs.field(validator=attrs.validators.instance_of(bool)) + input_replacement: Optional[str] = attrs.field( + validator=attrs.validators.optional(attr.validators.instance_of(str)) + ) + """ + The replacement for entered_input itself. Note that the replacement may be None even if a replacement is found. + This implies, that you must always check for replacement_found is True first and then, iff true, replace with the + replacement, even if it may be None/null. + """ + + @attrs.define(auto_attribs=True, kw_only=True) class DeepAnwendungshandbuch: """ @@ -310,6 +329,32 @@ def find_segments( result += segment_group.find_segments(segment_predicate) return result + def replace_inputs_based_on_discriminator(self, replacement_func: Callable[[str], DeepAhbInputReplacement]) -> None: + """ + Replace all the entered_inputs in the entire DeepAnwendungshandbuch using the given replacement_func. + Note that this modifies this DeepAnwendungshandbuch instance (self). + """ + _replace_inputs_based_on_discriminator(self.lines, replacement_func) + + +def _replace_inputs_based_on_discriminator( + segment_groups: List[SegmentGroup], replacement_func: Callable[[str], DeepAhbInputReplacement] +) -> None: + """ + Replace all the entered_inputs in the entire list of segment groups using the given replacement_func. + """ + for segment_group in segment_groups: + if segment_group.segment_groups is not None: + _replace_inputs_based_on_discriminator(segment_group.segment_groups, replacement_func) + if segment_group.segments is None: + continue + for segment in segment_group.segments: + for data_element in segment.data_elements: + if isinstance(data_element, DataElementFreeText): + replacement_result = replacement_func(data_element.discriminator) + if replacement_result.replacement_found is True: + data_element.entered_input = replacement_result.input_replacement + class DeepAnwendungshandbuchSchema(Schema): """ diff --git a/tests/unit_tests/test_ahb.py b/tests/unit_tests/test_ahb.py index 777cb3b2..41d49e5b 100644 --- a/tests/unit_tests/test_ahb.py +++ b/tests/unit_tests/test_ahb.py @@ -9,6 +9,7 @@ AhbLineSchema, AhbMetaInformation, AhbMetaInformationSchema, + DeepAhbInputReplacement, DeepAnwendungshandbuch, DeepAnwendungshandbuchSchema, FlatAnwendungshandbuch, @@ -639,3 +640,141 @@ def test_sorted_segment_groups( ): actual = FlatAnwendungshandbuch._sorted_lines_by_segment_groups(unsorted_input, sg_order) assert actual == expected_result + + @pytest.mark.parametrize( + "original,expected", + [ + pytest.param( + DeepAnwendungshandbuch( + meta=AhbMetaInformation(pruefidentifikator="11042"), + lines=[ + SegmentGroup( + ahb_expression="expr A", + discriminator="disc A", + segments=[ + Segment( + ahb_expression="expr B", + discriminator="disc B", + section_name="foo", + data_elements=[ + DataElementFreeText( + ahb_expression="Muss [1]", + entered_input="Hello Maus", + discriminator="foo", + data_element_id="4567", + ), + ], + ), + ], + segment_groups=[ + SegmentGroup( + discriminator="disc C", + ahb_expression="expr C", + segments=[ + Segment( + section_name="bar", + ahb_expression="expr Y", + discriminator="disc Y", + data_elements=[ + DataElementFreeText( + ahb_expression="Muss [1]", + entered_input="Hello Elefant", + discriminator="abc", + data_element_id="4567", + ), + DataElementFreeText( + ahb_expression="Muss [1]", + entered_input="this should stay untouched", + discriminator="qwert", + data_element_id="4567", + ), + DataElementFreeText( + ahb_expression="Muss [1]", + entered_input="Not none", + discriminator="replace_with_none_here", + data_element_id="4567", + ), + ], + ) + ], + segment_groups=None, + ), + ], + ), + ], + ), + DeepAnwendungshandbuch( + meta=AhbMetaInformation(pruefidentifikator="11042"), + lines=[ + SegmentGroup( + ahb_expression="expr A", + discriminator="disc A", + segments=[ + Segment( + ahb_expression="expr B", + discriminator="disc B", + section_name="foo", + data_elements=[ + DataElementFreeText( + ahb_expression="Muss [1]", + entered_input="bar", + discriminator="foo", + data_element_id="4567", + ), + ], + ), + ], + segment_groups=[ + SegmentGroup( + discriminator="disc C", + ahb_expression="expr C", + segments=[ + Segment( + section_name="bar", + ahb_expression="expr Y", + discriminator="disc Y", + data_elements=[ + DataElementFreeText( + ahb_expression="Muss [1]", + entered_input="xyz", + discriminator="abc", + data_element_id="4567", + ), + DataElementFreeText( + ahb_expression="Muss [1]", + entered_input="this should stay untouched", + discriminator="qwert", + data_element_id="4567", + ), + DataElementFreeText( + ahb_expression="Muss [1]", + entered_input=None, + discriminator="replace_with_none_here", + data_element_id="4567", + ), + ], + ) + ], + segment_groups=None, + ), + ], + ), + ], + ), + ) + ], + ) + def test_replacing_data_element_inputs(self, original: DeepAnwendungshandbuch, expected: DeepAnwendungshandbuch): + assert original != expected + + def replacement_func(discriminator: str) -> DeepAhbInputReplacement: + if discriminator == "foo": + return DeepAhbInputReplacement(replacement_found=True, input_replacement="bar") + if discriminator == "abc": + return DeepAhbInputReplacement(replacement_found=True, input_replacement="xyz") + if discriminator == "replace_with_none_here": + return DeepAhbInputReplacement(replacement_found=True, input_replacement=None) + return DeepAhbInputReplacement(replacement_found=False, input_replacement=None) + + original.replace_inputs_based_on_discriminator(replacement_func) + assert original == expected