diff --git a/src/bo4e/com/tagesvektor.py b/src/bo4e/com/tagesvektor.py new file mode 100644 index 000000000..3270bfc61 --- /dev/null +++ b/src/bo4e/com/tagesvektor.py @@ -0,0 +1,62 @@ +""" +Contains Tagesvektor class and corresponding marshmallow schema for de-/serialization +""" +import datetime +from typing import List + +import attr +from marshmallow import fields, post_load + +from bo4e.com.com import COM, COMSchema +from bo4e.com.zeitreihenwertkompakt import Zeitreihenwertkompakt, ZeitreihenwertkompaktSchema + + +# pylint: disable=too-few-public-methods +from bo4e.validators import check_list_length_at_least_one + + +@attr.s(auto_attribs=True, kw_only=True) +class Tagesvektor(COM): + """ + Abbildung eines Tagesvektors eines beliebigen äquidistanten Zeitrasters + """ + + # required attributes + # for the validator see https://github.com/Hochfrequenz/BO4E-python/issues/261 + tag: datetime.datetime = attr.ib(validator=attr.validators.instance_of(datetime.datetime)) + """ + Der Zeitpunkt ab dem die Werte ermittelt wurden. + Es kann entweder der Beginn des Strom- oder Gastages verwendet werden. + Der Zeitpunkt sollte eindeutig sein, d.h. sowohl Datum+Uhrzeit als auch den UTC-Offset spezifizieren. + """ + # for the validator see also https://github.com/Hochfrequenz/BO4E-python/issues/262 + # https://www.attrs.org/en/stable/api.html#attr.validators.deep_iterable + werte: List[Zeitreihenwertkompakt] = attr.ib( + validator=[ + attr.validators.deep_iterable( + member_validator=attr.validators.instance_of(Zeitreihenwertkompakt), + iterable_validator=attr.validators.instance_of(list), + ), + check_list_length_at_least_one, + ] + ) + """ + Die Werte am angegebenen Tag; + In Kombination aus Zeitintervall und Tag lassen sich die Zeiten der Werte eindeutig konstruieren. + """ + + +class TagesvektorSchema(COMSchema): + """ + Schema for de-/serialization of Tagesvektor + """ + + # required attributes + tag = fields.DateTime() + werte = fields.List(fields.Nested(ZeitreihenwertkompaktSchema)) + + # pylint: disable=no-self-use, unused-argument + @post_load + def deserialize(self, data, **kwargs) -> Tagesvektor: + """Deserialize JSON to Preisstaffel object""" + return Tagesvektor(**data) diff --git a/tests/test_tagesvektor.py b/tests/test_tagesvektor.py new file mode 100644 index 000000000..dd79be481 --- /dev/null +++ b/tests/test_tagesvektor.py @@ -0,0 +1,56 @@ +from datetime import datetime, timezone +from decimal import Decimal + +import pytest # type:ignore[import] + +from bo4e.com.tagesvektor import Tagesvektor, TagesvektorSchema +from bo4e.com.zeitreihenwertkompakt import Zeitreihenwertkompakt +from tests.serialization_helper import assert_serialization_roundtrip # type:ignore[import] +from tests.test_sigmoidparameter import example_sigmoidparameter # type:ignore[import] + + +class TestTagesvektor: + @pytest.mark.parametrize( + "tagesvektor, expected_json_dict", + [ + pytest.param( + Tagesvektor( + tag=datetime(2021, 12, 15, 5, 0, tzinfo=timezone.utc), + werte=[ + Zeitreihenwertkompakt( + wert=Decimal(40), + ), + Zeitreihenwertkompakt( + wert=Decimal(50), + ), + ], + ), + { + "tag": "2021-12-15T05:00:00+00:00", + "werte": [ + {"wert": "40", "statuszusatz": None, "status": None}, + {"wert": "50", "statuszusatz": None, "status": None}, + ], + }, + ), + ], + ) + def test_serialization_roundtrip(self, tagesvektor: Tagesvektor, expected_json_dict: dict): + """ + Test de-/serialisation of Preisstaffel. + """ + assert_serialization_roundtrip(tagesvektor, TagesvektorSchema(), expected_json_dict) + + def test_missing_required_attribute(self): + with pytest.raises(TypeError) as excinfo: + _ = Tagesvektor() + + assert "missing 2 required" in str(excinfo.value) + + def test_list_not_long_enough_attribute(self): + with pytest.raises(ValueError) as excinfo: + _ = Tagesvektor(tag=datetime(2021, 12, 15, 5, 0, tzinfo=timezone.utc), werte=[]) + + assert "List werte must not be empty" in str(excinfo.value) + + # add tests for issues 261 and 262 here