Repository: bo4e/BO4E-python (language: python)

Issue:
Implement COM Tagesvektor
https://www.bo4e.de/dokumentation/komponenten/com-tagesvektor
Bitte die Doku nicht ganz so kompliziert formulieren wie im Original. Wenn man einen sauberen Tag/Startzeitpunkt (mit definiertem UTC-Offset) hat ist alles kein Problem. Die Faustregel ist einfach, dass die Zeit innerhalb des Vektors in gleichmäßigen Schritten vorwärts läuft. Das ganze drum herum ist nur dann ein Problem, wenn man sich zu starr an die deutsche lokalzeit klammert.

requires
- #167 
COM Tagesvektor: Validate `tag` is always the begin of the German Stromtag or Gastag
Therefore:
- [ ] Determine the Timezone of the Datetime, raise an error if it's unknown
- [ ] Calculate the UTC-Timestamp of the begin of the German Strom and Gastag for the respective `tag` 
- [ ] Check that the (hour, minute, second) is correct, meaning it's the strom or gastag
- [ ] UTC datetimes should always pass


Discussion / hints:

Fix patch:
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)


Test patch:
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