generated from Hochfrequenz/python_template_repository
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
122 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
""" | ||
general data models for migrations | ||
""" | ||
import enum | ||
from typing import Generic, Iterable, Optional, Protocol, Type, TypeVar, Union | ||
|
||
import attrs | ||
from bo4e.bo.geschaeftsobjekt import Geschaeftsobjekt | ||
from bo4e.com.com import COM | ||
|
||
_SpecificBusinessObject = TypeVar("_SpecificBusinessObject", bound=Geschaeftsobjekt) | ||
""" | ||
an arbitrary but fixed business object type | ||
""" | ||
|
||
_SpecificCom = TypeVar("_SpecificCom", bound=COM) | ||
""" | ||
an arbitrary but fixed COM type | ||
""" | ||
|
||
Bo4eTyp = Union[_SpecificBusinessObject, _SpecificCom] | ||
|
||
|
||
# pylint:disable=too-few-public-methods | ||
@attrs.define(kw_only=True, auto_attribs=True) | ||
class BusinessObjectRelation: | ||
""" | ||
A business object relation describes the relation between two business object. | ||
E.g. a relation could have the type "has_melo" where relation_part_a is a bo4e.bo.Vertrag | ||
and relation_part_b is a bo4e.bo.Messlokation. Some relations are already defined in BO4E itself (e.g MaLo/MeLo) | ||
or MeLo/Address. | ||
The idea is to not enforce too much of a structure to the downstream code but still push coders to think about | ||
necessary relation information. | ||
""" | ||
|
||
relation_type: enum.Enum = attrs.field() | ||
""" | ||
The relation type describes how two business objects relate to each other. | ||
This is not (only) about cardinality. It's about being able to model different relations between objects. | ||
Think about e.g. a business partner and an address: The relation could be: | ||
- the address is the residential address of the business partner | ||
- the address is the invoice address of the business partner | ||
- the address is the place where the business partner was born | ||
All these relation types are 1:1 relations between business partners and adresses, yet they all carry different | ||
meaning which we'd like to distinguish in our data. | ||
""" | ||
relation_part_a: Bo4eTyp = attrs.field() | ||
""" | ||
one Business Object or COM | ||
""" | ||
|
||
relation_part_b: Bo4eTyp = attrs.field() | ||
""" | ||
another Business Object or COM | ||
""" | ||
|
||
|
||
class Bo4eDataSet(Protocol): | ||
""" | ||
A BO4E data set is a collection of Business Objects that relate to each other. | ||
This class just defines methods that any bo4e data set should implement (via structural subtyping) without forcing | ||
the data sets to inherit from a common base class. | ||
""" | ||
|
||
def get_relations(self) -> Iterable[BusinessObjectRelation]: | ||
""" | ||
returns all relations between the business objects | ||
""" | ||
|
||
def get_business_object(self, bo_type: Type[Bo4eTyp], specification: Optional[str] = None) -> Bo4eTyp: | ||
""" | ||
Returns a business object of the provided type from the collection. | ||
If the type alone is not unique, you can provide an additional specification. | ||
""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import enum | ||
from typing import Iterable, Optional, Type | ||
|
||
import pytest # type:ignore[import] | ||
from bo4e.bo.geschaeftspartner import Geschaeftspartner | ||
from bo4e.com.adresse import Adresse | ||
from bo4e.enum.strenum import StrEnum | ||
|
||
from bomf.model import Bo4eDataSet, Bo4eTyp, BusinessObjectRelation | ||
|
||
|
||
class _GeschaeftspartnerAdresseRelation(enum.Enum): | ||
HAS_LIEFERANSCHRIFT = 1 | ||
HAS_RECHNUNGSANSCHRIFT = 2 | ||
HAS_GEBURTSORT = 3 | ||
|
||
|
||
class _ExampleDataSet: | ||
def __init__(self): | ||
self.business_partner = Geschaeftspartner.construct(name1="Müller", name2="Hans") | ||
self.address = Adresse.construct(strasse="Rechnungsstrasse", hausnummer="5") | ||
|
||
def get_relations(self) -> Iterable[BusinessObjectRelation]: | ||
return [ | ||
BusinessObjectRelation( | ||
relation_type=_GeschaeftspartnerAdresseRelation.HAS_LIEFERANSCHRIFT, | ||
relation_part_a=self.business_partner, | ||
relation_part_b=self.address, | ||
) | ||
] | ||
|
||
def get_business_object(self, bo_type: Type[Bo4eTyp], specification: Optional[str] = None) -> Bo4eTyp: | ||
# pyling:disable=fixme | ||
# todo: find out how to allow the static type checker to not complain about the "dynamic" type | ||
if bo_type == Geschaeftspartner: | ||
return self.business_partner # type:ignore[return-value] | ||
if bo_type == Adresse: | ||
return self.address # type:ignore[return-value] | ||
raise NotImplementedError(f"The bo type {bo_type} is not implemented") | ||
|
||
|
||
class TestBo4eDataSet: | ||
async def test_example_data_set(self): | ||
dataset: Bo4eDataSet = _ExampleDataSet() | ||
assert len(list(dataset.get_relations())) == 1 | ||
assert isinstance(dataset.get_business_object(Geschaeftspartner), Geschaeftspartner) | ||
assert isinstance(dataset.get_business_object(Adresse), Adresse) |