Index: DamClients/DamPythonInterface/trunk/src/tests/test_data/test_serialize_surface_line.xml =================================================================== diff -u --- DamClients/DamPythonInterface/trunk/src/tests/test_data/test_serialize_surface_line.xml (revision 0) +++ DamClients/DamPythonInterface/trunk/src/tests/test_data/test_serialize_surface_line.xml (revision 3474) @@ -0,0 +1,10 @@ + + + + + + + + + + Index: DamClients/DamPythonInterface/trunk/src/tests/test_surface_line.py =================================================================== diff -u --- DamClients/DamPythonInterface/trunk/src/tests/test_surface_line.py (revision 0) +++ DamClients/DamPythonInterface/trunk/src/tests/test_surface_line.py (revision 3474) @@ -0,0 +1,149 @@ +from typing import List +from dampythoninterface.surface_line import Point, PointTypeEnum, SurfaceLine +from .utils import TestUtils + +import pytest +from pathlib import Path +from lxml import etree + + +class TestSurfaceLine: + @pytest.mark.unittest + def test_surface_line_error_raised(self): + # set up test data + list_points = [ + Point(X=i, Z=2 * i, PointType=PointTypeEnum.NONE) for i in range(10) + ] + # run test + expected_mssg = "Set Characteristic points Test surface line is not complete." + with pytest.raises(ValueError, match=expected_mssg): + SurfaceLine(Points=list_points, Name="Test surface line") + + @pytest.mark.unittest + @pytest.mark.parametrize( + "points_available, error_message", + [ + ( + [ + PointTypeEnum.DikeToeAtRiver, + PointTypeEnum.DikeTopAtRiver, + PointTypeEnum.DikeTopAtPolder, + PointTypeEnum.DikeToeAtPolder, + PointTypeEnum.SurfaceLevelInside, + ], + "SurfaceLevelOutside", + ), + ( + [ + PointTypeEnum.SurfaceLevelOutside, + PointTypeEnum.DikeTopAtRiver, + PointTypeEnum.DikeTopAtPolder, + PointTypeEnum.DikeToeAtPolder, + PointTypeEnum.SurfaceLevelInside, + ], + "DikeToeAtRiver", + ), + ( + [ + PointTypeEnum.SurfaceLevelOutside, + PointTypeEnum.DikeToeAtRiver, + PointTypeEnum.DikeTopAtPolder, + PointTypeEnum.DikeToeAtPolder, + PointTypeEnum.SurfaceLevelInside, + ], + "DikeTopAtRiver", + ), + ( + [ + PointTypeEnum.SurfaceLevelOutside, + PointTypeEnum.DikeToeAtRiver, + PointTypeEnum.DikeTopAtRiver, + PointTypeEnum.DikeToeAtPolder, + PointTypeEnum.SurfaceLevelInside, + ], + "DikeTopAtPolder", + ), + ( + [ + PointTypeEnum.SurfaceLevelOutside, + PointTypeEnum.DikeToeAtRiver, + PointTypeEnum.DikeTopAtRiver, + PointTypeEnum.DikeTopAtPolder, + PointTypeEnum.SurfaceLevelInside, + ], + "DikeToeAtPolder", + ), + ( + [ + PointTypeEnum.SurfaceLevelOutside, + PointTypeEnum.DikeToeAtRiver, + PointTypeEnum.DikeTopAtRiver, + PointTypeEnum.DikeTopAtPolder, + PointTypeEnum.DikeToeAtPolder, + ], + "SurfaceLevelInside", + ), + ], + ) + def test_surface_line_error_raised_per_point( + self, points_available: List, error_message: str + ): + list_of_points = [ + Point(X=1, Z=2, PointType=point_type) for point_type in points_available + ] + with pytest.raises(ValueError) as e_info: + SurfaceLine(Points=list_of_points, Name="Test surface line") + generated_error_message = str(e_info.value) + assert error_message in generated_error_message + + @pytest.mark.unittest + def test_create_surface_line(self): + points_available = [ + PointTypeEnum.SurfaceLevelOutside, + PointTypeEnum.DikeToeAtRiver, + PointTypeEnum.DikeTopAtRiver, + PointTypeEnum.DikeTopAtPolder, + PointTypeEnum.DikeToeAtPolder, + PointTypeEnum.SurfaceLevelInside, + ] + list_of_points = [ + Point(X=1 * counter, Z=2 * counter, PointType=point_type) + for counter, point_type in enumerate(points_available) + ] + surfaceline = SurfaceLine(Points=list_of_points, Name="Test surface line") + assert surfaceline.Name == "Test surface line" + + @pytest.mark.integrationtest + def test_serialize_surface_line(self): + # Initialize class to be tested + points_available = [ + PointTypeEnum.SurfaceLevelOutside, + PointTypeEnum.DikeToeAtRiver, + PointTypeEnum.DikeTopAtRiver, + PointTypeEnum.DikeTopAtPolder, + PointTypeEnum.DikeToeAtPolder, + PointTypeEnum.SurfaceLevelInside, + ] + list_of_points = [ + Point(X=1 * counter, Z=2 * counter, PointType=point_type) + for counter, point_type in enumerate(points_available) + ] + surfaceline = SurfaceLine(Points=list_of_points, Name="Test surface line") + # check initial expectations + assert surfaceline.Name == "Test surface line" + # run test + root = surfaceline.serialize() + # test output + xml_output = Path( + TestUtils.get_output_test_data_dir(""), "test_serialize_surface_line.xml" + ) + tree = etree.ElementTree(root) + tree.write(str(xml_output), pretty_print=True) + # get template file + xml_test_data = Path( + TestUtils.get_test_data_dir("", "test_data"), + "test_serialize_surface_line.xml", + ) + # Line by line comparison + for output_file, test_file in zip(open(xml_output), open(xml_test_data)): + assert output_file.strip() == test_file.strip() Index: DamClients/DamPythonInterface/trunk/src/tests/test_input.py =================================================================== diff -u --- DamClients/DamPythonInterface/trunk/src/tests/test_input.py (revision 0) +++ DamClients/DamPythonInterface/trunk/src/tests/test_input.py (revision 3474) @@ -0,0 +1,41 @@ +import pytest +from pathlib import Path +from lxml import etree + +from dampythoninterface.input import DamInput +from dampythoninterface.surface_line import SurfaceLine, PointTypeEnum, Point +from .utils import TestUtils + + +class TestDamInput: + @pytest.mark.integrationtest + def test_ExportToXml(self): + # Create surface lines + points_available = [ + PointTypeEnum.SurfaceLevelOutside, + PointTypeEnum.DikeToeAtRiver, + PointTypeEnum.DikeTopAtRiver, + PointTypeEnum.DikeTopAtPolder, + PointTypeEnum.DikeToeAtPolder, + PointTypeEnum.SurfaceLevelInside, + ] + list_of_points = [ + Point(X=1 * counter, Z=2 * counter, PointType=point_type) + for counter, point_type in enumerate(points_available) + ] + surfaceline_1 = SurfaceLine(Points=list_of_points, Name="Surface line 1") + surfaceline_2 = SurfaceLine(Points=list_of_points, Name="Surface line 2") + # check initial expectations + assert surfaceline_1.Name == "Surface line 1" + assert surfaceline_2.Name == "Surface line 2" + list_of_surface_lines = [surfaceline_1, surfaceline_2] + # Make input class + model = DamInput(SurfaceLines=list_of_surface_lines) + assert model + # run test + xml_output = Path( + TestUtils.get_output_test_data_dir(""), "test_full_output.xml" + ) + model.ExportToXml(xml_file=xml_output) + # test if file was created + assert xml_output.is_file() Index: DamClients/DamPythonInterface/trunk/src/tests/test_output/test_full_output.xml =================================================================== diff -u --- DamClients/DamPythonInterface/trunk/src/tests/test_output/test_full_output.xml (revision 0) +++ DamClients/DamPythonInterface/trunk/src/tests/test_output/test_full_output.xml (revision 3474) @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + Index: DamClients/DamPythonInterface/trunk/src/dampythoninterface/surface_line.py =================================================================== diff -u --- DamClients/DamPythonInterface/trunk/src/dampythoninterface/surface_line.py (revision 0) +++ DamClients/DamPythonInterface/trunk/src/dampythoninterface/surface_line.py (revision 3474) @@ -0,0 +1,96 @@ +from .base_class import BaseDataClass + +from typing import List +from enum import IntEnum +from pydantic import root_validator +import lxml.etree as et + + +class PointTypeEnum(IntEnum): + NONE = 1 + SurfaceLevelOutside = 2 + DikeToeAtRiver = 3 + ShoulderTopOutside = 4 + ShoulderBaseOutside = 5 + DikeTopAtRiver = 6 + DikeLine = 7 + TrafficLoadOutside = 8 + TrafficLoadInside = 9 + DikeTopAtPolder = 10 + ShoulderBaseInside = 11 + ShoulderTopInside = 12 + DikeToeAtPolder = 13 + DitchDikeSide = 14 + BottomDitchDikeSide = 15 + BottomDitchPolderSide = 16 + DitchPolderSide = 17 + SurfaceLevelInside = 18 + + +class Point(BaseDataClass): + X: float + Z: float + PointType: PointTypeEnum + + +class SurfaceLine(BaseDataClass): + Name: str + Points: List[Point] = [] + + @staticmethod + def is_type_in_list(list_of_points: Point, required_point_type: Point) -> bool: + for point in list_of_points: + if point.PointType == required_point_type: + return True + return False + + @staticmethod + def raise_error_if_point_does_not_exist( + list_of_points: Point, + name: str, + required_point_type: Point, + ) -> None: + + if not (SurfaceLine.is_type_in_list(list_of_points, required_point_type)): + raise ValueError( + f"Set Characteristic points {name} is not complete. \ + Characteristic point {required_point_type.name} is missing." + ) + + @root_validator + def check_that_necessary_points_exist(cls, values): + """ + Property validator of Points of the SurfaceLine class. + Required characteristic points are: + * Surface level outside + * Dike toe at river + * Dike top at river + * Dike top at polder + * Dike toe at polder + * Surface level inside + """ + required_points = [ + PointTypeEnum.SurfaceLevelOutside, + PointTypeEnum.DikeToeAtRiver, + PointTypeEnum.DikeTopAtRiver, + PointTypeEnum.DikeTopAtPolder, + PointTypeEnum.DikeToeAtPolder, + PointTypeEnum.SurfaceLevelInside, + ] + for required_point in required_points: + SurfaceLine.raise_error_if_point_does_not_exist( + values.get("Points"), values.get("Name"), required_point + ) + return values + + def serialize(self) -> et.Element: + surface_line_root = et.Element("SurfaceLine") + surface_line_root.set("Name", self.Name) + points_root = et.SubElement(surface_line_root, "Points") + for point in self.Points: + point_root = et.SubElement(points_root, "Point") + + point_root.set("X", str(point.X)) + point_root.set("Z", str(point.Z)) + point_root.set("PointType", str(point.PointType.value)) + return surface_line_root Index: DamClients/DamPythonInterface/trunk/src/dampythoninterface/base_class.py =================================================================== diff -u --- DamClients/DamPythonInterface/trunk/src/dampythoninterface/base_class.py (revision 0) +++ DamClients/DamPythonInterface/trunk/src/dampythoninterface/base_class.py (revision 3474) @@ -0,0 +1,8 @@ +from pydantic import BaseModel + + +class BaseDataClass(BaseModel): + class Config: + validate_assignment = True + arbitrary_types_allowed = True + validate_all = True \ No newline at end of file Index: DamClients/DamPythonInterface/trunk/src/tests/test_data/TestInputFile.xml =================================================================== diff -u --- DamClients/DamPythonInterface/trunk/src/tests/test_data/TestInputFile.xml (revision 0) +++ DamClients/DamPythonInterface/trunk/src/tests/test_data/TestInputFile.xml (revision 3474) @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file Index: DamClients/DamPythonInterface/trunk/src/tests/test_output/test_serialize_surface_line.xml =================================================================== diff -u --- DamClients/DamPythonInterface/trunk/src/tests/test_output/test_serialize_surface_line.xml (revision 0) +++ DamClients/DamPythonInterface/trunk/src/tests/test_output/test_serialize_surface_line.xml (revision 3474) @@ -0,0 +1,10 @@ + + + + + + + + + + Index: DamClients/DamPythonInterface/trunk/src/dampythoninterface/input.py =================================================================== diff -u --- DamClients/DamPythonInterface/trunk/src/dampythoninterface/input.py (revision 0) +++ DamClients/DamPythonInterface/trunk/src/dampythoninterface/input.py (revision 3474) @@ -0,0 +1,20 @@ +from typing import List +from pathlib import Path +import lxml.etree as et + +from .surface_line import SurfaceLine +from .serializer import Serializer +from .base_class import BaseDataClass + + +class DamInput(BaseDataClass): + SurfaceLines: List[SurfaceLine] = [] + + def ExportToXml(self, xml_file: Path) -> None: + input_root = et.Element("Input") + SurfaceLines_root = et.Element("SurfaceLines") + for surface_line in self.SurfaceLines: + SurfaceLines_root.append(surface_line.serialize()) + input_root.append(SurfaceLines_root) + tree = et.ElementTree(input_root) + tree.write(str(xml_file), pretty_print=True)