Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

272#: Refactor get_location_specification_root_validator #347

Merged
Show file tree
Hide file tree
Changes from 40 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
87a2841
#272: Initial refactor of the get_location_specification_rootvalidator
priscavdsluis Sep 30, 2022
76975f5
autoformat: isort & black
priscavdsluis Sep 30, 2022
70d62f2
#272: Assign default values to LocationValidationConfiguration and Lo…
priscavdsluis Sep 30, 2022
ea77164
autoformat: isort & black
priscavdsluis Sep 30, 2022
a53db06
Merge branch 'main' into refactor/272-refactor-get-location-specifica…
priscavdsluis Sep 30, 2022
d9197f8
#272: Fix validation for presence of fields.
priscavdsluis Sep 30, 2022
93f216c
#272: Switch error messages
priscavdsluis Sep 30, 2022
ca60c25
autoformat: isort & black
priscavdsluis Sep 30, 2022
2afa590
#272: Use new validator for crosssection
priscavdsluis Sep 30, 2022
c8b63be
#272:Formatting
priscavdsluis Sep 30, 2022
a85116f
#272: Fix tests for crosssection
priscavdsluis Sep 30, 2022
c5e1a72
autoformat: isort & black
priscavdsluis Sep 30, 2022
e5e1653
#272: Use new validator for lateral
priscavdsluis Sep 30, 2022
509c6cc
#272: Fix mistake in validator.
priscavdsluis Sep 30, 2022
4d1bde2
#272: Also see empty fields as missing
priscavdsluis Sep 30, 2022
23033cc
#272: Fix tests for lateral
priscavdsluis Sep 30, 2022
183f91c
#272: Use new validator for observationpoint
priscavdsluis Sep 30, 2022
1e773bb
#272: Use new validator for observationcrosssection
priscavdsluis Sep 30, 2022
c151bd3
autoformat: isort & black
priscavdsluis Sep 30, 2022
32bf295
#272: Use new validator for observationcrosssection
priscavdsluis Sep 30, 2022
33d590b
#272: Remove original validator
priscavdsluis Sep 30, 2022
2ca87c6
autoformat: isort & black
priscavdsluis Sep 30, 2022
b7d50e8
#272: Rename validator to original validator
priscavdsluis Sep 30, 2022
4635f0e
#272: Formatting
priscavdsluis Sep 30, 2022
9ec760e
#272: Update comment
priscavdsluis Sep 30, 2022
fa31ef2
autoformat: isort & black
priscavdsluis Sep 30, 2022
03ea8fb
#272: Add tests for validator.
priscavdsluis Oct 3, 2022
fa67583
autoformat: isort & black
priscavdsluis Oct 3, 2022
7ab563d
#272: Update tests for validator.
priscavdsluis Oct 3, 2022
86ae8c0
autoformat: isort & black
priscavdsluis Oct 3, 2022
2ce2765
#272: Remove `get_number_of_coordinates_validator`
priscavdsluis Oct 3, 2022
9f9fdb1
autoformat: isort & black
priscavdsluis Oct 3, 2022
50fe6f6
#272: Move repeated string to constant field to remove code smell.
priscavdsluis Oct 3, 2022
31ca83d
autoformat: isort & black
priscavdsluis Oct 3, 2022
567f21f
#272: Process review commentc; remove unused import.
priscavdsluis Oct 4, 2022
182ece8
#272: Process review commentc; add/update documentation
priscavdsluis Oct 4, 2022
dcc4c8e
#272: Process review commentc; validate number of coordinates as well…
priscavdsluis Oct 4, 2022
4a6aeb3
autoformat: isort & black
priscavdsluis Oct 4, 2022
63f5b51
#272: Remove f
priscavdsluis Oct 4, 2022
9023857
autoformat: isort & black
priscavdsluis Oct 4, 2022
5c362f4
Merge branch 'main' into refactor/272-refactor-get-location-specifica…
priscavdsluis Oct 5, 2022
a47b85a
#272: Use LocationType instead of str
priscavdsluis Oct 6, 2022
1ba46ae
autoformat: isort & black
priscavdsluis Oct 6, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion hydrolib/core/io/crosssection/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
from hydrolib.core.io.friction.models import FrictionType
from hydrolib.core.io.ini.models import INIBasedModel, INIGeneral, INIModel
from hydrolib.core.io.ini.util import (
LocationValidationConfiguration,
LocationValidationFieldNames,
get_enum_validator,
get_from_subclass_defaults,
get_location_specification_rootvalidator,
Expand Down Expand Up @@ -683,7 +685,10 @@ class Comments(INIBasedModel.Comments):
definitionid: str = Field(alias="definitionId")

_location_validator = get_location_specification_rootvalidator(
allow_nodeid=False, numfield_name=None, xfield_name="x", yfield_name="y"
config=LocationValidationConfiguration(
validate_node=False, validate_num_coordinates=False
),
fields=LocationValidationFieldNames(x_coordinates="x", y_coordinates="y"),
)


Expand Down
7 changes: 3 additions & 4 deletions hydrolib/core/io/ext/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
from hydrolib.core.io.ini.models import INIBasedModel, INIGeneral, INIModel
from hydrolib.core.io.ini.serializer import SerializerConfig, write_ini
from hydrolib.core.io.ini.util import (
LocationValidationConfiguration,
get_location_specification_rootvalidator,
get_number_of_coordinates_validator,
get_split_string_on_delimiter_validator,
make_list_validator,
)
Expand Down Expand Up @@ -141,9 +141,8 @@ def is_intermediate_link(self) -> bool:
"xcoordinates", "ycoordinates"
)

