From 2c7dde4eba2bacbf52a622f791d6275c8ffcb9cb Mon Sep 17 00:00:00 2001 From: konstantin Date: Tue, 18 Jan 2022 22:29:06 +0100 Subject: [PATCH] Implement BO Region (#311) Co-authored-by: Franziska <73471037+hf-fvesely@users.noreply.github.com> --- src/bo4e/bo/region.py | 59 ++++++++++++++++++++++++++++++++++ src/bo4e/enum/botyp.py | 1 + tests/test_region.py | 42 ++++++++++++++++++++++++ tests/test_regionskriterium.py | 12 ++++--- 4 files changed, 109 insertions(+), 5 deletions(-) create mode 100644 src/bo4e/bo/region.py create mode 100644 tests/test_region.py diff --git a/src/bo4e/bo/region.py b/src/bo4e/bo/region.py new file mode 100644 index 000000000..b2f4b9393 --- /dev/null +++ b/src/bo4e/bo/region.py @@ -0,0 +1,59 @@ +""" +Contains Region class and corresponding marshmallow schema for de-/serialization +""" +from typing import List, Optional + +import attr +from marshmallow import fields + +from bo4e.bo.geschaeftsobjekt import Geschaeftsobjekt, GeschaeftsobjektSchema +from bo4e.com.regionskriterium import Regionskriterium, RegionskriteriumSchema +from bo4e.enum.botyp import BoTyp +from bo4e.validators import check_list_length_at_least_one + + +# pylint: disable=too-few-public-methods +@attr.s(auto_attribs=True, kw_only=True) +class Region(Geschaeftsobjekt): + """ + Modellierung einer Region als Menge von Kriterien, die eine Region beschreiben + """ + + # required attributes + bo_typ: BoTyp = attr.ib(default=BoTyp.REGION) + #: Bezeichnung der Region + bezeichnung: str = attr.ib(validator=attr.validators.instance_of(str)) + + #: Positivliste der Kriterien zur Definition der Region + positiv_liste: List[Regionskriterium] = attr.ib( + validator=attr.validators.deep_iterable( + member_validator=attr.validators.instance_of(Regionskriterium), + iterable_validator=check_list_length_at_least_one, + ) + ) + + # optional attributes + #: Negativliste der Kriterien zur Definition der Region + negativ_liste: Optional[List[Regionskriterium]] = attr.ib( + default=None, + validator=attr.validators.optional( + attr.validators.deep_iterable( + member_validator=attr.validators.instance_of(Regionskriterium), + iterable_validator=attr.validators.instance_of(list), # no min length for negativListe + ) + ), + ) + + +class RegionSchema(GeschaeftsobjektSchema): + """ + Schema for de-/serialization of Region + """ + + class_name = Region + # required attributes + bezeichnung = fields.Str() + positiv_liste = fields.List(fields.Nested(RegionskriteriumSchema)) + + # optional attributes + negativ_liste = fields.List(fields.Nested(RegionskriteriumSchema), load_default=None) diff --git a/src/bo4e/enum/botyp.py b/src/bo4e/enum/botyp.py index fd57e50b5..217e3c103 100644 --- a/src/bo4e/enum/botyp.py +++ b/src/bo4e/enum/botyp.py @@ -31,6 +31,7 @@ class BoTyp(StrEnum): PREISBLATTNETZNUTZUNG = "PREISBLATTNETZNUTZUNG" PREISBLATTUMLAGEN = "PREISBLATTUMLAGEN" RECHNUNG = "RECHNUNG" + REGION = "REGION" REGIONALTARIF = "REGIONALTARIF" STANDORTEIGENSCHAFTEN = "STANDORTEIGENSCHAFTEN" TARIFINFO = "TARIFINFO" diff --git a/tests/test_region.py b/tests/test_region.py new file mode 100644 index 000000000..70cc61ec7 --- /dev/null +++ b/tests/test_region.py @@ -0,0 +1,42 @@ +import pytest # type:ignore[import] + +from bo4e.bo.region import Region, RegionSchema +from tests.serialization_helper import assert_serialization_roundtrip # type:ignore[import] +from tests.test_regionskriterium import example_regionskriterium # type:ignore[import] + + +class TestRegion: + @pytest.mark.parametrize( + "region", + [ + pytest.param( + Region( + bezeichnung="Bikini Bottom", + positiv_liste=[example_regionskriterium], + negativ_liste=[example_regionskriterium], + ), + id="max attributes", + ), + pytest.param( + Region(bezeichnung="Bikini Bottom", positiv_liste=[example_regionskriterium]), id="min attributes" + ), + ], + ) + def test_serialization_roundtrip(self, region: Region): + assert_serialization_roundtrip(region, RegionSchema()) + + def test_missing_required_attribute(self): + with pytest.raises(TypeError) as excinfo: + _ = Region() + + assert "missing 2 required" in str(excinfo.value) + + def test_region_positiv_liste_required_and_negativ_liste_not_required(self): + with pytest.raises(ValueError) as excinfo: + _ = Region( + bezeichnung="Bikini Bottom", + positiv_liste=[], + negativ_liste=[], + ) + + assert "List positiv_liste must not be empty." in str(excinfo.value) diff --git a/tests/test_regionskriterium.py b/tests/test_regionskriterium.py index 8ada2874c..4ba2b1ab2 100644 --- a/tests/test_regionskriterium.py +++ b/tests/test_regionskriterium.py @@ -5,17 +5,19 @@ from bo4e.enum.regionskriteriumtyp import Regionskriteriumtyp from tests.serialization_helper import assert_serialization_roundtrip # type:ignore[import] +example_regionskriterium = Regionskriterium( + regionskriteriumtyp=Regionskriteriumtyp.REGELGEBIET_NAME, + gueltigkeitstyp=Gueltigkeitstyp.NICHT_IN, + wert="Was ist ein Regionskriterium?", +) + class TestRegionskriterium: @pytest.mark.parametrize( "regionskriterium, expected_json_dict", [ pytest.param( - Regionskriterium( - regionskriteriumtyp=Regionskriteriumtyp.REGELGEBIET_NAME, - gueltigkeitstyp=Gueltigkeitstyp.NICHT_IN, - wert="Was ist ein Regionskriterium?", - ), + example_regionskriterium, { "gueltigkeitstyp": "NICHT_IN", "regionskriteriumtyp": "REGELGEBIET_NAME",