Skip to content

Commit e36fb7c

Browse files
levtojilord-haffihf-aschloegl
authored
feat: new BO Lokationszuordnung and transfer of BO/COMs from bo4e-dotnet (#759)
* feat: add new lokationszuordnung * chore: linter fix * chore: pre-commit * chore: module docstring * chore: working? doc * empty commit * missing bo's created and adaptions implemented (wip) * add missing classes from bo4e dotnet * using new added classes * style: fix formatting * added classes to init script * cyclic import and missing imports * doc strings * renaming to match naming convention and adding some comments * Revert of formatting changes * Revert "style: fix formatting" This reverts commit 460da79. * add missing comments * fix docstrings * messlokationszuordnung fix after merge * Refactor using TYPE_CHECKING * fix: useless import * Removed reappearing file * Add missing none to make the property optional * Remove Typ for Konfigurationsprodukt * Fix JSON-schema generation for models with circular references * fix for schema generation * Update src/bo4e/bo/marktlokation.py Co-authored-by: Annika <73470827+hf-aschloegl@users.noreply.github.com> * Update generate_or_validate_json_schemas.py Co-authored-by: Leon Haffmans <49658102+lord-haffi@users.noreply.github.com> * Update src/bo4e/bo/netzlokation.py Co-authored-by: Annika <73470827+hf-aschloegl@users.noreply.github.com> * Update src/bo4e/bo/steuerbareressource.py Co-authored-by: Annika <73470827+hf-aschloegl@users.noreply.github.com> * Update src/bo4e/bo/steuerbareressource.py Co-authored-by: Annika <73470827+hf-aschloegl@users.noreply.github.com> * review fixes * Todo added to zuordnungstyp in lokationszuordnung * Added roundtrip tests * Add test konfigurationsprodukt * fix for Menge * change to decimal * Update generate_or_validate_json_schemas.py Co-authored-by: Leon Haffmans <49658102+lord-haffi@users.noreply.github.com> * Remove unused include * Add list of Lokationszuordnung * Add import * Adapt tests to new field * Fix tests * Update src/bo4e/bo/messlokation.py Co-authored-by: Annika <73470827+hf-aschloegl@users.noreply.github.com> --------- Co-authored-by: Leon Haffmans <leon.haffmans@hochfrequenz.de> Co-authored-by: Annika <73470827+hf-aschloegl@users.noreply.github.com> Co-authored-by: Leon Haffmans <49658102+lord-haffi@users.noreply.github.com>
1 parent 2fe9c73 commit e36fb7c

24 files changed

+581
-104
lines changed

generate_or_validate_json_schemas.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,19 @@ def get_schema_json_dict(cls: Any) -> dict[str, Any]:
106106
schema_json_dict = TypeAdapter(cls).json_schema(schema_generator=GenerateJsonSchema)
107107
else:
108108
raise ValueError(f"Class {cls} is neither a pydantic BaseModel nor an enum.")
109+
if {"allOf", "$defs"} == set(schema_json_dict.keys()):
110+
assert (
111+
len(schema_json_dict["allOf"]) == 1
112+
), "Internal error: Assumed circular reference but structure is unexpected"
113+
# This is the case for schemas containing circular references
114+
reference_pattern = re.compile(r"^#/\$defs/(?P<cls_name>\w+)$")
115+
reference_match = reference_pattern.fullmatch(schema_json_dict["allOf"][0]["$ref"])
116+
assert (
117+
reference_match is not None
118+
), "Internal Error: Reference string has unexpected format: {schema_json_dict['allOf'][0]['$ref']}"
119+
schema_json_dict_to_merge = schema_json_dict["$defs"][reference_match.group("cls_name")]
120+
del schema_json_dict["allOf"]
121+
schema_json_dict.update(schema_json_dict_to_merge)
109122
if "$defs" in schema_json_dict:
110123
del schema_json_dict["$defs"]
111124
return schema_json_dict
@@ -191,7 +204,7 @@ def traverse_dict(obj: dict[str, Any]) -> None:
191204
)
192205
def generate_or_validate_json_schemas(mode: Literal["validate", "generate"], target_version: str) -> None:
193206
"""generate json schemas for all BOs and COMs"""
194-
_logger.debug("Mode: %s, target version: %s", mode, target_version)
207+
_logger.info("Mode: %s, target version: %s", mode, target_version)
195208
packages = ["bo", "com", "enum"]
196209

197210
if mode == "generate":

