Skip to content

Commit 409f63a

Browse files
authored
feat: Add SQL Models for Anwendungshandbuecher (#85)
1 parent eeefb04 commit 409f63a

8 files changed

+598
-9
lines changed

pyproject.toml

+4
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ dependencies = [
2323
dynamic = ["readme", "version"]
2424

2525
[project.optional-dependencies]
26+
sqlmodels = [
27+
"sqlmodel>=0.0.22",
28+
"sqlalchemy[mypy]>=2.0.37"
29+
]
2630
coverage = [
2731
"coverage==7.6.10"
2832
]

requirements.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# This file is autogenerated by pip-compile with Python 3.12
33
# by the following command:
44
#
5-
# pip-compile '.\pyproject.toml'
5+
# pip-compile pyproject.toml
66
#
77
annotated-types==0.7.0
88
# via pydantic

src/fundamend/sqlmodels/__init__.py

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
"""
2+
models that work together with SQLModel+SQLAlchemy
3+
you need to install fundamend[sqlmodels] to use this sub-package
4+
"""
5+
6+
# the models here do NOT inherit from the original models, because I didn't manage to fix the issue that arise:
7+
# <frozen abc>:106: in __new__
8+
# ???
9+
# E TypeError: Anwendungshandbuch.__init_subclass__() takes no keyword arguments
10+
#
11+
# or
12+
#
13+
# ..\.tox\dev\Lib\site-packages\sqlmodel\main.py:697: in get_sqlalchemy_type
14+
# raise ValueError(f"{type_} has no matching SQLAlchemy type")
15+
# E ValueError: <class 'fundamend.models.anwendungshandbuch.Anwendungshandbuch'> has no matching SQLAlchemy type
16+
# => you need to keep the models in sync manually by now

src/fundamend/sqlmodels/anwendungshandbuch.py

+499
Large diffs are not rendered by default.

tox.ini

+4
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ commands = python -m pip install --upgrade pip
1515
deps =
1616
-r requirements.txt
1717
.[tests]
18+
.[sqlmodels]
1819
setenv = PYTHONPATH = {toxinidir}/src
1920
commands = python -m pytest --basetemp={envtmpdir} {posargs} -vv
2021

@@ -31,6 +32,7 @@ deps =
3132
{[testenv:tests]deps}
3233
.[linting]
3334
.[cli]
35+
.[sqlmodels]
3436
# add your fixtures like e.g. pytest_datafiles here
3537
setenv = PYTHONPATH = {toxinidir}/src
3638
commands =
@@ -45,6 +47,7 @@ deps =
4547
{[testenv:tests]deps}
4648
.[type_check]
4749
.[cli]
50+
.[sqlmodels]
4851
commands =
4952
mypy --show-error-codes src/fundamend --strict
5053
mypy --show-error-codes unittests --strict
@@ -68,6 +71,7 @@ deps =
6871
{[testenv:tests]deps}
6972
.[coverage]
7073
.[cli]
74+
.[sqlmodels]
7175
setenv = PYTHONPATH = {toxinidir}/src
7276
commands =
7377
coverage run -m pytest --basetemp={envtmpdir} {posargs}

unittests/conftest.py

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from pathlib import Path
2+
3+
private_submodule_root = Path(__file__).parent.parent / "xml-migs-and-ahbs"
4+
5+
6+
def is_private_submodule_checked_out() -> bool:
7+
return any(private_submodule_root.iterdir())

unittests/test_ahb_serialisierung.py

+6-8
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,9 @@
55

66
from fundamend.reader import AhbReader, MigReader
77

8-
data_path: Path = Path(__file__).parent.parent / "xml-migs-and-ahbs"
9-
8+
from .conftest import is_private_submodule_checked_out
109

11-
def private_submodule_is_checked_out() -> bool:
12-
return any(data_path.iterdir())
10+
data_path: Path = Path(__file__).parent.parent / "xml-migs-and-ahbs"
1311

1412

1513
@pytest.mark.parametrize(
@@ -98,15 +96,15 @@ def private_submodule_is_checked_out() -> bool:
9896
],
9997
)
10098
def test_read_ahb_xml(ahb_xml_file_path: Path, expected_date: date) -> None:
101-
if not private_submodule_is_checked_out():
99+
if not is_private_submodule_checked_out():
102100
pytest.skip("Skipping test because of missing private submodule")
103101
reader = AhbReader(ahb_xml_file_path)
104102
actual = reader.get_publishing_date()
105103
assert actual == expected_date
106104

107105

108106
def test_deserializing_all_ahbs() -> None:
109-
if not private_submodule_is_checked_out():
107+
if not is_private_submodule_checked_out():
110108
pytest.skip("Skipping test because of missing private submodule")
111109
for ahb_file_path in data_path.rglob("**/*AHB*.xml"):
112110
reader = AhbReader(ahb_file_path)
@@ -199,15 +197,15 @@ def test_deserializing_all_ahbs() -> None:
199197
],
200198
)
201199
def test_read_mig_xml(mig_xml_file_path: Path, expected_date: date) -> None:
202-
if not private_submodule_is_checked_out():
200+
if not is_private_submodule_checked_out():
203201
pytest.skip("Skipping test because of missing private submodule")
204202
reader = MigReader(mig_xml_file_path)
205203
actual = reader.get_publishing_date()
206204
assert actual == expected_date
207205