_location_validator = get_location_specification_rootvalidator(allow_nodeid=True)
_number_of_coordinates_validator = get_number_of_coordinates_validator(
minimum_required_number_of_coordinates=1
_location_validator = get_location_specification_rootvalidator(
config=LocationValidationConfiguration(minimum_num_coordinates=1)
)

def _get_identifier(self, data: dict) -> Optional[str]:
Expand Down
300 changes: 161 additions & 139 deletions hydrolib/core/io/ini/util.py

Large diffs are not rendered by default.

8 changes: 7 additions & 1 deletion hydrolib/core/io/obs/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
from hydrolib.core.io.common.models import LocationType
from hydrolib.core.io.ini.models import INIBasedModel, INIGeneral, INIModel
from hydrolib.core.io.ini.util import (
LocationValidationConfiguration,
LocationValidationFieldNames,
get_enum_validator,
get_location_specification_rootvalidator,
make_list_validator,
Expand Down Expand Up @@ -70,8 +72,12 @@ class Comments(INIBasedModel.Comments):
y: Optional[float] = Field(None, alias="y")

_type_validator = get_enum_validator("locationtype", enum=LocationType)

_location_validator = get_location_specification_rootvalidator(
allow_nodeid=False, numfield_name=None, xfield_name="x", yfield_name="y"
config=LocationValidationConfiguration(
validate_node=False, validate_num_coordinates=False
),
fields=LocationValidationFieldNames(x_coordinates="x", y_coordinates="y"),
)

def _get_identifier(self, data: dict) -> Optional[str]:
Expand Down
10 changes: 5 additions & 5 deletions hydrolib/core/io/obscrosssection/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

from hydrolib.core.io.ini.models import INIBasedModel, INIGeneral, INIModel
from hydrolib.core.io.ini.util import (
LocationValidationConfiguration,
get_location_specification_rootvalidator,
get_number_of_coordinates_validator,
get_split_string_on_delimiter_validator,
)

Expand Down Expand Up @@ -71,10 +71,10 @@ class Comments(INIBasedModel.Comments):
"xcoordinates", "ycoordinates"
)

_location_validator = get_location_specification_rootvalidator(allow_nodeid=False)

_number_of_coordinates_validator = get_number_of_coordinates_validator(
minimum_required_number_of_coordinates=2
_location_validator = get_location_specification_rootvalidator(
config=LocationValidationConfiguration(
validate_node=False, minimum_num_coordinates=2
tim-vd-aardweg marked this conversation as resolved.
Show resolved Hide resolved
)
)

def _get_identifier(self, data: dict) -> Optional[str]:
Expand Down
272 changes: 189 additions & 83 deletions tests/io/ini/test_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,92 +3,198 @@
import pytest
from pydantic.error_wrappers import ValidationError

from hydrolib.core.io.ini.models import INIBasedModel
from hydrolib.core.io.ini.util import get_number_of_coordinates_validator


class TestCoordinatesValidator:
class DummyModel(INIBasedModel):
"""Dummy model to test the validation of the number of coordinates."""

numcoordinates: Optional[int]
from hydrolib.core.basemodel import BaseModel
from hydrolib.core.io.ini.util import (
LocationValidationConfiguration,
LocationValidationFieldNames,
get_location_specification_rootvalidator,
)


class TestLocationValidationConfiguration:
def test_default(self):
config = LocationValidationConfiguration()
tim-vd-aardweg marked this conversation as resolved.
Show resolved Hide resolved
assert config.validate_node == True
assert config.validate_coordinates == True
assert config.validate_branch == True
assert config.validate_num_coordinates == True
assert config.minimum_num_coordinates == 0


class TestLocationValidationFieldNames:
def test_default(self):
fields = LocationValidationFieldNames()
assert fields.node_id == "nodeId"
assert fields.branch_id == "branchId"
assert fields.chainage == "chainage"
assert fields.x_coordinates == "xCoordinates"
assert fields.y_coordinates == "yCoordinates"
assert fields.num_coordinates == "numCoordinates"
assert fields.location_type == "locationType"


class TestLocationSpecificationValidator:
class DummyModel(BaseModel):
"""Dummy model to test the validation of the location specification."""

nodeid: Optional[str]
branchid: Optional[str]
chainage: Optional[str]
xcoordinates: Optional[List[float]]
ycoordinates: Optional[List[float]]
numcoordinates: Optional[int]
locationtype: Optional[str]

_number_of_coordinates_validator = get_number_of_coordinates_validator(
minimum_required_number_of_coordinates=2
validator = get_location_specification_rootvalidator(
config=LocationValidationConfiguration(minimum_num_coordinates=3)
)

def test_all_values_none_does_not_throw(self):
model = TestCoordinatesValidator.DummyModel()

assert model.numcoordinates is None
assert model.xcoordinates is None
assert model.ycoordinates is None

def test_coordinates_given_but_none_expected_throws_value_error(self):
values = self._create_valid_dummy_model_values()
values["numcoordinates"] = None

with pytest.raises(ValidationError):
TestCoordinatesValidator.DummyModel(**values)

def test_no_xcoordinates_given_while_expected_throws_value_error(self):
values = self._create_valid_dummy_model_values()
values["xcoordinates"] = None

with pytest.raises(ValidationError):
TestCoordinatesValidator.DummyModel(**values)

def test_no_ycoordinates_given_while_expected_throws_value_error(self):
values = self._create_valid_dummy_model_values()
values["ycoordinates"] = None

with pytest.raises(ValidationError):
TestCoordinatesValidator.DummyModel(**values)

def test_fewer_xcoordinates_than_expected_throws_value_error(self):
values = self._create_valid_dummy_model_values()
values["xcoordinates"] = [1, 2]

with pytest.raises(ValidationError):
TestCoordinatesValidator.DummyModel(**values)

def test_more_xcoordinates_than_expected_throws_value_error(self):
values = self._create_valid_dummy_model_values()
values["xcoordinates"] = [1, 2, 3, 4]

with pytest.raises(ValidationError):
TestCoordinatesValidator.DummyModel(**values)

def test_fewer_ycoordinates_than_expected_throws_value_error(self):
values = self._create_valid_dummy_model_values()
values["ycoordinates"] = [1, 2]

with pytest.raises(ValidationError):
TestCoordinatesValidator.DummyModel(**values)

def test_more_ycoordinates_than_expected_throws_value_error(self):
values = self._create_valid_dummy_model_values()
values["ycoordinates"] = [1, 2, 3, 4]

with pytest.raises(ValidationError):
TestCoordinatesValidator.DummyModel(**values)

def test_fewer_than_minimum_required_number_of_coordinates_throws_value_error(self):
values = self._create_valid_dummy_model_values()
values["numcoordinates"] = 1
values["xcoordinates"] = [1.23]
values["ycoordinates"] = [9.87]

with pytest.raises(ValidationError):
TestCoordinatesValidator.DummyModel(**values)

def _create_valid_dummy_model_values(self) -> Dict:
values = dict(
numcoordinates=3,
xcoordinates=[1.23, 4.56, 7.89],
ycoordinates=[9.87, 6.54, 3.21],
@pytest.mark.parametrize(
"values",
[
{},
{
"nodeid": "some_nodeid",
"branchid": "some_branchid",
"chainage": 1.23,
"xcoordinates": [4.56, 5.67, 6.78],
"ycoordinates": [7.89, 8.91, 9.12],
"numcoordinates": 3,
},
{
"xcoordinates": [4.56, 5.67, 6.78],
"ycoordinates": [7.89, 8.91, 9.12],
tim-vd-aardweg marked this conversation as resolved.
Show resolved Hide resolved
},
{
"nodeid": "some_nodeid",
"branchid": "some_branchid",
"chainage": 1.23,
},
{
"branchid": "some_branchid",
"chainage": 1.23,
"xcoordinates": [4.56, 5.67, 6.78],
},
{
"branchid": "some_branchid",
tim-vd-aardweg marked this conversation as resolved.
Show resolved Hide resolved
},
],
)
def test_incorrect_fields_provided_raises_error(self, values: dict):
with pytest.raises(ValidationError) as error:
TestLocationSpecificationValidator.DummyModel(**values)

expected_message = "nodeId or branchId and chainage or xCoordinates, yCoordinates and numCoordinates should be provided"
assert expected_message in str(error.value)

def test_too_few_coordinates_raises_error(self):
values = {
"xcoordinates": [1.23, 2.34],
"ycoordinates": [3.45, 4.56],
"numcoordinates": 2,
}

with pytest.raises(ValidationError) as error:
TestLocationSpecificationValidator.DummyModel(**values)

expected_message = (
"xCoordinates and yCoordinates should have at least 3 coordinate(s)"
)

return values
assert expected_message in str(error.value)

def test_coordinate_amount_does_not_match_numcoordinates_raises_error(self):
values = {
"xcoordinates": [1.23, 2.34, 3.45],
"ycoordinates": [4.56, 5.67, 6.78],
"numcoordinates": 4,
}

with pytest.raises(ValidationError) as error:
TestLocationSpecificationValidator.DummyModel(**values)

expected_message = "numCoordinates should be equal to the amount of xCoordinates and yCoordinates"
assert expected_message in str(error.value)

@pytest.mark.parametrize(
"values",
[
pytest.param(
{
"nodeid": "some_nodeid",
"locationtype": "2d",
},
id="nodeid",
),
pytest.param(
{
"branchid": "some_branchid",
"chainage": 1.23,
"locationtype": "2d",
},
id="branchid",
),
],
)
def test_incorrect_location_type_raises_error(self, values: dict):
with pytest.raises(ValidationError) as error:
TestLocationSpecificationValidator.DummyModel(**values)

expected_message = "locationType should be 1d but was 2d"
assert expected_message in str(error.value)

@pytest.mark.parametrize(
"values",
[
pytest.param(
{
"nodeid": "some_nodeid",
},
id="nodeid",
),
pytest.param(
{
"branchid": "some_branchid",
"chainage": 1.23,
},
id="branchid",
),
pytest.param(
{
"xcoordinates": [4.56, 5.67, 6.78],
"ycoordinates": [7.89, 8.91, 9.12],
"numcoordinates": 3,
},
id="coordinates",
),
],
)
def test_correct_fields_initializes(self, values: dict):
validated_values = TestLocationSpecificationValidator.DummyModel.validator(
values
)
assert validated_values == values

@pytest.mark.parametrize(
"values, expected_values",
[
pytest.param(
{
"nodeid": "some_nodeid",
},
{"nodeid": "some_nodeid", "locationtype": "1d"},
id="nodeid",
),
pytest.param(
{"branchid": "some_branchid", "chainage": 1.23},
{"branchid": "some_branchid", "chainage": 1.23, "locationtype": "1d"},
id="branchid",
),
],
)
def test_correct_1d_fields_locationtype_is_added(
self, values: dict, expected_values: dict
):
validated_values = TestLocationSpecificationValidator.DummyModel.validator(
values
)
assert validated_values == expected_values
Loading