src/bo4e/__init__.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,11 @@
1919
"Geschaeftspartner",
2020
"Kosten",
2121
"Lastgang",
22+
"Lokationszuordnung",
2223
"Marktlokation",
2324
"Marktteilnehmer",
2425
"Messlokation",
26+
"Netzlokation",
2527
"Person",
2628
"Preisblatt",
2729
"PreisblattDienstleistung",
@@ -33,10 +35,12 @@
3335
"Region",
3436
"Regionaltarif",
3537
"Standorteigenschaften",
38+
"SteuerbareRessource",
3639
"Tarif",
3740
"Tarifinfo",
3841
"Tarifkosten",
3942
"Tarifpreisblatt",
43+
"TechnischeRessource",
4044
"Vertrag",
4145
"Zaehler",
4246
"Zeitreihe",
@@ -66,7 +70,6 @@
6670
"KriteriumWert",
6771
"MarktgebietInfo",
6872
"Menge",
69-
"Messlokationszuordnung",
7073
"PositionsAufAbschlag",
7174
"Preis",
7275
"Preisgarantie",
@@ -115,6 +118,7 @@
115118
"Bemessungsgroesse",
116119
"Bilanzierungsmethode",
117120
"Dienstleistungstyp",
121+
"EMobilitaetsart",
118122
"Energierichtung",
119123
"Erzeugungsart",
120124
"Gasqualitaet",
@@ -124,6 +128,7 @@
124128
"Geschaeftspartnerrolle",
125129
"Gueltigkeitstyp",
126130
"Kalkulationsmethode",
131+
"Konfigurationsprodukt",
127132
"Kontaktart",
128133
"Kostenklasse",
129134
"Kundengruppe",
@@ -159,13 +164,17 @@
159164
"Registeranzahl",
160165
"Rollencodetyp",
161166
"Sparte",
167+
"Speicherart",
168+
"SteuerkanalLeistungsbeschreibung",
162169
"Steuerkennzeichen",
163170
"StrEnum",
164171
"Tarifkalkulationsmethode",
165172
"Tarifmerkmal",
166173
"Tarifregionskriterium",
167174
"Tariftyp",
168175
"Tarifzeit",
176+
"TechnischeRessourceNutzung",
177+
"TechnischeRessourceVerbrauchsart",
169178
"Themengebiet",
170179
"Titel",
171180
"Typ",
@@ -201,9 +210,11 @@
201210
from .bo.geschaeftspartner import Geschaeftspartner
202211
from .bo.kosten import Kosten
203212
from .bo.lastgang import Lastgang
213+
from .bo.lokationszuordnung import Lokationszuordnung
204214
from .bo.marktlokation import Marktlokation
205215
from .bo.marktteilnehmer import Marktteilnehmer
206216
from .bo.messlokation import Messlokation
217+
from .bo.netzlokation import Netzlokation
207218
from .bo.person import Person
208219
from .bo.preisblatt import Preisblatt
209220
from .bo.preisblattdienstleistung import PreisblattDienstleistung
@@ -215,10 +226,12 @@
215226
from .bo.region import Region
216227
from .bo.regionaltarif import Regionaltarif
217228
from .bo.standorteigenschaften import Standorteigenschaften
229+
from .bo.steuerbareressource import SteuerbareRessource
218230
from .bo.tarif import Tarif
219231
from .bo.tarifinfo import Tarifinfo
220232
from .bo.tarifkosten import Tarifkosten
221233
from .bo.tarifpreisblatt import Tarifpreisblatt
234+
from .bo.technischeressource import TechnischeRessource
222235
from .bo.vertrag import Vertrag
223236
from .bo.zaehler import Zaehler
224237
from .bo.zeitreihe import Zeitreihe
@@ -243,14 +256,14 @@
243256
from .com.fremdkostenposition import Fremdkostenposition
244257
from .com.geokoordinaten import Geokoordinaten
245258
from .com.katasteradresse import Katasteradresse
259+
from .com.konfigurationsprodukt import Konfigurationsprodukt
246260
from .com.kontaktweg import Kontaktweg
247261
from .com.konzessionsabgabe import Konzessionsabgabe
248262
from .com.kostenblock import Kostenblock
249263
from .com.kostenposition import Kostenposition
250264
from .com.kriteriumwert import KriteriumWert
251265
from .com.marktgebietinfo import MarktgebietInfo
252266
from .com.menge import Menge
253-
from .com.messlokationszuordnung import Messlokationszuordnung
254267
from .com.positionsaufabschlag import PositionsAufAbschlag
255268
from .com.preis import Preis
256269
from .com.preisgarantie import Preisgarantie
@@ -301,6 +314,7 @@
301314
from .enum.bemessungsgroesse import Bemessungsgroesse
302315
from .enum.bilanzierungsmethode import Bilanzierungsmethode
303316
from .enum.dienstleistungstyp import Dienstleistungstyp
317+
from .enum.emobilitaetsart import EMobilitaetsart
304318
from .enum.energierichtung import Energierichtung
305319
from .enum.erzeugungsart import Erzeugungsart
306320
from .enum.gasqualitaet import Gasqualitaet
@@ -345,13 +359,17 @@
345359
from .enum.registeranzahl import Registeranzahl
346360
from .enum.rollencodetyp import Rollencodetyp
347361
from .enum.sparte import Sparte
362+
from .enum.speicherart import Speicherart
363+
from .enum.steuerkanalleistungsbeschreibung import SteuerkanalLeistungsbeschreibung
348364
from .enum.steuerkennzeichen import Steuerkennzeichen
349365
from .enum.strenum import StrEnum
350366
from .enum.tarifkalkulationsmethode import Tarifkalkulationsmethode
351367
from .enum.tarifmerkmal import Tarifmerkmal
352368
from .enum.tarifregionskriterium import Tarifregionskriterium
353369
from .enum.tariftyp import Tariftyp
354370
from .enum.tarifzeit import Tarifzeit
371+
from .enum.technischeressourcenutzung import TechnischeRessourceNutzung
372+
from .enum.technischeressourceverbrauchsart import TechnischeRessourceVerbrauchsart
355373
from .enum.themengebiet import Themengebiet
356374
from .enum.titel import Titel
357375
from .enum.typ import Typ

