From 8b43995bbfbddf5169d14bd7619bc9245a16c421 Mon Sep 17 00:00:00 2001 From: vvmruder Date: Mon, 9 Oct 2023 20:45:41 +0200 Subject: [PATCH] Finalize project (#16) * finalize readme and add information how interface was created * add doc, add tests --- .coveragerc | 10 +- Makefile | 2 +- doc/source/basics.rst | 23 + doc/source/cli.rst | 25 + doc/source/conf.py | 11 +- doc/source/description.rst | 2 - doc/source/geolink2oereb.rst | 7 +- doc/source/index.rst | 9 +- doc/source/interfaces.rst | 71 + setup.py | 2 +- src/geolink2oereb/cli.py | 26 +- .../oerebkrmtrsfr/v2_0/OeREBKRMtrsfr_V2_0.ili | 129 + .../oerebkrmtrsfr/v2_0/OeREBKRMtrsfr_V2_0.xsd | 2525 +++++++++++++++++ .../interfaces/oerebkrmtrsfr/v2_0/README.md | 28 +- .../interfaces/oerebkrmtrsfr/v2_0/classes.py | 4 +- .../oerebkrmtrsfr/v2_0/generators.py | 58 +- .../lib/interfaces/pyramid_oereb/__init__.py | 146 +- src/geolink2oereb/transform.py | 47 +- .../oerebkrmtrsfr/v2_0/test_generators.py | 127 +- tests/lib/interfaces/test_pyramid_oereb.py | 557 ++++ tests/lib/test_transform.py | 180 ++ 21 files changed, 3916 insertions(+), 73 deletions(-) create mode 100644 doc/source/basics.rst create mode 100644 doc/source/cli.rst delete mode 100644 doc/source/description.rst create mode 100644 doc/source/interfaces.rst create mode 100644 src/geolink2oereb/lib/interfaces/oerebkrmtrsfr/v2_0/OeREBKRMtrsfr_V2_0.ili create mode 100644 src/geolink2oereb/lib/interfaces/oerebkrmtrsfr/v2_0/OeREBKRMtrsfr_V2_0.xsd create mode 100644 tests/lib/interfaces/test_pyramid_oereb.py create mode 100644 tests/lib/test_transform.py diff --git a/.coveragerc b/.coveragerc index c1ca431..4225351 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,5 +1,13 @@ [run] source = - src/geolink2oereb + src omit = src/geolink2oereb/lib/interfaces/oerebkrmtrsfr/v2_0/classes.py + src/geolink2oereb/cli.py + setup.py + tests/*.py +[report] +skip_covered = True +show_missing = True +[xml] +output = coverage.xml diff --git a/Makefile b/Makefile index f522c79..1c412d9 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,7 @@ lint: .venv/requirements.timestamp .PHONY: test test: .venv/requirements.timestamp - .venv/bin/py.test -vv --cov-config .coveragerc --cov src/geolink2oereb --cov-report=xml:coverage.xml tests + .venv/bin/py.test -vvv --cov --cov-config .coveragerc --cov-report=xml --cov-report=term tests .PHONY: check diff --git a/doc/source/basics.rst b/doc/source/basics.rst new file mode 100644 index 0000000..e59e7d5 --- /dev/null +++ b/doc/source/basics.rst @@ -0,0 +1,23 @@ +Basic info +========== + + +This is a small library, meant to be used in combination with an *OEREBlex* instance. +It is capable of parsing a received *lexlink* response (XML) and transforming it to the +`OeREBKRMtrsfr_V2_0 `_ structure for further +usage. + +To make the configuration more easy it is using the pyramid_oereb library to reuse the +complex ÖREBlex +`configuration `_ +and ÖREBlex +`parsing `_ +already developed there. + +For development and integration purposes geolink2oereb offers a :ref:`CLI` executable. But its +main use case is to be utilized as a library in another python libraries context. + +See the mgdm2oereb implementation as an example: + +`mgdm2oereb => oereblex.geolink2oereb `_ + diff --git a/doc/source/cli.rst b/doc/source/cli.rst new file mode 100644 index 0000000..382b06d --- /dev/null +++ b/doc/source/cli.rst @@ -0,0 +1,25 @@ + +CLI +=== + +The command line interface of geolink2oereb. + +Once installed as python package geolink2oereb offers a command line +interface to issue the transformation of one lexlink id to the +OEREB transfer structure. To get info about the parameters the cli programm +needs, you can issue the execution as follows: + +.. code-block:: shell + + load_documents --help + +You can call the tool like this: + +.. code-block:: shell + + load_documents -l 4304 -t ch.Planungszonen -p /config.yaml + +Please keep in mind, that the binary executable is available only in the +python path where you installed it. If you installed it with +a VENV you might prefix it with the path to you VENV's bin directory. + diff --git a/doc/source/conf.py b/doc/source/conf.py index dd72dd8..235a1a5 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # -# OEREBlex geoLink Formatter documentation build configuration file, created by -# sphinx-quickstart on Tue May 16 13:06:47 2017. +# geolink2oereb documentation build configuration file, created by +# sphinx-quickstart on Tue Oct 08 13:06:47 2023. # # This file is execfile()d with the current directory set to its # containing dir. @@ -37,12 +37,14 @@ extensions = [ + 'sphinx_rtd_theme', 'sphinx.ext.autodoc', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.ifconfig', 'sphinx.ext.viewcode', - 'sphinx.ext.napoleon' + 'sphinx.ext.napoleon', + 'sphinx.ext.autosectionlabel' ] # Add any paths that contain templates here, relative to this directory. @@ -175,3 +177,6 @@ # Napoleon configuration napoleon_numpy_docstring = False +napoleon_google_docstring = True +napoleon_include_init_with_doc = True +napoleon_preprocess_types = True diff --git a/doc/source/description.rst b/doc/source/description.rst deleted file mode 100644 index f011bc4..0000000 --- a/doc/source/description.rst +++ /dev/null @@ -1,2 +0,0 @@ -This is a small library, meant to be used in combination with *OEREBlex*. It is capable of parsing a received -*geoLink* response (XML) and transforming it to the OeREBKRMtrsfr structure for further usage. \ No newline at end of file diff --git a/doc/source/geolink2oereb.rst b/doc/source/geolink2oereb.rst index baa27bb..6e30918 100644 --- a/doc/source/geolink2oereb.rst +++ b/doc/source/geolink2oereb.rst @@ -1,4 +1,5 @@ -Module *geolink2oereb* -========================== +Module *transform* +================== -.. automodule:: geolink2oereb +.. automodule:: geolink2oereb.transform + :members: diff --git a/doc/source/index.rst b/doc/source/index.rst index f8ebb2b..1bf7663 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -1,4 +1,3 @@ - OEREBlex geolink2oereb ====================== @@ -6,9 +5,10 @@ OEREBlex geolink2oereb :maxdepth: 2 :hidden: + basics + cli geolink2oereb - -.. include:: description.rst + interfaces Indices and tables @@ -17,6 +17,3 @@ Indices and tables * :ref:`genindex` * :ref:`modindex` * :ref:`search` - - -run with `documents = run_batch([4304,4305,4305], 'ch.Planungszonen', 'config_gr.yaml', 'pyramid_oereb')` \ No newline at end of file diff --git a/doc/source/interfaces.rst b/doc/source/interfaces.rst new file mode 100644 index 0000000..4a7265e --- /dev/null +++ b/doc/source/interfaces.rst @@ -0,0 +1,71 @@ +Interfaces +========== + +*pyramid_oereb* +--------------- + +.. automodule:: geolink2oereb.lib.interfaces.pyramid_oereb + :members: + +*OeREBKRMtrsfr_V2_0 generators* +------------------------------- + +.. automodule:: geolink2oereb.lib.interfaces.oerebkrmtrsfr.v2_0.generators + :members: + +*OeREBKRMtrsfr_V2_0 classes* +------------------------------- + +This is a selection of classes available to adapt to ``OeREBKRMtrsfr_V2_0``. The selection shows the classes +used in the current implementation. Please have a look in the source file to see the complete implementation. + +.. autoclass:: geolink2oereb.lib.interfaces.oerebkrmtrsfr.v2_0.classes.OeREBKRM_V2_0_Dokumente_Dokument + :members: + :undoc-members: + +.. autoclass:: geolink2oereb.lib.interfaces.oerebkrmtrsfr.v2_0.classes.OeREBKRM_V2_0_Amt_Amt + :members: + :undoc-members: + +.. autoclass:: geolink2oereb.lib.interfaces.oerebkrmtrsfr.v2_0.classes.ZustaendigeStelleType + :members: + :undoc-members: + +.. autoclass:: geolink2oereb.lib.interfaces.oerebkrmtrsfr.v2_0.classes.TitelType + :members: + :undoc-members: + +.. autoclass:: geolink2oereb.lib.interfaces.oerebkrmtrsfr.v2_0.classes.TextImWebType + :members: + :undoc-members: + +.. autoclass:: geolink2oereb.lib.interfaces.oerebkrmtrsfr.v2_0.classes.OeREBKRM_V2_0_MultilingualUri + :members: + :undoc-members: + +.. autoclass:: geolink2oereb.lib.interfaces.oerebkrmtrsfr.v2_0.classes.OeREBKRM_V2_0_LocalisedUri + :members: + :undoc-members: + +.. autoclass:: geolink2oereb.lib.interfaces.oerebkrmtrsfr.v2_0.classes.LocalisedTextType + :members: + :undoc-members: + +.. autoclass:: geolink2oereb.lib.interfaces.oerebkrmtrsfr.v2_0.classes.LocalisedTextType86 + :members: + :undoc-members: + +.. autoclass:: geolink2oereb.lib.interfaces.oerebkrmtrsfr.v2_0.classes.LocalisationCH_V1_LocalisedText + :members: + :undoc-members: + +.. autoclass:: geolink2oereb.lib.interfaces.oerebkrmtrsfr.v2_0.classes.LocalisationCH_V1_MultilingualText + :members: + :undoc-members: + +``geolink2oereb.lib.interfaces.oerebkrmtrsfr.v2_0.classes.GeneratedsSuper`` is the superclass all above +mentioned classes are inheriting from. + +.. autoclass:: geolink2oereb.lib.interfaces.oerebkrmtrsfr.v2_0.classes.GeneratedsSuper + :members: + :undoc-members: diff --git a/setup.py b/setup.py index 02cd4ce..376141a 100644 --- a/setup.py +++ b/setup.py @@ -18,7 +18,7 @@ tests_require = [ 'WebTest >= 1.3.1', # py3 compat - 'pytest', # includes virtualenv + 'pytest==7.4.2', # includes virtualenv 'pytest-cov' ] diff --git a/src/geolink2oereb/cli.py b/src/geolink2oereb/cli.py index 48fc35c..4604743 100644 --- a/src/geolink2oereb/cli.py +++ b/src/geolink2oereb/cli.py @@ -1,7 +1,7 @@ import optparse import logging -import uuid +from io import StringIO from geolink2oereb.transform import run logging.basicConfig(level="DEBUG", format="%(asctime)s [%(levelname)s] %(message)s") @@ -19,7 +19,7 @@ def geolink2oereb(): "--geolink_id", dest="geolink_id", metavar="GEOLINKID", - type="integer", + type="int", help="The the ID to load the documents for.", ) parser.add_option( @@ -66,8 +66,9 @@ def geolink2oereb(): "-o", "--outfile-path", dest="outfile_path", - default=f"/tmp/{str(uuid.uuid4())}.xml", - help="The absolute path where the output will be written to.", + default=None, + help="The absolute path where the output will be written to." + "If omitted, the output will be printed as command output to the console.", ) options, args = parser.parse_args() @@ -79,6 +80,17 @@ def geolink2oereb(): options.source_class_path, options.c2ctemplate_style, ) - with open(options.outfile_path) as fh: - for element in oerebkrmtrsfr: - fh.write(str(element)) + out_string_list = [] + + for lexlink_group in oerebkrmtrsfr: + for element in lexlink_group: + output = StringIO() + element.export(output, 0, namespacedef_=None) + out_string_list.append(output.getvalue()) + output.close() + out_string = '\n'.join(out_string_list) + if options.outfile_path is None: + print(out_string) + else: + with open(options.outfile_path, mode="w+") as fh: + fh.write(out_string) diff --git a/src/geolink2oereb/lib/interfaces/oerebkrmtrsfr/v2_0/OeREBKRMtrsfr_V2_0.ili b/src/geolink2oereb/lib/interfaces/oerebkrmtrsfr/v2_0/OeREBKRMtrsfr_V2_0.ili new file mode 100644 index 0000000..d962bff --- /dev/null +++ b/src/geolink2oereb/lib/interfaces/oerebkrmtrsfr/v2_0/OeREBKRMtrsfr_V2_0.ili @@ -0,0 +1,129 @@ +INTERLIS 2.3; + +/** Transferstruktur: Schnittstelle zwischen der für die Geobasisdaten zuständigen Stelle und der für den Kataster verantwortlichen Stelle des Kantons + */ +!!@ furtherInformation=https://www.cadastre.ch/oereb-public +!!@ technicalContact=mailto:vermessung@swisstopo.ch +MODEL OeREBKRMtrsfr_V2_0 (de) +AT "https://models.geo.admin.ch/V_D/OeREB/" +VERSION "2021-04-14" = + IMPORTS OeREBKRM_V2_0,LocalisationCH_V1,GeometryCHLV95_V1; + + /** Dieses Teilmodell definiert die Struktur der Daten, wie diese von der zuständigen Stelle an die für den Kataster verantwortliche Stelle des Kantons geliefert werden müssen + */ + TOPIC Transferstruktur + EXTENDS OeREBKRM_V2_0.Dokumente = + + /** Angaben zum Darstellungsdienst + */ + CLASS DarstellungsDienst = + /** WMS GetMap-Request (für Maschine-Maschine-Kommunikation) inkl. alle benötigten Parameter, z.B. «https://wms.geo.admin.ch/?SERVICE=WMS&REQUEST=GetMap&VERSION=1.3.0&STYLES=default&CRS=EPSG:2056&BBOX=2475000,1060000,2845000,1310000&WIDTH=740&HEIGHT=500&FORMAT=image/png&LAYERS=ch.bazl.kataster-belasteter-standorte-zivilflugplaetze.oereb» + */ + VerweisWMS : MANDATORY OeREBKRM_V2_0.MultilingualUri; + END DarstellungsDienst; + + /** Wurzelelement für Informationen über eine Beschränkung des Grundeigentums, die rechtskräftig, z.B. auf Grund einer Genehmigung oder eines richterlichen Entscheids, zustande gekommen ist + */ + CLASS Eigentumsbeschraenkung = + /** Status, ob diese Eigentumsbeschränkung in Kraft ist + */ + Rechtsstatus : MANDATORY OeREBKRM_V2_0.RechtsStatus; + /** Datum, ab dem diese Eigentumsbeschränkung in Auszügen erscheint + */ + publiziertAb : MANDATORY OeREBKRM_V2_0.Datum; + /** Datum, an dem diese Eigentumsbeschränkung letztmalig in Auszügen erscheint + */ + publiziertBis : OeREBKRM_V2_0.Datum; + END Eigentumsbeschraenkung; + + /** Punkt-, linien-, oder flächenförmige Geometrie; neu zu definierende Eigentumsbeschränkungen sollten in der Regel flächenförmig sein + */ + CLASS Geometrie = + /** Punktgeometrie + */ + Punkt : GeometryCHLV95_V1.Coord2; + /** Linienförmige Geometrie + */ + Linie : GeometryCHLV95_V1.Line; + /** Flächenförmige Geometrie + */ + Flaeche : GeometryCHLV95_V1.Surface; + /** Status, ob diese Geometrie in Kraft ist + */ + Rechtsstatus : MANDATORY OeREBKRM_V2_0.RechtsStatus; + /** Datum, ab dem diese Geometrie in Auszügen erscheint + */ + publiziertAb : MANDATORY OeREBKRM_V2_0.Datum; + /** Datum, an dem diese Geometrie letztmalig in Auszügen erscheint + */ + publiziertBis : OeREBKRM_V2_0.Datum; + /** Verweis auf maschinenlesbare Metadaten (XML) der zugrundeliegenden Geobasisdaten, z.B. «http://www.geocat.ch/geonetwork/srv/deu/gm03.xml?id=705» + */ + MetadatenGeobasisdaten : URI; + MANDATORY CONSTRAINT DEFINED(Punkt) OR DEFINED(Linie) OR DEFINED(Flaeche); + END Geometrie; + + /** Ein Eintrag in der Planlegende + */ + CLASS LegendeEintrag = + /** Grafischer Teil des Legendeneintrages für die Darstellung im PNG-Format mit 300dpi oder im SVG-Format + */ + Symbol : MANDATORY BLACKBOX BINARY; + /** Text des Legendeneintrages + */ + LegendeText : MANDATORY LocalisationCH_V1.MultilingualText; + /** Art der Eigentumsbeschränkung, die durch diesen Legendeneintrag dargestellt wird + */ + ArtCode : MANDATORY OeREBKRM_V2_0.ArtEigentumsbeschraenkung; + /** Codeliste der Eigentumsbeschränkung, die durch diesen Legendeneintrag dargestellt wird + */ + ArtCodeliste : MANDATORY URI; + /** Zu welchem ÖREB-Thema der Legendeneintrag gehört + */ + Thema : MANDATORY OeREBKRM_V2_0.Thema; + /** Z.B. für «Überlagernde Festlegungen» innerhalb Nutzungsplanung + */ + SubThema : OeREBKRM_V2_0.Thema; + END LegendeEintrag; + + ASSOCIATION DarstellungsDienstEigentumsbeschraenkung = + /** Darstellungsdienst, auf dem diese Eigentumsbeschränkung sichtbar, aber nicht hervorgehoben, ist + */ + DarstellungsDienst -- {1} DarstellungsDienst; + Eigentumsbeschraenkung -<> {1..*} Eigentumsbeschraenkung; + END DarstellungsDienstEigentumsbeschraenkung; + + ASSOCIATION EigentumsbeschraenkungLegende = + Eigentumsbeschraenkung -<> {0..*} Eigentumsbeschraenkung; + Legende -- {1} LegendeEintrag; + END EigentumsbeschraenkungLegende; + + ASSOCIATION GeometrieEigentumsbeschraenkung = + /** Geometrie der Eigentumsbeschränkung, die Rechtswirkung hat (als Basis für den Verschnitt mit den Liegenschaften) + */ + Geometrie -- {1..*} Geometrie; + Eigentumsbeschraenkung -<#> {1} Eigentumsbeschraenkung; + END GeometrieEigentumsbeschraenkung; + + ASSOCIATION HinweisVorschrift = + Eigentumsbeschraenkung -- {0..*} Eigentumsbeschraenkung; + /** Rechtsvorschrift/Hinweis zur Eigentumsbeschränkung + */ + Vorschrift -- {1..*} OeREBKRM_V2_0.Dokumente.Dokument; + END HinweisVorschrift; + + ASSOCIATION LegendeDarstellungsdienst = + Legende -- {1..*} LegendeEintrag; + DarstellungsDienst -<#> {1} DarstellungsDienst; + END LegendeDarstellungsdienst; + + ASSOCIATION ZustaendigeStelleEigentumsbeschraenkung = + /** Zuständige Stelle für die Geobasisdaten (Originaldaten) gemäss GeoIG Art. 8 Abs. 1 + */ + ZustaendigeStelle (EXTERNAL) -- {1} OeREBKRM_V2_0.Amt.Amt; + Eigentumsbeschraenkung -<> {0..*} Eigentumsbeschraenkung; + END ZustaendigeStelleEigentumsbeschraenkung; + + END Transferstruktur; + +END OeREBKRMtrsfr_V2_0. diff --git a/src/geolink2oereb/lib/interfaces/oerebkrmtrsfr/v2_0/OeREBKRMtrsfr_V2_0.xsd b/src/geolink2oereb/lib/interfaces/oerebkrmtrsfr/v2_0/OeREBKRMtrsfr_V2_0.xsd new file mode 100644 index 0000000..ac42227 --- /dev/null +++ b/src/geolink2oereb/lib/interfaces/oerebkrmtrsfr/v2_0/OeREBKRMtrsfr_V2_0.xsd @@ -0,0 +1,2525 @@ + + + + 5.2.8-61f7212579ac3c4ae219d511afb5a0615855bc73 + + AdministrativeUnitsCH_V1 + 2020-04-24 + http://www.geo.admin.ch + ili23AnnexB + + + AdministrativeUnits_V1 + 2022-10-06 + http://www.geo.admin.ch + ili23AnnexB + + + CHAdminCodes_V1 + 2018-02-19 + http://www.geo.admin.ch + ili23AnnexB + + + CoordSys + 2015-11-24 + http://www.interlis.ch/models + ili23AnnexB + + + DictionariesCH_V1 + 2011-08-30 + http://www.geo.admin.ch + ili23AnnexB + + + Dictionaries_V1 + 2011-08-30 + http://www.geo.admin.ch + ili23AnnexB + + + GeometryCHLV03_V1 + 2017-12-04 + http://www.geo.admin.ch + ili23AnnexB + + + GeometryCHLV95_V1 + 2017-12-04 + http://www.geo.admin.ch + ili23AnnexB + + + InternationalCodes_V1 + 2011-08-30 + http://www.geo.admin.ch + ili23AnnexB + + + LocalisationCH_V1 + 2011-08-30 + http://www.geo.admin.ch + ili23AnnexB + + + Localisation_V1 + 2011-08-30 + http://www.geo.admin.ch + ili23AnnexB + + + OeREBKRM_V2_0 + 2021-04-14 + https://models.geo.admin.ch/V_D/OeREB/ + ili23AnnexB + + + OeREBKRMtrsfr_V2_0 + 2021-04-14 + https://models.geo.admin.ch/V_D/OeREB/ + ili23AnnexB + + + Units + 2012-02-20 + http://www.interlis.ch/models + ili23AnnexB + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/geolink2oereb/lib/interfaces/oerebkrmtrsfr/v2_0/README.md b/src/geolink2oereb/lib/interfaces/oerebkrmtrsfr/v2_0/README.md index 775d648..4eb7b68 100644 --- a/src/geolink2oereb/lib/interfaces/oerebkrmtrsfr/v2_0/README.md +++ b/src/geolink2oereb/lib/interfaces/oerebkrmtrsfr/v2_0/README.md @@ -1,6 +1,32 @@ # Interface to oerebkrmtrsfr 2.0 +The interface was created by the following steps. + +## Creating a XML Schema based on ILI model + +The tool version used was: [ili2c-5.2.8](https://downloads.interlis.ch/ili2c/ili2c-5.2.8.zip) + +Please look for newer versions if you update the workflow. + +The ili model used [OeREBKRMtrsfr_V2_0.ili](OeREBKRMtrsfr_V2_0.ili) + +```shell +java -jar ili2c-5.2.8/ili2c.jar -oXSD OeREBKRMtrsfr_V2_0.ili > OeREBKRMtrsfr_V2_0.xsd +``` + +## Creating python interface + +The XML schema produced by the above step was used as input to +[generateDS 2.41.4](https://pypi.org/project/generateDS/2.41.4/) +to produce the python interface. + Generated by generateDS -additional code by rudert-geoinformatik +```shell +generateDS -o classes.py --create-mandatory-children --member-specs=dict OeREBKRMtrsfr_V2_0.xsd +``` + +Additional code by rudert-geoinformatik. + + diff --git a/src/geolink2oereb/lib/interfaces/oerebkrmtrsfr/v2_0/classes.py b/src/geolink2oereb/lib/interfaces/oerebkrmtrsfr/v2_0/classes.py index 3db0f40..6374699 100644 --- a/src/geolink2oereb/lib/interfaces/oerebkrmtrsfr/v2_0/classes.py +++ b/src/geolink2oereb/lib/interfaces/oerebkrmtrsfr/v2_0/classes.py @@ -11,10 +11,10 @@ # ('--member-specs', 'dict') # # Command line arguments: -# /home/kalle/projects/rudert-geoinformatik/openoereb/mgdm2oereb/mgdm2oereb_pavel/doc/fed/OeREBKRMtrsfr_V2_0.xsd +# OeREBKRMtrsfr_V2_0.xsd # # Command line: -# /home/kalle/.local/bin/generateDS -o "classes.py" --create-mandatory-children --member-specs="dict" /home/kalle/projects/rudert-geoinformatik/openoereb/mgdm2oereb/mgdm2oereb_pavel/doc/fed/OeREBKRMtrsfr_V2_0.xsd +# generateDS -o "classes.py" --create-mandatory-children --member-specs="dict" OeREBKRMtrsfr_V2_0.xsd # # Current working directory (os.getcwd()): # 2_0 diff --git a/src/geolink2oereb/lib/interfaces/oerebkrmtrsfr/v2_0/generators.py b/src/geolink2oereb/lib/interfaces/oerebkrmtrsfr/v2_0/generators.py index d6134fa..8db0fe2 100644 --- a/src/geolink2oereb/lib/interfaces/oerebkrmtrsfr/v2_0/generators.py +++ b/src/geolink2oereb/lib/interfaces/oerebkrmtrsfr/v2_0/generators.py @@ -1,3 +1,8 @@ +""" +This interface offers methods to access python classes matching the ``OeREBKRMtrsfr_V2_0`` INTERLIS model. It +is used mainly for translation and handling between ``pyramid_oereb`` and ``OeREBKRMtrsfr_V2_0``. +""" + import logging from geolink2oereb.lib.interfaces.oerebkrmtrsfr.v2_0.classes import ( OeREBKRM_V2_0_Dokumente_Dokument, @@ -17,6 +22,16 @@ def fix_url(url): + """ + Helper method to hotfix an often occurring problem with urls not being valid in the ili sense. The URLs + out of ÖREBlex are missing the http/https part often. So we try to fix this on the fly. + + Args: + url (str): The URL string which might be fixed. + + Returns: + The maybe fixed URL. + """ if not url.startswith('http'): new_url = 'https://{}'.format(url) logging.info(f"Fixing url from {url} to {new_url}") @@ -25,6 +40,23 @@ def fix_url(url): def multilingual_text_from_dict(multilingual_dict): + """ + Produces a MultilingualText object out of a dict in the form: + + .. code-block:: python + + { + "de": "Test", + "it": "Testo" + } + + Args: + multilingual_dict (dict or None): The definition of the multilingual element or None. + + Returns: + geolink2oereb.lib.interfaces.oerebkrmtrsfr.v2_0.classes.TitelType + """ + if multilingual_dict is None: return multilingual_dict localized_texts = LocalisedTextType() @@ -36,6 +68,22 @@ def multilingual_text_from_dict(multilingual_dict): def multilingual_uri_from_dict(multilingual_dict): + """ + Produces a MultilingualText object out of a dict in the form: + + .. code-block:: python + + { + "de": "Test", + "it": "Testo" + } + + Args: + multilingual_dict (dict or None): The definition of the multilingual element or None. + + Returns: + geolink2oereb.lib.interfaces.oerebkrmtrsfr.v2_0.classes.TextImWebType + """ if multilingual_dict is None: return multilingual_dict localized_texts = LocalisedTextType86() @@ -48,6 +96,7 @@ def multilingual_uri_from_dict(multilingual_dict): def office_record_to_oerebkrmtrsfr(office_record): """ + Translates a ``pyramid_oereb`` office record object to an OeREBKRM_V2_0_Amt_Amt object. Args: office_record (pyramid_oereb.core.records.office.OfficeRecord): The office record to translate. @@ -70,13 +119,18 @@ def office_record_to_oerebkrmtrsfr(office_record): def document_record_to_oerebkrmtrsfr(document_record): """ + Translates a ``pyramid_oereb`` document record object to an OeREBKRM_V2_0_Dokumente_Dokument object. Args: document_record (pyramid_oereb.core.records.documents.DocumentRecord): The record to translate. Returns: - (geolink2oereb.lib.interfaces.oerebkrmtrsfr.v2_0.classes.OeREBKRM_V2_0_Amt_Amt, - geolink2oereb.lib.interfaces.oerebkrmtrsfr.v2_0.classes.OeREBKRM_V2_0_Dokumente_Dokument) + (tuple): tuple containing: + + geolink2oereb.lib.interfaces.oerebkrmtrsfr.v2_0.classes.OeREBKRM_V2_0_Amt_Amt: + The office which belongs to the document (responsible office). + geolink2oereb.lib.interfaces.oerebkrmtrsfr.v2_0.classes.OeREBKRM_V2_0_Dokumente_Dokument: + The document. """ amt = office_record_to_oerebkrmtrsfr(document_record.responsible_office) dokument = OeREBKRM_V2_0_Dokumente_Dokument( diff --git a/src/geolink2oereb/lib/interfaces/pyramid_oereb/__init__.py b/src/geolink2oereb/lib/interfaces/pyramid_oereb/__init__.py index 78b9579..0c0ff96 100644 --- a/src/geolink2oereb/lib/interfaces/pyramid_oereb/__init__.py +++ b/src/geolink2oereb/lib/interfaces/pyramid_oereb/__init__.py @@ -1,3 +1,9 @@ +""" +This module offers the interface to the `pyramid_oereb `_ library. +All usage of ``pyramid_oereb`` elements should be implemented here and proxied through this part +of geolink2oereb. That makes it easier to adapt once changes occur. +""" + import logging from pyramid_oereb.core.records.law_status import LawStatusRecord from pyramid_oereb.core.config import Config @@ -14,11 +20,58 @@ __version__ = "1.0.0" +def merge_attribute(master_attribute, merger_attribute): + """ + A helper method to merge attributes which can be a dict or None. + + Args: + master_attribute (dict or None): The master attribute. + merger_attribute (dict or None): The merger attribute. + + Returns: + dict or None: The merged Attribute + """ + + if master_attribute is None and merger_attribute is None: + # both attributes are None, we can return None here + return None + if master_attribute is None and isinstance(merger_attribute, dict): + # one attribute is None the other is a dict, we return the dict + return merger_attribute + if merger_attribute is None and isinstance(master_attribute, dict): + # one attribute is None the other is a dict, we return the dict + return master_attribute + if isinstance(master_attribute, dict) and isinstance(merger_attribute, dict): + # both attributes are dicts, we return the merged dict + return {**master_attribute, **merger_attribute} + # in all other cases we simply return None + return None + + class OEREBlexSourceCustom(OEREBlexSource): - # comletely skip federal documents because they should be added via official - # sources: https://models.geo.admin.ch/V_D/OeREB/OeREBKRM_V2_0_Gesetze.xml - # TODO: add french identifier of federal docs - filter_federal_documents = ['Bund', 'Cancelleria federale', 'Confederaziun', 'Confederazione'] + """ + This subclass is basically used to manipulate the behaviour of the normal OEREBlexSource as it is used + in ``pyramid_oereb``. + + Main changes are: + - adaption of _get_document_title to be able to manipulate title of documents more easy + - adaption of _get_document_records to add a filter for federal documents because they are provided + in another way and can be omitted. + """ + @property + def filter_federal_documents(self): + """ + A list to filter documents. It can be adopted to manipulate how the filter should work. In the default + implementation it comletely skip federal documents because they should be added via official sources: + https://models.geo.admin.ch/V_D/OeREB/OeREBKRM_V2_0_Gesetze.xml + + The strings in the list are applied to the ``federal_level`` of a ÖEREBlex document. + + Returns: + list of str: The filter strings which are applied to ``federal_level`` of a ÖEREBlex document. + """ + # TODO: add french identifier of federal docs + return ['Bund', 'Cancelleria federale', 'Confederaziun', 'Confederazione'] def _get_document_title(self, document, current_file, language): """ @@ -68,7 +121,10 @@ def _get_document_records(self, document, language): def oerebkrm_v2_0_dokument_typ_2_document_type_records(): """ - Returns: list of pyramid_oereb.core.records.document_types.DocumentTypeRecord + Translates all OeREBKRM_V2_0_DokumentTyp to actual DocumentTypeRecord. + + Returns: + list of pyramid_oereb.core.records.document_types.DocumentTypeRecord """ document_type_records = [] for document_type_enum in list(OeREBKRM_V2_0_DokumentTyp): @@ -80,11 +136,13 @@ def create_document_source( source_config, theme_code, language, oereb_lex_document_source_class=OEREBlexSource ): """ + Interface method to ``pyramid_oereb``, to complete the config of the used ``pyramid_oereb`` source + with missing elements and create the actual data source. Args: - source_config (dict): - theme_code (str): - language (str): + source_config (dict): The config dict which is used to instanciate the ÖEREBlex Source. + theme_code (str): The theme code matching the ``pyramid_oereb`` configuration. + language (str): The language code (de, it, fr, ...). oereb_lex_document_source_class (pyramid_oereb.contrib.data_sources.oereblex.sources.document.OEREBlexSource): # noqa: E501 The class which is used to produce the document records. @@ -98,6 +156,16 @@ def create_document_source( def get_document_type_code_by_extract_value(theme_code, extract_value): + """ + Interface method to ``pyramid_oereb``. Shortcut method to access the translation of ``document_types`` as + defined in ``pyramid_oereb`` configuration yaml. + + Args: + theme_code (str): The theme code matching the ``pyramid_oereb`` configuration. + extract_value (str): The ``document_type`` as it is expected in the extract. + Returns: + None or str: The translated ``document_type`` or None if no match was found. + """ for lookup in Config.get_document_types_lookups(theme_code): if lookup["extract_code"] == extract_value: return lookup["transfer_code"] @@ -105,6 +173,16 @@ def get_document_type_code_by_extract_value(theme_code, extract_value): def get_law_status_code_by_extract_value(theme_code, extract_value): + """ + Interface method to ``pyramid_oereb``. Shortcut method to access the translation of ``law_status_codes`` + as defined in ``pyramid_oereb`` configuration yaml. + + Args: + theme_code (str): The theme code matching the ``pyramid_oereb`` configuration. + extract_value (str): The ``law_status_code`` as it is expected in the extract. + Returns: + None or str: The translated ``law_status_code`` or None if no match was found. + """ for lookup in Config.get_law_status_lookups(theme_code): if lookup["extract_code"] == extract_value: return lookup["transfer_code"] @@ -113,6 +191,8 @@ def get_law_status_code_by_extract_value(theme_code, extract_value): def merge_office(master, merger): """ + Merge multiple ``OfficeRecords`` to one. While processing ÖREBlex there can occur different versions + of the same office in different languages. We try to solve that problem here. Args: master (pyramid_oereb.core.records.office.OfficeRecord): The record all date will be added to. @@ -120,16 +200,15 @@ def merge_office(master, merger): Returns (pyramid_oereb.core.records.office.OfficeRecord): the updated master record. """ - master.name.update(merger.name) - if master.office_at_web is None: - master.office_at_web = merger.office_at_web - else: - master.office_at_web.update(merger.office_at_web) + master.name = merge_attribute(master.name, merger.name) + master.office_at_web = merge_attribute(master.office_at_web, merger.office_at_web) return master def merge_document_type(master, merger): """ + Merge multiple ``DocumentTypeRecord`` to one. While processing ÖREBlex there can occur different versions + of the same document type in different languages. We try to solve that problem here. Args: master (pyramid_oereb.core.records.document_types.DocumentTypeRecord): The record all date will be @@ -138,12 +217,15 @@ def merge_document_type(master, merger): taken from. Returns (pyramid_oereb.core.records.document_types.DocumentTypeRecord): the updated master record. """ - master.title.update(merger.title) + + master.title = merge_attribute(master.title, merger.title) return DocumentTypeRecord(master.code, master.title) def merge_document(master, merger): """ + Merge multiple ``DocumentRecord`` to one. While processing ÖREBlex there can occur different versions + of the same document in different languages. We try to solve that problem here. Args: master (pyramid_oereb.core.records.documents.DocumentRecord): The record all date will be added to. @@ -153,24 +235,17 @@ def merge_document(master, merger): master.document_type = merge_document_type(master.document_type, merger.document_type) master.law_status = LawStatusRecord(master.law_status.code, master.law_status.title) master.responsible_office = merge_office(master.responsible_office, merger.responsible_office) - master.title.update(merger.title) - if master.text_at_web is None: - master.text_at_web = merger.text_at_web - else: - master.text_at_web.update(merger.text_at_web) - if master.abbreviation is None: - master.abbreviation = merger.abbreviation - else: - master.abbreviation.update(merger.abbreviation) - if master.official_number is None: - master.official_number = merger.official_number - else: - master.official_number.update(merger.official_number) + master.title = merge_attribute(master.title, merger.title) + master.text_at_web = merge_attribute(master.text_at_web, merger.text_at_web) + master.abbreviation = merge_attribute(master.abbreviation, merger.abbreviation) + master.official_number = merge_attribute(master.official_number, merger.official_number) return master def make_office_at_web_multilingual(documents, language): """ + ÖEREBlex offers multilingual elements via different URLS. With this method we combine all available + languages into one multilingual element. Args: documents (list of pyramid_oereb.core.records.documents.DocumentRecord): The records to change the @@ -192,6 +267,23 @@ def load( source_class_path="geolink2oereb.lib.interfaces.pyramid_oereb.OEREBlexSourceCustom", c2ctemplate_style=False, ): + """ + Interface method to ``pyramid_oereb``. It utilizes the lib to obtain a set of records from ÖREBlex. + + Args: + geolink_id (int): The geoLink ID (lexlink ID). + theme_code (str): The theme code matching the ``pyramid_oereb`` configuration. + pyramid_oereb_config_path (str): The configuration yaml file path. + pyramid_config_section (str): The section within the yaml file. + source_class_path (str): The point separated path to the class which is used to produce the document + records (Default: ``geolink2oereb.lib.interfaces.pyramid_oereb.OEREBlexSourceCustom``). + c2ctemplate_style (bool): If set to true, c2c.template library will be used + to load config file (Default: False). + + Returns: + list of pyramid_oereb.core.records.documents.DocumentRecord: The collected and corrected + documents, with types and offices. + """ Config._config = None Config.init( pyramid_oereb_config_path, diff --git a/src/geolink2oereb/transform.py b/src/geolink2oereb/transform.py index 0850271..6903870 100644 --- a/src/geolink2oereb/transform.py +++ b/src/geolink2oereb/transform.py @@ -1,3 +1,10 @@ +""" +The basic entry module. It offers methods to be used right away: + +* run +* run_batch +""" + from uuid import uuid4 from geolink2oereb.lib.interfaces.pyramid_oereb import load from geolink2oereb.lib.interfaces.oerebkrmtrsfr.v2_0.generators import ( @@ -13,8 +20,7 @@ def run( source_class_path="geolink2oereb.lib.interfaces.pyramid_oereb.OEREBlexSourceCustom", c2ctemplate_style=False, ): - """ - Lads documents from ÖREBlex and transforms it to OeREBKRMtrsfr objects. + """Loads documents from one ÖREBlex geolink and transforms it to OeREBKRMtrsfr objects. Args: geolink_id (int): The lexlink/geolink of the ÖREBlex document to download. @@ -50,7 +56,7 @@ def run_batch( c2ctemplate_style=False ): """ - Lads documents from ÖREBlex and transforms it to OeREBKRMtrsfr objects. + Loads documents from multiple ÖREBlex geolinks and transforms it to OeREBKRMtrsfr objects. Args: geolink_id (list of int): A list of the lexlinks/geolinks of the ÖREBlex document to download. @@ -80,15 +86,21 @@ def run_batch( def unify_gathered(gathered): - """ + """add docs Args: - gathered ([(geolink2oereb.lib.interfaces.oerebkrmtrsfr.v2_0.classes.OeREBKRM_V2_0_Amt_Amt, - geolink2oereb.lib.interfaces.oerebkrmtrsfr.v2_0.classes.OeREBKRM_V2_0_Dokumente_Dokument)]): - + gathered (list): + consists of (tuple): + geolink2oereb.lib.interfaces.oerebkrmtrsfr.v2_0.classes.OeREBKRM_V2_0_Dokumente_Dokument: + The Dokument + geolink2oereb.lib.interfaces.oerebkrmtrsfr.v2_0.classes.OeREBKRM_V2_0_Amt_Amt: + The Amt Returns: - ([geolink2oereb.lib.interfaces.oerebkrmtrsfr.v2_0.classes.OeREBKRM_V2_0_Amt_Amt], - [geolink2oereb.lib.interfaces.oerebkrmtrsfr.v2_0.classes.OeREBKRM_V2_0_Dokumente_Dokument]) + (tuple): tuple containing: + list of geolink2oereb.lib.interfaces.oerebkrmtrsfr.v2_0.classes.OeREBKRM_V2_0_Dokumente_Dokument: + The list of unified Dokumente. + list of geolink2oereb.lib.interfaces.oerebkrmtrsfr.v2_0.classes.OeREBKRM_V2_0_Amt_Amt: + The list of unified Ämter. """ unique_amt_tids = [] unique_amt = [] @@ -106,15 +118,20 @@ def unify_gathered(gathered): def assign_uuids(unique_dokumente, unique_aemter): """ + Assigns UUIDs to a list of unique Aemter and Dokumente. It needs the lists to be in predefined order + to match the correct objects. Args: - unique_dokumente - ([geolink2oereb.lib.interfaces.oerebkrmtrsfr.v2_0.classes.OeREBKRM_V2_0_Dokumente_Dokument]): - unique_aemter ([geolink2oereb.lib.interfaces.oerebkrmtrsfr.v2_0.classes.OeREBKRM_V2_0_Amt_Amt]): - + unique_dokumente (list of geolink2oereb.lib.interfaces.oerebkrmtrsfr.v2_0.classes.OeREBKRM_V2_0_Dokumente_Dokument): # noqa: E501 + List of unique dokumente where each dokument should receive a UUID. + unique_aemter (list of geolink2oereb.lib.interfaces.oerebkrmtrsfr.v2_0.classes.OeREBKRM_V2_0_Amt_Amt): + List of unique aemter where each amt should receive a UUID. Returns: - ([geolink2oereb.lib.interfaces.oerebkrmtrsfr.v2_0.classes.OeREBKRM_V2_0_Amt_Amt], - [geolink2oereb.lib.interfaces.oerebkrmtrsfr.v2_0.classes.OeREBKRM_V2_0_Dokumente_Dokument]) + (tuple): tuple containing: + list of geolink2oereb.lib.interfaces.oerebkrmtrsfr.v2_0.classes.OeREBKRM_V2_0_Dokumente_Dokument: + The list of unique Dokumente where each Dokument has a valid UUID assigned. + list of geolink2oereb.lib.interfaces.oerebkrmtrsfr.v2_0.classes.OeREBKRM_V2_0_Amt_Amt): + The list of unique Ämter where each Amt has a valid UUID assigned. """ uuid_aemter = [] uuid_dokumente = [] diff --git a/tests/lib/interfaces/oerebkrmtrsfr/v2_0/test_generators.py b/tests/lib/interfaces/oerebkrmtrsfr/v2_0/test_generators.py index b4e24ab..d6a8d5e 100644 --- a/tests/lib/interfaces/oerebkrmtrsfr/v2_0/test_generators.py +++ b/tests/lib/interfaces/oerebkrmtrsfr/v2_0/test_generators.py @@ -1,7 +1,130 @@ -from geolink2oereb.lib.interfaces.oerebkrmtrsfr.v2_0.classes import TitelType -from geolink2oereb.lib.interfaces.oerebkrmtrsfr.v2_0.generators import multilingual_text_from_dict +import datetime +import pytest + +from pyramid_oereb.core.records.document_types import DocumentTypeRecord +from pyramid_oereb.core.records.documents import DocumentRecord +from pyramid_oereb.core.records.law_status import LawStatusRecord +from pyramid_oereb.core.records.office import OfficeRecord + +from geolink2oereb.lib.interfaces.oerebkrmtrsfr.v2_0.classes import TitelType, TextImWebType, \ + OeREBKRM_V2_0_Amt_Amt, OeREBKRM_V2_0_Dokumente_Dokument, ZustaendigeStelleType +from geolink2oereb.lib.interfaces.oerebkrmtrsfr.v2_0.generators import ( + multilingual_text_from_dict, + fix_url, + multilingual_uri_from_dict, + office_record_to_oerebkrmtrsfr, + document_record_to_oerebkrmtrsfr +) + + +@pytest.fixture +def law_status_record(): + yield LawStatusRecord( + 'inKraft', { + "de": "Rechtskräftig", + "fr": "En vigueur", + "it": "In vigore", + "rm": "En vigur", + "en": "In force" + } + ) + + +@pytest.fixture +def document_type_record(): + yield DocumentTypeRecord( + 'GesetzlicheGrundlage', + {'de': 'Gesetzliche Grundlage'} + ) + + +@pytest.fixture +def office_record(): + yield OfficeRecord( + {'de': 'Test'}, + office_at_web={'de': 'www.example.com'}, + uid='ch99', + postal_code=4123, + line1='Adresszeile 1', + line2='Adresszeile 2', + street='Straße', + number='999', + city='Basel' + ) + + +@pytest.fixture +def document_record(office_record, document_type_record, law_status_record): + yield DocumentRecord( + document_type_record, + 1, + law_status_record, + {'de': 'Title'}, + office_record, + datetime.date(1985, 8, 29), + text_at_web={'de': 'http://mein.dokument.ch'}, + abbreviation={'de': 'abkrzg'}, + official_number={'de': '123 BGB'}, + only_in_municipality=2345, + published_until=datetime.date(2999, 8, 29) + ) + + +@pytest.mark.parametrize( + 'input,expected', [ + ('www.test.ch', 'https://www.test.ch'), + ('http://test.ch', 'http://test.ch'), + ('test.ch', 'https://test.ch'), + ('https://test.ch', 'https://test.ch') + ]) +def test_fix_url(input, expected): + result = fix_url(input) + assert result == expected def test_multilingual_text_from_dict(): result = multilingual_text_from_dict({'de': 'Hallo', 'en': 'Hello'}) assert isinstance(result, TitelType) + + +def test_test_multilingual_text_from_dict_none(): + assert multilingual_text_from_dict(None) is None + + +def test_multilingual_uri_from_dict(): + result = multilingual_uri_from_dict({'de': 'Hallo', 'en': 'Hello'}) + assert isinstance(result, TextImWebType) + + +def test_multilingual_uri_from_dict_none(): + assert multilingual_uri_from_dict(None) is None + + +def test_office_record_to_oerebkrmtrsfr(office_record): + result = office_record_to_oerebkrmtrsfr(office_record) + assert isinstance(result, OeREBKRM_V2_0_Amt_Amt) + assert isinstance(result.Name, TitelType) + assert isinstance(result.AmtImWeb, TextImWebType) + assert result.UID == office_record.uid + assert result.Zeile1 == office_record.line1 + assert result.Zeile2 == office_record.line2 + assert result.Strasse == office_record.street + assert result.Hausnr == office_record.number + assert result.PLZ == office_record.postal_code + assert result.Ort == office_record.city + + +def test_document_record_to_oerebkrmtrsfr(document_record): + dokument, amt = document_record_to_oerebkrmtrsfr(document_record) + assert isinstance(dokument, OeREBKRM_V2_0_Dokumente_Dokument) + assert dokument.Typ == document_record.document_type.code + assert isinstance(dokument.Titel, TitelType) + assert isinstance(dokument.Abkuerzung, TitelType) + assert isinstance(dokument.OffizielleNr, TitelType) + assert dokument.NurInGemeinde == document_record.only_in_municipality + assert isinstance(dokument.TextImWeb, TextImWebType) + assert dokument.AuszugIndex == document_record.index + assert dokument.Rechtsstatus == document_record.law_status.code + assert dokument.publiziertAb == document_record.published_from + assert dokument.publiziertBis == document_record.published_until + assert isinstance(dokument.ZustaendigeStelle, ZustaendigeStelleType) diff --git a/tests/lib/interfaces/test_pyramid_oereb.py b/tests/lib/interfaces/test_pyramid_oereb.py new file mode 100644 index 0000000..f0be624 --- /dev/null +++ b/tests/lib/interfaces/test_pyramid_oereb.py @@ -0,0 +1,557 @@ +import datetime + +import pytest +from geolink_formatter.entity import Document, File +from pyramid_oereb.contrib.data_sources.oereblex.sources.document import OEREBlexSource +from pyramid_oereb.core.records.document_types import DocumentTypeRecord +from pyramid_oereb.core.records.documents import DocumentRecord +from pyramid_oereb.core.records.law_status import LawStatusRecord +from pyramid_oereb.core.records.office import OfficeRecord + +from geolink2oereb.lib.interfaces.pyramid_oereb import oerebkrm_v2_0_dokument_typ_2_document_type_records, \ + create_document_source, OEREBlexSourceCustom, get_document_type_code_by_extract_value, \ + get_law_status_code_by_extract_value, merge_office, merge_document_type, merge_document, \ + merge_attribute, make_office_at_web_multilingual + + +@pytest.fixture +def pyramid_oereb_config(): + yield { + "language": ["de", "it", "rm"], + "default_language": "de", + "flavour": ["REDUCED"], + "srid": 2056, + "plrs": [{ + "code": "ch.Planungszonen", + "geometry_type": "GEOMETRYCOLLECTION", + "thresholds": { + "length": { + "limit": 1.0, + "unit": "m", + "precision": 2 + }, + "area": { + "limit": 1.0, + "unit": "m²", + "precision": 2 + }, + "percentage": { + "precision": 1 + } + }, + "language": "de", + "federal": False, + "view_service": { + "layer_index": 1, + "layer_opacity": 0.75 + }, + "source": { + "class": "pyramid_oereb.contrib.data_sources.interlis_2_3.sources.plr.DatabaseSource", + "params": { + "db_connection": "main_db_connection", + "model_factory": "pyramid_oereb.contrib.data_sources.interlis_2_3.models.theme.model_factory_integer_pk", # noqa: E501 + "schema_name": "planungszonen" + } + }, + "hooks": { + "get_symbol": "pyramid_oereb.contrib.data_sources.interlis_2_3.hook_methods.get_symbol", + "get_symbol_ref": "pyramid_oereb.core.hook_methods.get_symbol_ref" + }, + "law_status_lookup": [{ + "data_code": "inKraft", + "transfer_code": "inKraft", + "extract_code": "inForce" + }, { + "data_code": "AenderungMitVorwirkung", + "transfer_code": "AenderungMitVorwirkung", + "extract_code": "changeWithPreEffect" + }, { + "data_code": "AenderungOhneVorwirkung", + "transfer_code": "AenderungOhneVorwirkung", + "extract_code": "changeWithoutPreEffect" + }], + "document_types_lookup": [{ + "data_code": "decree", + "transfer_code": "Rechtsvorschrift", + "extract_code": "LegalProvision" + }, { + "data_code": "edict", + "transfer_code": "GesetzlicheGrundlage", + "extract_code": "Law" + }, { + "data_code": "notice", + "transfer_code": "Hinweis", + "extract_code": "Hint" + }] + }], + "static_error_message": { + "de": "Ein oder mehrere ÖREB-Themen stehen momentan nicht zur Verfügung. Daher kann kein Auszug erstellt werden. Versuchen Sie es zu einem späteren Zeitpunkt erneut. Wir entschuldigen uns für die Unannehmlichkeiten.", # noqa: E501 + "rm": "Ein oder mehrere ÖREB-Themen stehen momentan nicht zur Verfügung. Daher kann kein Auszug erstellt werden. Versuchen Sie es zu einem späteren Zeitpunkt erneut. Wir entschuldigen uns für die Unannehmlichkeiten.", # noqa: E501 + "it": "Uno o più temi relativi alle RDPP non sono attualmente disponibili. Non è pertanto possibile allestire alcun estratto. Vi preghiamo di riprovare più tardi. Ci scusiamo per l’inconveniente." # noqa: E501 + } + } + + +@pytest.fixture +def document_type_records(): + yield [ + DocumentTypeRecord( + 'GesetzlicheGrundlage', + {'de': 'Gesetzliche Grundlage'} + ), + DocumentTypeRecord( + 'Rechtsvorschrift', + {'de': 'Rechtsvorschrift'} + ), + DocumentTypeRecord( + 'Hinweis', + {'de': 'Hinweis'} + ) + ] + + +@pytest.fixture +def law_status_record(): + yield LawStatusRecord( + 'inKraft', { + "de": "Rechtskräftig", + "fr": "En vigueur", + "it": "In vigore", + "rm": "En vigur", + "en": "In force" + } + ) + + +@pytest.fixture +def source_config(): + yield { + "host": "https://oereblex.gr.ch", + "version": "1.2.2", + "pass_version": True, + "validation": True, + "language": "de", + "canton": "GR", + "mapping": { + "official_number": "number", + "abbreviation": "abbreviation" + }, + "related_decree_as_main": False, + "related_notice_as_main": False, + "proxy": None, + "url_param_config": [{ + "code": "ch.StatischeWaldgrenzen", + "url_param": "oereb_id=24" + }, { + "code": "ch.Waldabstandslinien", + "url_param": "oereb_id=25" + }, { + "code": "ch.Laermempfindlichkeitsstufen", + "url_param": "oereb_id=26" + }, { + "code": "ch.GR.NutzungsplanungZpGgp", + "url_param": "oereb_id=11" + }, { + "code": "ch.GR.NutzungsplanungGep", + "url_param": "oereb_id=11" + }, { + "code": "ch.GR.NutzungsplanungFp", + "url_param": "oereb_id=11" + }] + } + + +@pytest.fixture +def geolinkformatter_document_decree(): + file = File( + "main", + "/api/attachments/17906", + "3619_B_Prüfung_red_Bauzonen_WMZ_Anpassung_de.pdf", + "3619_B_Prüfung_red_Bauzonen_WMZ_Anpassung_de.pdf" + ) + yield Document( + [file], + "5", + "related", + "decree", + "Gemeinde", + "Gemeindeverwaltung", + "www.ilanz-glion.ch", + "Prüfung einer Reduktion von Bauzonen WMZ (Anpassung)", + "00.075.963", + None, + None, + "Planungszonen", + None, + None, + datetime.date(2022, 12, 23), + datetime.date(2024, 2, 12), + None, + "Ilanz/Glion", + None, + None, + None, + None + ) + + +@pytest.fixture +def geolinkformatter_document_edict(): + file = File( + "main", + "https://www.lexfind.ch/tolv/220739/de", + "700.de.pdf", + "700.de.pdf" + ) + yield Document( + [file], + "5", + "related", + "edict", + "Bund", + "Bundeskanzlei", + "https://www.bk.admin.ch/", + "Bundesgesetz über die Raumplanung", + "SR 700", + "Raumplanungsgesetz, RPG", + None, + None, + None, + None, + datetime.date(2019, 1, 1), + None, + None, + None, + 10, + None, + None, + None + ) + + +@pytest.mark.parametrize('master,merger,output', [ + (None, None, None), + ({'de': 'test'}, None, {'de': 'test'}), + (None, {'de': 'test'}, {'de': 'test'}), + ({'en': 'test'}, {'de': 'test'}, {'de': 'test', 'en': 'test'}), + ('test', {'de': 'test'}, None) +]) +def test_merge_attribute(master, merger, output): + assert merge_attribute(master, merger) == output + + +def test_oerebkrm_v2_0_dokument_typ_2_document_type_records(): + result = oerebkrm_v2_0_dokument_typ_2_document_type_records() + assert len(result) == 3 + for document_type in result: + assert isinstance(document_type, DocumentTypeRecord) + + +def test_create_document_source(source_config): + result = create_document_source( + source_config, + 'ch.Planungszonen', + 'de', + oereb_lex_document_source_class=OEREBlexSource + ) + assert isinstance(result, OEREBlexSource) + + +def test_oereblex_source_custom(source_config): + result = create_document_source( + source_config, + 'ch.Planungszonen', + 'de', + oereb_lex_document_source_class=OEREBlexSourceCustom + ) + assert isinstance(result, OEREBlexSourceCustom) + + +def test_oereblex_source_custom_filter_federal_documents(source_config): + result = create_document_source( + source_config, + 'ch.Planungszonen', + 'de', + oereb_lex_document_source_class=OEREBlexSourceCustom + ) + assert isinstance(result.filter_federal_documents, list) + for item in result.filter_federal_documents: + assert isinstance(item, str) + + +def test_oereblex_source_custom_get_document_title_edict(source_config, geolinkformatter_document_edict): + result = create_document_source( + source_config, + 'ch.Planungszonen', + 'de', + oereb_lex_document_source_class=OEREBlexSourceCustom + ) + title = result._get_document_title( + geolinkformatter_document_edict, + geolinkformatter_document_edict.files[0], + 'de' + ) + assert isinstance(title, dict) + assert title['de'] == geolinkformatter_document_edict.title + + +def test_oereblex_source_custom_get_document_title_decree(source_config, geolinkformatter_document_decree): + result = create_document_source( + source_config, + 'ch.Planungszonen', + 'de', + oereb_lex_document_source_class=OEREBlexSourceCustom + ) + title = result._get_document_title( + geolinkformatter_document_decree, + geolinkformatter_document_decree.files[0], + 'de' + ) + assert isinstance(title, dict) + assert title['de'] == (f'{geolinkformatter_document_decree.title} ' + f'({geolinkformatter_document_decree.files[0]._title})') + + +def test_oereblex_source_custom_get_document_records_filtered(source_config, geolinkformatter_document_edict): + result = create_document_source( + source_config, + 'ch.Planungszonen', + 'de', + oereb_lex_document_source_class=OEREBlexSourceCustom + ) + document_records = result._get_document_records(geolinkformatter_document_edict, 'de') + assert isinstance(document_records, list) + assert len(document_records) == 0 + + +def test_oereblex_source_custom_get_document_records_not_filtered( + source_config, + geolinkformatter_document_decree, + pyramid_oereb_config, + document_type_records, + law_status_record +): + from pyramid_oereb.core.config import Config + Config._config = pyramid_oereb_config + Config.document_types = document_type_records + Config.law_status = [law_status_record] + result = create_document_source( + source_config, + 'ch.Planungszonen', + 'de', + oereb_lex_document_source_class=OEREBlexSourceCustom + ) + document_records = result._get_document_records(geolinkformatter_document_decree, 'de') + assert isinstance(document_records, list) + assert len(document_records) == 1 + assert isinstance(document_records[0], DocumentRecord) + + +@pytest.mark.parametrize('input,output', [ + ('LegalProvision', 'Rechtsvorschrift'), + ('Law', 'GesetzlicheGrundlage'), + ('Hint', 'Hinweis'), + ('NotConfigured', None) +]) +def test_get_document_type_code_by_extract_value(input, output, pyramid_oereb_config): + from pyramid_oereb.core.config import Config + Config._config = pyramid_oereb_config + result = get_document_type_code_by_extract_value( + 'ch.Planungszonen', + input + ) + assert result == output + + +@pytest.mark.parametrize('input,output', [ + ('inForce', 'inKraft'), + ('changeWithPreEffect', 'AenderungMitVorwirkung'), + ('changeWithoutPreEffect', 'AenderungOhneVorwirkung'), + ('NotConfigured', None) +]) +def test_get_law_status_code_by_extract_value(input, output, pyramid_oereb_config): + from pyramid_oereb.core.config import Config + Config._config = pyramid_oereb_config + result = get_law_status_code_by_extract_value( + 'ch.Planungszonen', + input + ) + assert result == output + + +def test_merge_office_updated_name(): + office_master = OfficeRecord({'de': 'Das ist ein Testname'}) + office_merger = OfficeRecord({'en': 'This is a test name'}) + result = merge_office(office_master, office_merger) + assert result.name == {'de': 'Das ist ein Testname', 'en': 'This is a test name'} + + +def test_merge_office_updated_update_url_merger_none(): + office_master = OfficeRecord( + {'de': 'Das ist ein Testname'}, + office_at_web={'de': 'https://test.de'} + ) + office_merger = OfficeRecord( + {'en': 'This is a test name'} + ) + result = merge_office(office_master, office_merger) + assert result.office_at_web == office_master.office_at_web + + +def test_merge_office_updated_update_url_master_none(): + office_master = OfficeRecord( + {'de': 'Das ist ein Testname'} + ) + office_merger = OfficeRecord( + {'en': 'This is a test name'}, + office_at_web={'en': 'https://test.com'} + ) + result = merge_office(office_master, office_merger) + assert result.office_at_web == office_merger.office_at_web + + +def test_merge_office_updated_update_url(): + office_master = OfficeRecord( + {'de': 'Das ist ein Testname'}, + office_at_web={'de': 'https://test.de'} + ) + office_merger = OfficeRecord( + {'en': 'This is a test name'}, + office_at_web={'en': 'https://test.com'} + ) + result = merge_office(office_master, office_merger) + assert result.office_at_web == {'de': 'https://test.de', 'en': 'https://test.com'} + + +def test_merge_document_type(): + document_type_master = DocumentTypeRecord( + 'GesetzlicheGrundlage', + {'de': 'Gesetzliche Grundlage'} + ) + document_type_merger = DocumentTypeRecord( + 'GesetzlicheGrundlage', + {'en': 'Legal Base'} + ) + result = merge_document_type(document_type_master, document_type_merger) + assert result.title == {'de': 'Gesetzliche Grundlage', 'en': 'Legal Base'} + + +def test_merge_document(): + office_master = OfficeRecord( + {'de': 'Das ist ein Testname'}, + office_at_web={'de': 'https://test.de'} + ) + office_merger = OfficeRecord( + {'en': 'This is a test name'}, + office_at_web={'en': 'https://test.com'} + ) + document_type_master = DocumentTypeRecord( + 'GesetzlicheGrundlage', + {'de': 'Gesetzliche Grundlage'} + ) + document_type_merger = DocumentTypeRecord( + 'GesetzlicheGrundlage', + {'en': 'Legal Base'} + ) + law_status_master = LawStatusRecord( + 'inKraft', + { + "de": "Rechtskräftig" + } + ) + law_status_merger = LawStatusRecord( + 'inKraft', + { + "en": "In force" + } + ) + document_master = DocumentRecord( + document_type_master, + 1, + law_status_master, + {'de': 'Der Titel'}, + office_master, + datetime.date(1986, 8, 29), + text_at_web={'de': 'https://dokument.de'}, + abbreviation={'de': 'abkrzg'}, + official_number={'de': 'RPG 1234'} + ) + document_merger = DocumentRecord( + document_type_merger, + 1, + law_status_merger, + {'en': 'The Title'}, + office_merger, + datetime.date(1986, 8, 29), + text_at_web={'en': 'https://document.com'}, + abbreviation={'en': 'abbrv'}, + official_number={'en': 'legal 1234'} + ) + result = merge_document(document_master, document_merger) + assert isinstance(result, DocumentRecord) + assert result.law_status.code == document_master.law_status.code + assert result.law_status.title == document_master.law_status.title + assert result.title == {'de': 'Der Titel', 'en': 'The Title'} + assert result.text_at_web == {'de': 'https://dokument.de', 'en': 'https://document.com'} + assert result.abbreviation == {'de': 'abkrzg', 'en': 'abbrv'} + assert result.official_number == {'de': 'RPG 1234', 'en': 'legal 1234'} + + +def test_make_office_at_web_multilingual_pass(): + office = OfficeRecord( + {'de': 'Das ist ein Testname'}, + office_at_web={'de': 'https://test.de'} + ) + document_type = DocumentTypeRecord( + 'GesetzlicheGrundlage', + {'de': 'Gesetzliche Grundlage'} + ) + law_status = LawStatusRecord( + 'inKraft', + { + "de": "Rechtskräftig" + } + ) + document = DocumentRecord( + document_type, + 1, + law_status, + {'de': 'Der Titel'}, + office, + datetime.date(1986, 8, 29), + text_at_web={'de': 'https://dokument.de'}, + abbreviation={'de': 'abkrzg'}, + official_number={'de': 'RPG 1234'} + ) + result = make_office_at_web_multilingual([document], 'de') + assert result[0].responsible_office.office_at_web == office.office_at_web + + +def test_make_office_at_web_multilingual_not_pass(): + office = OfficeRecord( + {'de': 'Das ist ein Testname'}, + office_at_web='https://test.de' + ) + document_type = DocumentTypeRecord( + 'GesetzlicheGrundlage', + {'de': 'Gesetzliche Grundlage'} + ) + law_status = LawStatusRecord( + 'inKraft', + { + "de": "Rechtskräftig" + } + ) + document = DocumentRecord( + document_type, + 1, + law_status, + {'de': 'Der Titel'}, + office, + datetime.date(1986, 8, 29), + text_at_web={'de': 'https://dokument.de'}, + abbreviation={'de': 'abkrzg'}, + official_number={'de': 'RPG 1234'} + ) + result = make_office_at_web_multilingual([document], 'de') + assert result[0].responsible_office.office_at_web == {'de': 'https://test.de'} diff --git a/tests/lib/test_transform.py b/tests/lib/test_transform.py new file mode 100644 index 0000000..c2d5e18 --- /dev/null +++ b/tests/lib/test_transform.py @@ -0,0 +1,180 @@ +import datetime +from uuid import UUID + +import pytest +from unittest.mock import patch + +from pyramid_oereb.core.records.document_types import DocumentTypeRecord +from pyramid_oereb.core.records.documents import DocumentRecord +from pyramid_oereb.core.records.law_status import LawStatusRecord +from pyramid_oereb.core.records.office import OfficeRecord + + +@pytest.fixture +def law_status_record(): + yield LawStatusRecord( + 'inKraft', { + "de": "Rechtskräftig", + "fr": "En vigueur", + "it": "In vigore", + "rm": "En vigur", + "en": "In force" + } + ) + + +@pytest.fixture +def document_type_record(): + yield DocumentTypeRecord( + 'GesetzlicheGrundlage', + {'de': 'Gesetzliche Grundlage'} + ) + + +@pytest.fixture +def office_record(): + yield OfficeRecord( + {'de': 'Test'}, + office_at_web={'de': 'www.example.com'}, + uid='ch99', + postal_code=4123, + line1='Adresszeile 1', + line2='Adresszeile 2', + street='Straße', + number='999', + city='Basel' + ) + + +@pytest.fixture +def document_record(office_record, document_type_record, law_status_record): + yield DocumentRecord( + document_type_record, + 1, + law_status_record, + {'de': 'Title'}, + office_record, + datetime.date(1985, 8, 29), + text_at_web={'de': 'http://mein.dokument.ch'}, + abbreviation={'de': 'abkrzg'}, + official_number={'de': '123 BGB'}, + only_in_municipality=2345, + published_until=datetime.date(2999, 8, 29) + ) + + +def test_run(document_record): + with patch('geolink2oereb.lib.interfaces.pyramid_oereb.load', return_value=[document_record]): + from geolink2oereb.transform import run + from geolink2oereb.lib.interfaces.oerebkrmtrsfr.v2_0.classes import OeREBKRM_V2_0_Dokumente_Dokument + result = run( + 4304, + 'ch.Planungszonen', + '/a/b/c', + 'pyramid_oereb' + ) + assert isinstance(result, list) + assert len(result) == 1 + assert isinstance(result[0], tuple) + assert isinstance(list(result[0])[0], OeREBKRM_V2_0_Dokumente_Dokument) + + +@pytest.fixture +def gathered_documents(): + office_master = OfficeRecord( + {'de': 'Das ist ein Testname'}, + office_at_web={'de': 'https://test.de'} + ) + document_type_master = DocumentTypeRecord( + 'GesetzlicheGrundlage', + {'de': 'Gesetzliche Grundlage'} + ) + document_type_merger = DocumentTypeRecord( + 'GesetzlicheGrundlage', + {'en': 'Legal Base'} + ) + law_status_master = LawStatusRecord( + 'inKraft', + { + "de": "Rechtskräftig" + } + ) + law_status_merger = LawStatusRecord( + 'inKraft', + { + "en": "In force" + } + ) + document_master = DocumentRecord( + document_type_master, + 1, + law_status_master, + {'de': 'Der Titel'}, + office_master, + datetime.date(1986, 8, 29), + text_at_web={'de': 'https://dokument.de'}, + abbreviation={'de': 'abkrzg'}, + official_number={'de': 'RPG 1234'} + ) + document_merger = DocumentRecord( + document_type_merger, + 1, + law_status_merger, + {'en': 'The Title'}, + office_master, + datetime.date(1986, 8, 29), + text_at_web={'en': 'https://document.com'}, + abbreviation={'en': 'abbrv'}, + official_number={'en': 'legal 1234'} + ) + yield [document_merger, document_master] + + +def test_run_batch(gathered_documents): + with patch('geolink2oereb.lib.interfaces.pyramid_oereb.load', return_value=gathered_documents): + from geolink2oereb.transform import run_batch + from geolink2oereb.lib.interfaces.oerebkrmtrsfr.v2_0.classes import ( + OeREBKRM_V2_0_Dokumente_Dokument, OeREBKRM_V2_0_Amt_Amt) + result = run_batch( + [4304, 4305], + 'ch.Planungszonen', + '/a/b/c', + 'pyramid_oereb' + ) + assert isinstance(result, list) + assert len(result) == 2 + assert len(list(result[0])) == 2 + assert isinstance(list(result[0])[0], OeREBKRM_V2_0_Dokumente_Dokument) + assert isinstance(list(result[0])[1], OeREBKRM_V2_0_Amt_Amt) + assert isinstance(list(result[1])[0], OeREBKRM_V2_0_Dokumente_Dokument) + assert isinstance(list(result[1])[1], OeREBKRM_V2_0_Amt_Amt) + + +@pytest.fixture +def gathered_oerebkrm(gathered_documents): + from geolink2oereb.lib.interfaces.oerebkrmtrsfr.v2_0.generators import document_record_to_oerebkrmtrsfr + yield [ + document_record_to_oerebkrmtrsfr(gathered_documents[0]), + document_record_to_oerebkrmtrsfr(gathered_documents[1]) + ] + + +def test_unify_gathered(gathered_oerebkrm): + from geolink2oereb.transform import unify_gathered + result = unify_gathered(gathered_oerebkrm) + assert isinstance(result, tuple) + assert len(list(result)[0]) == 2 + assert len(list(result)[1]) == 1 + + +def test_assign_uuids(gathered_oerebkrm): + from geolink2oereb.transform import unify_gathered, assign_uuids + result = assign_uuids(*unify_gathered(gathered_oerebkrm)) + assert isinstance(result, tuple) + assert len(list(result)[0]) == 2 + assert len(list(result)[1]) == 1 + assert UUID(list(result)[1][0].TID, version=4) + assert UUID(list(result)[0][0].TID, version=4) + assert UUID(list(result)[0][1].TID, version=4) + assert list(result)[0][0].ZustaendigeStelle.REF == list(result)[1][0].TID + assert list(result)[0][1].ZustaendigeStelle.REF == list(result)[1][0].TID