Skip to content

Commit

Permalink
✨ Add MausProvider (#78)
Browse files Browse the repository at this point in the history
This basically ports code that has been introduced in AHBicht to the MAUS:
Hochfrequenz/ahbicht#158

* ✨ Implement MAUS Provider

* ✔Add unit tests
  • Loading branch information
hf-kklein authored May 2, 2022
1 parent af7b65d commit 1ba7b45
Show file tree
Hide file tree
Showing 2 changed files with 130 additions and 0 deletions.
67 changes: 67 additions & 0 deletions src/maus/maus_provider.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
"""
MAUS s (plural of MAUS) are data structures which are structurally equivalent to the maus.DeepAnwendungshandbuch.
For more information see the MAUS repo and its documentation: https://github.com/Hochfrequenz/mig_ahb_utility_stack/
A MAUS Provider is a class that returns MAUS s' from what ever data source the implementation prefers.
The MAUS provider is supposed to be used with dependency injection.
"""

import json
from abc import ABC, abstractmethod
from pathlib import Path
from typing import Optional

from maus import DeepAnwendungshandbuch
from maus.edifact import EdifactFormat, EdifactFormatVersion
from maus.models.anwendungshandbuch import DeepAnwendungshandbuchSchema

# pylint:disable=too-few-public-methods


class MausProvider(ABC):
"""
A MausProvider is a class that provides MAUS' (Deep Anwendungshandbuch) to calling code.
"""

@abstractmethod
def get_maus(
self, edifact_format: EdifactFormat, edifact_format_version: EdifactFormatVersion, pruefidentifikator: str
) -> Optional[DeepAnwendungshandbuch]:
"""
Return a MAUS for the given parameters. returns None if the requested MAUS is not available.
"""
raise NotImplementedError("Has to be implemented in inheriting class")


class FileBasedMausProvider(MausProvider):
"""
A MAUS provider that uses the file system to retrieve MAUS s.
"""

def __init__(self, base_path: Path, encoding: str = "utf-8"):
"""
initialize by providing a base path relative to which the MAUS s can be found.
"""
self.base_path: Path = base_path
self._encoding = encoding

@abstractmethod
def to_path(
self, edifact_format: EdifactFormat, edifact_format_version: EdifactFormatVersion, pruefidentifikator: str
) -> Path:
"""
returns the path of the maus file relative to the given parameters.
"""
raise NotImplementedError("Has to be implemented in inheriting class")

def get_maus(
self, edifact_format: EdifactFormat, edifact_format_version: EdifactFormatVersion, pruefidentifikator: str
) -> Optional[DeepAnwendungshandbuch]:
relative_path = self.to_path(edifact_format, edifact_format_version, pruefidentifikator)
full_path: Path = self.base_path / relative_path
try:
with open(full_path, "r", encoding=self._encoding) as maus_infile:
file_content_json = json.load(maus_infile)
maus = DeepAnwendungshandbuchSchema().load(file_content_json)
except FileNotFoundError:
return None
return maus
63 changes: 63 additions & 0 deletions unit_tests/test_maus_provider.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
"""
Test the maus provider as a concept and the file based maus provider as an implementation.
"""
import json
from pathlib import Path

import pytest # type:ignore[import]

from maus import DeepAnwendungshandbuch
from maus.edifact import EdifactFormat, EdifactFormatVersion
from maus.maus_provider import FileBasedMausProvider, MausProvider
from maus.models.anwendungshandbuch import AhbMetaInformation, DeepAnwendungshandbuchSchema

pytestmark = pytest.mark.asyncio


class MyFooBarMausProvider(FileBasedMausProvider):
"""
A maus provider, just for this test.
"""

def to_path(
self, edifact_format: EdifactFormat, edifact_format_version: EdifactFormatVersion, pruefidentifikator: str
) -> Path:
return Path(f"{edifact_format_version}/{edifact_format}/{pruefidentifikator}_maus.json")


class TestMausProvider:
"""
Test the file based maus provider
"""

def test_file_based_maus_provider_not_found(self, tmpdir_factory):
maus_root_dir = tmpdir_factory.mktemp("test_dir")
# we just create an empty temporary directory, just for the sake of having a directory
provider: MausProvider = MyFooBarMausProvider(base_path=maus_root_dir)
assert provider is not None
actual = provider.get_maus(
edifact_format=EdifactFormat.UTILMD,
edifact_format_version=EdifactFormatVersion.FV2104,
pruefidentifikator="11001",
)
assert actual is None # because nothing was found

def test_file_based_maus_provider_success(self, tmpdir_factory):
maus_root_dir = tmpdir_factory.mktemp("test_dir")
# no we create something more: a directory structure compatible with the above MyFooBarMausProvider
maus_root_dir.mkdir("FV2104")
maus_root_dir.mkdir("FV2104/UTILMD")
# a minimal maus that is serializable and deserializable
example_maus = DeepAnwendungshandbuch(meta=AhbMetaInformation(pruefidentifikator="11001"), lines=[])
with open(maus_root_dir / "FV2104/UTILMD/11001_maus.json", "w+") as maus_test_outfile:
deep_ahb_dict = DeepAnwendungshandbuchSchema().dump(example_maus) # create a dictionary
json.dump(deep_ahb_dict, maus_test_outfile) # dump the dictionary to the file
provider: MausProvider = MyFooBarMausProvider(base_path=maus_root_dir)
assert provider is not None
actual = provider.get_maus(
edifact_format=EdifactFormat.UTILMD,
edifact_format_version=EdifactFormatVersion.FV2104,
pruefidentifikator="11001",
)
assert actual is not None # because the file was found
assert actual == example_maus # is equivalent to the original because it was read from the file

0 comments on commit 1ba7b45

Please sign in to comment.