src/bo4e/bo/lokationszuordnung.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
"""
2+
Contains Lokationszuordnung class
3+
"""
4+
5+
from typing import TYPE_CHECKING, Annotated, Optional
6+
7+
from pydantic import Field
8+
9+
from ..enum.typ import Typ
10+
from ..utils import postprocess_docstring
11+
from .geschaeftsobjekt import Geschaeftsobjekt
12+
13+
if TYPE_CHECKING:
14+
from ..bo.marktlokation import Marktlokation
15+
from ..bo.messlokation import Messlokation
16+
from ..bo.netzlokation import Netzlokation
17+
from ..bo.steuerbareressource import SteuerbareRessource
18+
from ..bo.technischeressource import TechnischeRessource
19+
from ..com.zeitspanne import Zeitspanne
20+
21+
22+
@postprocess_docstring
23+
class Lokationszuordnung(Geschaeftsobjekt):
24+
"""
25+
Modell für die Abbildung der Referenz auf die Lokationsbündelstruktur. Diese gibt an welche Marktlokationen,
26+
Messlokationen, Netzlokationen, technische/steuerbaren Ressourcen an einer Lokation vorhanden sind.
27+
28+
.. raw:: html
29+
30+
<object data="../_static/images/bo4e/bo/Lokationszuordnung.svg" type="image/svg+xml"></object>
31+
32+
.. HINT::
33+
`Lokationszuordnung JSON Schema <https://json-schema.app/view/%23?url=https://raw.githubusercontent.com/BO4E/BO4E-Schemas/{__gh_version__}/src/bo4e_schemas/bo/Lokationszuordnung.json>`_
34+
"""
35+
36+
typ: Annotated[Optional[Typ], Field(alias="_typ")] = Typ.LOKATIONSZUORDNUNG
37+
38+
#: Liste mit referenzierten Marktlokationen
39+
marktlokationen: Optional[list["Marktlokation"]] = None
40+
#: Liste mit referenzierten Messlokationen
41+
messlokationen: Optional[list["Messlokation"]] = None
42+
#: Liste mit referenzierten Netzlokationen
43+
netzlokationen: Optional[list["Netzlokation"]] = None
44+
#: Liste mit referenzierten technischen Ressourcen
45+
technische_ressourcen: Optional[list["TechnischeRessource"]] = None
46+
#: Liste mit referenzierten steuerbaren Ressourcen
47+
steuerbare_ressourcen: Optional[list["SteuerbareRessource"]] = None
48+
#: Zeitspanne der Gültigkeit
49+
gueltigkeit: Optional["Zeitspanne"] = None
50+
#: Verknüpfungsrichtung z.B. Malo-Melo [TODO: Eventuell anderer Datentyp]
51+
zuordnungstyp: Optional[str] = None
52+
#: Code, der angibt wie die Lokationsbündelstruktur zusammengesetzt ist (zu finden unter "Codeliste der Lokationsbündelstrukturen" auf https://www.edi-energy.de/index.php?id=38)
53+
lokationsbuendelcode: Optional[str] = None