208206

209207
def test_deserializing_all_migs() -> None:
210-
if not private_submodule_is_checked_out():
208+
if not is_private_submodule_checked_out():
211209
pytest.skip("Skipping test because of missing private submodule")
212210
for mig_file_path in data_path.rglob("**/*MIG*.xml"):
213211
reader = MigReader(mig_file_path)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
"""
2+
we try to fill a database using kohlrahbi[sqlmodels] and the data from the machine-readable AHB submodule
3+
"""
4+
5+
from pathlib import Path
6+
from typing import Generator
7+
8+
import pytest
9+
from sqlmodel import Session, SQLModel, create_engine
10+
11+
from fundamend import AhbReader
12+
from fundamend.models.anwendungshandbuch import Anwendungshandbuch as PydanticAnwendunghandbuch
13+
from fundamend.sqlmodels.anwendungshandbuch import Anwendungshandbuch as SqlAnwendungshandbuch
14+
15+
from .conftest import is_private_submodule_checked_out
16+
17+
18+
@pytest.fixture()
19+
def sqlite_session(tmp_path: Path) -> Generator[Session, None, None]:
20+
database_path = tmp_path / "test.db"
21+
engine = create_engine(f"sqlite:///{database_path}")
22+
SQLModel.metadata.drop_all(engine)
23+
SQLModel.metadata.create_all(engine)
24+
with Session(bind=engine) as session:
25+
yield session
26+
session.commit()
27+
session.flush()
28+
print(f"Wrote all data to {database_path.absolute()}")
29+
30+
31+
def _load_anwendungshandbuch_ahb_to_and_from_db(
32+
session: Session, pydantic_ahb: PydanticAnwendunghandbuch
33+
) -> PydanticAnwendunghandbuch:
34+
sql_ahb = SqlAnwendungshandbuch.from_model(pydantic_ahb)
35+
session.add(sql_ahb)
36+
session.commit()
37+
session.refresh(sql_ahb)
38+
pydantic_ahb_after_roundtrip = sql_ahb.to_model()
39+
return pydantic_ahb_after_roundtrip
40+
41+
42+
def test_sqlmodels_single_anwendungshandbuch(sqlite_session: Session) -> None:
43+
ahb = AhbReader(
44+
Path(__file__).parent / "example_files" / "UTILTS_AHB_1.1d_Konsultationsfassung_2024_04_02.xml"
45+
).read()
46+
roundtrip_abb = _load_anwendungshandbuch_ahb_to_and_from_db(sqlite_session, ahb)
47+
ahb_json = ahb.model_dump(mode="json")
48+
roundtrip_json = roundtrip_abb.model_dump(mode="json")
49+
assert ahb_json == roundtrip_json # in pycharm the error message is much better when comparing plain python dicts
50+
assert roundtrip_abb == ahb
51+
52+
53+
def test_sqlmodels_all_anwendungshandbuch(sqlite_session: Session) -> None:
54+
if not is_private_submodule_checked_out():
55+
pytest.skip("Skipping test because of missing private submodule")
56+
private_submodule_root = Path(__file__).parent.parent / "xml-migs-and-ahbs"
57+
assert private_submodule_root.exists() and private_submodule_root.is_dir()
58+
for ahb_file_path in private_submodule_root.rglob("**/*AHB*.xml"):
59+
ahb = AhbReader(ahb_file_path).read()
60+
roundtrip_abb = _load_anwendungshandbuch_ahb_to_and_from_db(sqlite_session, ahb)
61+
assert roundtrip_abb == ahb

0 commit comments

Comments
 (0)