src/bo4e/bo/marktlokation.py

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
from ..com.adresse import Adresse
1717
from ..com.geokoordinaten import Geokoordinaten
1818
from ..com.katasteradresse import Katasteradresse
19-
from ..com.messlokationszuordnung import Messlokationszuordnung
2019
from ..com.verbrauch import Verbrauch
2120
from ..com.zaehlwerk import Zaehlwerk
2221
from ..enum.bilanzierungsmethode import Bilanzierungsmethode
@@ -28,6 +27,7 @@
2827
from ..enum.sparte import Sparte
2928
from ..enum.verbrauchsart import Verbrauchsart
3029
from .geschaeftspartner import Geschaeftspartner
30+
from .lokationszuordnung import Lokationszuordnung
3131

3232
# pylint: disable=no-name-in-module
3333

@@ -47,6 +47,7 @@ class Marktlokation(Geschaeftsobjekt):
4747
"""
4848

4949
typ: Annotated[Optional["Typ"], Field(alias="_typ")] = Typ.MARKTLOKATION
50+
5051
#: Identifikationsnummer einer Marktlokation, an der Energie entweder verbraucht, oder erzeugt wird.
5152
marktlokations_id: Optional[str] = None
5253
#: Sparte der Marktlokation, z.B. Gas oder Strom
@@ -80,28 +81,6 @@ class Marktlokation(Geschaeftsobjekt):
8081
gasqualitaet: Optional["Gasqualitaet"] = None
8182
#: Geschäftspartner, dem diese Marktlokation gehört
8283
endkunde: Optional["Geschaeftspartner"] = None
83-
zugehoerige_messlokation: Optional["Messlokationszuordnung"] = None # todo: rename to plural
84-
"""
85-
Aufzählung der Messlokationen, die zu dieser Marktlokation gehören.
86-
Es können 3 verschiedene Konstrukte auftreten:
87-
88-
Beziehung 1 : 0 : Hier handelt es sich um Pauschalanlagen ohne Messung. D.h. die Verbrauchsdaten sind direkt über
89-
die Marktlokation abgreifbar.
90-
Beziehung 1 : 1 : Das ist die Standard-Beziehung für die meisten Fälle. In diesem Fall gibt es zu einer
91-
Marktlokation genau eine Messlokation.
92-
Beziehung 1 : N : Hier liegt beispielsweise eine Untermessung vor. Der Verbrauch einer Marklokation berechnet sich
93-
hier aus mehreren Messungen.
94-
95-
Es gibt praktisch auch noch die Beziehung N : 1, beispielsweise bei einer Zweirichtungsmessung bei der durch eine
96-
Messeinrichtung die Messung sowohl für die Einspreiseseite als auch für die Aussspeiseseite erfolgt.
97-
Da Abrechnung und Bilanzierung jedoch für beide Marktlokationen getrennt erfolgt, werden nie beide Marktlokationen
98-
gemeinsam betrachtet. Daher lässt sich dieses Konstrukt auf zwei 1:1-Beziehung zurückführen,
99-
wobei die Messlokation in beiden Fällen die gleiche ist.
100-
101-
In den Zuordnungen sind ist die arithmetische Operation mit der der Verbrauch einer Messlokation zum Verbrauch einer
102-
Marktlokation beitrögt mit aufgeführt.
103-
Der Standard ist hier die Addition.
104-
"""
10584

10685
# only one of the following three optional attributes can be set
10786
#: Die Adresse, an der die Energie-Lieferung oder -Einspeisung erfolgt
@@ -127,3 +106,8 @@ class Marktlokation(Geschaeftsobjekt):
127106
zaehlwerke: Optional[list["Zaehlwerk"]] = None
128107
verbrauchsmengen: Optional[list["Verbrauch"]] = None
129108
zaehlwerke_der_beteiligten_marktrolle: Optional[list["Zaehlwerk"]] = None
109+
110+
#: Lokationszuordnung, um bspw. die zugehörigen Messlokationen anzugeben
111+
lokationszuordnungen: Optional[list["Lokationszuordnung"]] = None
112+
#: Lokationsbuendel Code, der die Funktion dieses BOs an der Lokationsbuendelstruktur beschreibt.
113+
lokationsbuendel_objektcode: Optional[str] = None

src/bo4e/bo/messlokation.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
if TYPE_CHECKING:
1515
from ..bo.geraet import Geraet
16+
from ..bo.lokationszuordnung import Lokationszuordnung
1617
from ..com.adresse import Adresse
1718
from ..com.dienstleistung import Dienstleistung
1819
from ..com.geokoordinaten import Geokoordinaten
@@ -39,6 +40,7 @@ class Messlokation(Geschaeftsobjekt):
3940
"""
4041

4142
typ: Annotated[Optional["Typ"], Field(alias="_typ")] = Typ.MESSLOKATION
43+
4244
#: Die Messlokations-Identifikation; Das ist die frühere Zählpunktbezeichnung
4345
messlokations_id: Optional[str] = None
4446
#: Sparte der Messlokation, z.B. Gas oder Strom
@@ -83,3 +85,7 @@ class Messlokation(Geschaeftsobjekt):
8385
Alternativ zu einer postalischen Adresse und Geokoordinaten kann hier eine Ortsangabe mittels Gemarkung und
8486
Flurstück erfolgen.
8587
"""
88+
#: Lokationszuordnung, um bspw. die zugehörigen Marktlokationen anzugeben
89+
lokationszuordnungen: Optional[list["Lokationszuordnung"]] = None
90+
#: Lokationsbuendel Code, der die Funktion dieses BOs an der Lokationsbuendelstruktur beschreibt.
91+
lokationsbuendel_objektcode: Optional[str] = None

src/bo4e/bo/netzlokation.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
"""
2+
Contains Netzlokation class
3+
and corresponding marshmallow schema for de-/serialization
4+
"""
5+
6+
from typing import TYPE_CHECKING, Annotated, Optional
7+
8+
from pydantic import Field
9+
10+
from ..enum.typ import Typ
11+
from ..utils import postprocess_docstring
12+
from .geschaeftsobjekt import Geschaeftsobjekt
13+
14+
if TYPE_CHECKING:
15+
from ..bo.lokationszuordnung import Lokationszuordnung
16+
from ..com.konfigurationsprodukt import Konfigurationsprodukt
17+
from ..com.menge import Menge
18+
from ..com.verwendungszweckpromarktrolle import VerwendungszweckProMarktrolle
19+
from ..enum.marktrolle import Marktrolle
20+
from ..enum.sparte import Sparte
21+
22+
23+
# pylint: disable=too-many-instance-attributes, too-few-public-methods
24+
25+
26+
@postprocess_docstring
27+
class Netzlokation(Geschaeftsobjekt):
28+
"""
29+
Object containing information about a Netzlokation
30+
31+
.. raw:: html
32+
33+
<object data="../_static/images/bo4e/bo/Netzlokation.svg" type="image/svg+xml"></object>
34+
35+
.. HINT::
36+
`Netzlokation JSON Schema <https://json-schema.app/view/%23?url=https://raw.githubusercontent.com/BO4E/BO4E-Schemas/{__gh_version__}/src/bo4e_schemas/bo/Netzlokation.json>`_
37+
38+
"""
39+
40+
typ: Annotated[Optional[Typ], Field(alias="_typ")] = Typ.NETZLOKATION
41+
42+
#: Identifikationsnummer einer Netzlokation, an der Energie entweder verbraucht, oder erzeugt wird
43+
netzlokations_id: Optional[str] = None
44+
#: Sparte der Netzlokation, z.B. Gas oder Strom.
45+
sparte: Optional["Sparte"] = None
46+
#: Netzanschlussleistungsmenge der Netzlokation
47+
netzanschlussleistung: Optional["Menge"] = None
48+
#: Codenummer des grundzuständigen Messstellenbetreibers, der für diese Netzlokation zuständig ist.
49+
grundzustaendiger_msb_codenr: Optional[str] = None
50+
#: Ob ein Steuerkanal der Netzlokation zugeordnet ist und somit die Netzlokation gesteuert werden kann.
51+
steuerkanal: Optional[bool] = None
52+
#: Die OBIS-Kennzahl für die Netzlokation
53+
obiskennzahl: Optional[str] = None
54+
#: Verwendungungszweck der Werte Netzlokation
55+
verwendungszweck: Optional["VerwendungszweckProMarktrolle"] = None
56+
#: Produkt-Daten der Netzlokation
57+
konfigurationsprodukte: Optional[list["Konfigurationsprodukt"]] = None
58+
#: Eigenschaft des Messstellenbetreibers an der Lokation
59+
eigenschaft_msb_lokation: Optional["Marktrolle"] = None
60+
#: Lokationszuordnung, um bspw. die zugehörigen Messlokationen anzugeben
61+
lokationszuordnungen: Optional[list["Lokationszuordnung"]] = None
62+
#: Lokationsbuendel Code, der die Funktion dieses BOs an der Lokationsbuendelstruktur beschreibt.
63+
lokationsbuendel_objektcode: Optional[str] = None

0 commit comments

Comments
 (0)